diff --git a/.gitignore b/.gitignore index 3d9f6b4..62a9bb8 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ doc/source/api/** # Cythonized extension .c files planarity/full/*.c -planarity/classic/*.c +planarity/express/*.c diff --git a/MANIFEST.in b/MANIFEST.in index 441a926..d56c15b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,16 +9,12 @@ include planarity/c/README recursive-include planarity/c/ *.h -include planarity/classic/cplanarity.pxd -include planarity/classic/planarity.c -include planarity/classic/planarity.pyx +include planarity/express/cplanarity.pxd +include planarity/express/planarity.pyx include planarity/full/cgraphLib.pxd include planarity/full/graphLib.pxd include planarity/full/graph.pxd -include planarity/full/graphLib.c -include planarity/full/graph.c -include planarity/full/g6IterationUtils.c include planarity/full/graphLib.pyx include planarity/full/graph.pyx include planarity/full/g6IterationUtils.pyx diff --git a/README.md b/README.md index 9658b68..8c037be 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ In [11]: print(planarity.ascii(edgelist)) ```python In [12]: planarity.draw(edgelist, outfileName='K5-minus-edge.png', vertex_bordercolor='black') ``` -![K_5-minus-edge](https://raw.githubusercontent.com/graph-algorithms/planarity/master/examples/classic/output/K5-minus-edge.png +![K_5-minus-edge](https://raw.githubusercontent.com/graph-algorithms/planarity/master/examples/express/output/K5-minus-edge.png "Matplotlib render of $K_5$-minus-edge") Note that edge `(a, b)` would correspond to an edge between vertex indexes diff --git a/examples/classic/output/K5-minus-edge.png b/examples/classic/output/K5-minus-edge.png deleted file mode 100644 index 84459e2..0000000 Binary files a/examples/classic/output/K5-minus-edge.png and /dev/null differ diff --git a/examples/classic/ascii.py b/examples/express/ascii.py similarity index 100% rename from examples/classic/ascii.py rename to examples/express/ascii.py diff --git a/examples/classic/draw.py b/examples/express/draw.py similarity index 100% rename from examples/classic/draw.py rename to examples/express/draw.py diff --git a/examples/classic/draw_planar_colors.py b/examples/express/draw_planar_colors.py similarity index 100% rename from examples/classic/draw_planar_colors.py rename to examples/express/draw_planar_colors.py diff --git a/examples/classic/graph_formats.py b/examples/express/graph_formats.py similarity index 100% rename from examples/classic/graph_formats.py rename to examples/express/graph_formats.py diff --git a/examples/classic/kuratowski.py b/examples/express/kuratowski.py similarity index 100% rename from examples/classic/kuratowski.py rename to examples/express/kuratowski.py diff --git a/examples/classic/networkx_draw.py b/examples/express/networkx_draw.py similarity index 100% rename from examples/classic/networkx_draw.py rename to examples/express/networkx_draw.py diff --git a/examples/classic/networkx_interface.py b/examples/express/networkx_interface.py similarity index 100% rename from examples/classic/networkx_interface.py rename to examples/express/networkx_interface.py diff --git a/examples/express/output/K5-minus-edge.png b/examples/express/output/K5-minus-edge.png new file mode 100644 index 0000000..f4d68e8 Binary files /dev/null and b/examples/express/output/K5-minus-edge.png differ diff --git a/examples/classic/pgraph_class.py b/examples/express/pgraph_class.py similarity index 100% rename from examples/classic/pgraph_class.py rename to examples/express/pgraph_class.py diff --git a/examples/classic/write_tests.py b/examples/express/write_tests.py similarity index 100% rename from examples/classic/write_tests.py rename to examples/express/write_tests.py diff --git a/planarity/__init__.py b/planarity/__init__.py index 64db20a..11c35d2 100644 --- a/planarity/__init__.py +++ b/planarity/__init__.py @@ -1,6 +1,6 @@ -from .classic.planarity import PGraph -from .classic.planarity_functions import * -from .classic.planarity_networkx import * +from .express.planarity import PGraph +from .express.planarity_functions import * +from .express.planarity_networkx import * # Surfaced from planarityc/graphLib/lowLevelUtils/apiutils.h from .full.graphLib import ( @@ -108,4 +108,4 @@ # NOTE: In the future, we could automatically generate the version number by # configuring setuptools-scm, but presently this seems simpler. -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/planarity/classic/__init__.pxd b/planarity/express/__init__.pxd similarity index 100% rename from planarity/classic/__init__.pxd rename to planarity/express/__init__.pxd diff --git a/planarity/classic/__init__.py b/planarity/express/__init__.py similarity index 100% rename from planarity/classic/__init__.py rename to planarity/express/__init__.py diff --git a/planarity/classic/cplanarity.pxd b/planarity/express/cplanarity.pxd similarity index 100% rename from planarity/classic/cplanarity.pxd rename to planarity/express/cplanarity.pxd diff --git a/planarity/classic/planarity.pyx b/planarity/express/planarity.pyx similarity index 95% rename from planarity/classic/planarity.pyx rename to planarity/express/planarity.pyx index e75e6e6..a4fc9cc 100644 --- a/planarity/classic/planarity.pyx +++ b/planarity/express/planarity.pyx @@ -7,7 +7,7 @@ from libc.stdlib cimport free import typing import warnings -from planarity.classic cimport cplanarity +from planarity.express cimport cplanarity cdef class PGraph: @@ -15,7 +15,7 @@ cdef class PGraph: Attributes: theGraph: The C-layer graph data structure instance wrapped by the - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. nodemap (dict[typing.Any, int]): the mapping of original labels to the internal vertex indexes. reverse_nodemap (dict[int, typing.Any]): The mapping of internal vertex @@ -23,9 +23,10 @@ cdef class PGraph: _embedding_workflow_status (int): Indicates the status of the embedding workflow; the value is not meaningful until after an embedding workflow method such as one of the following has been called: - * :py:meth:`~planarity.classic.planarity.PGraph.is_planar` - * :py:meth:`~planarity.classic.planarity.PGraph.embed_planar` - * :py:meth:`~planarity.classic.planarity.PGraph.embed_drawplanar` + + - :py:meth:`~planarity.express.planarity.PGraph.is_planar` + - :py:meth:`~planarity.express.planarity.PGraph.embed_planar` + - :py:meth:`~planarity.express.planarity.PGraph.embed_drawplanar` Note that these are called by several of the other member methods. @@ -39,7 +40,7 @@ cdef class PGraph: cdef int _embedding_workflow_status def __init__(self, graph): - """Initialize :py:class:`~planarity.classic.planarity.PGraph` from an input graph. + """Initialize :py:class:`~planarity.express.planarity.PGraph` from an input graph. Args: graph (networkx.Graph | dict[typing.Any, collections.abc.Iterable[typing.Any]] | list[list[typing.Any] | tuple[typing.Any, typing.Any]]): @@ -48,9 +49,9 @@ cdef class PGraph: Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if an error was encountered by C-layer methods such as ``gp_New()``, ``gp_EnsureVertexCapacity()``, or ``gp_DynamicAddEdge()``. @@ -98,7 +99,7 @@ cdef class PGraph: n = len(nodes) # NOTE: This presumes 1-based arrays; however, this can only be changed - # if you add -DUSE_0BASEDARRAYS to the extra_compile_args of the classic + # if you add -DUSE_0BASEDARRAYS to the extra_compile_args of the express # planarity extension in setup.py. self.nodemap = dict(zip(nodes, range(1, n+1))) self.reverse_nodemap = dict(zip(range(1, n+1), nodes)) @@ -420,10 +421,11 @@ cdef class PGraph: ) ) - # NOTE: The DrawPlanar context data gives geometric positioning; - # a value of -1 indicates an error state, and therefore the - # drawplanar_edge_info should not be included (i.e., the - # final tuple member will be an empty dict) + # NOTE: The DrawPlanar context data gives geometric + # positioning; a value of -1 indicates an error state, + # and therefore the drawplanar_edge_info should not be + # included (i.e., the final tuple member will be an + # empty dict) if ( edge_position > -1 and edge_start > -1 and @@ -490,12 +492,13 @@ cdef class PGraph: Matplotlib and then saved to ``outfileName``, if given. Args: - labels (bool): If ``True``, vertex labels are rendered in the drawing. - Otherwise, vertices are rendered unlabelled in the drawing. If a - label's rendered width would exceed its vertex's own bounding - rectangle, the label is truncated with a trailing ``...`` so it - stays within that rectangle (see Issue #91). The vertex's - geometry itself is never resized to accommodate a label. + labels (bool): If ``True``, vertex labels are rendered in the + drawing. Otherwise, vertices are rendered unlabelled in the + drawing. If a label's rendered width would exceed its vertex's + own bounding rectangle, the label is truncated with a trailing + ``...`` so it stays within that rectangle (see Issue #91). The + vertex's geometry itself is never resized to accommodate a + label. outfileName (:obj:`str`): File to which to output a Matplotlib rendering of the planar graph. If not given, then the caller can call :external+matplotlib:py:func:`matplotlib.pyplot.savefig`. @@ -621,11 +624,13 @@ cdef class PGraph: ax = plt.gca() ax.add_collection(p) - # Sets the aspect ratio of the axes to 'equal', then recomputes - # the geometric limits from the vertex and edge drawings, and - # autoscales the view limits of the plot using these limits. + # Sets the aspect ratio of the axes to 'equal', then recomputes + # the geometric limits from the vertex and edge drawings, removes + # any default margins, and then autoscales the view limits of the plot + # using these limits. ax.set_aspect('equal', adjustable='box') ax.relim() + ax.margins(0) ax.autoscale_view() #flipping y axis direction @@ -715,9 +720,8 @@ cdef class PGraph: Returns: A mapping between the integers assigned to the vertices, by - :py:class:`~planarity.classic.planarity.PGraph` initialization, + :py:class:`~planarity.express.planarity.PGraph` initialization, and their original labels provided to - :py:class:`~planarity.classic.planarity.PGraph` initialization. + :py:class:`~planarity.express.planarity.PGraph` initialization. """ return self.reverse_nodemap - diff --git a/planarity/classic/planarity_functions.py b/planarity/express/planarity_functions.py similarity index 74% rename from planarity/classic/planarity_functions.py rename to planarity/express/planarity_functions.py index 487e83d..741072a 100644 --- a/planarity/classic/planarity_functions.py +++ b/planarity/express/planarity_functions.py @@ -18,22 +18,22 @@ def is_planar(graph): """Tests whether or not the graph is planar. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` - and calls its :py:meth:`~planarity.classic.planarity.PGraph.is_planar` + :py:class:`~planarity.express.planarity.PGraph` + and calls its :py:meth:`~planarity.express.planarity.PGraph.is_planar` method. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. Returns: ``True`` if the graph is planar, or ``False`` if not. Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if an error was encountered by C-layer methods such as ``gp_Embed()``. RuntimeError: if a prior invocation of this method already failed. @@ -47,13 +47,13 @@ def kuratowski_edges(graph): """Returns a list of the edges in a minimal non-planar subgraph of the graph. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` + :py:class:`~planarity.express.planarity.PGraph` and calls its - :py:meth:`~planarity.classic.planarity.PGraph.kuratowski_edges` method. + :py:meth:`~planarity.express.planarity.PGraph.kuratowski_edges` method. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. Returns: A list of the edges in a minimal non-planar subgraph of the graph, @@ -61,9 +61,9 @@ def kuratowski_edges(graph): Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if an error was encountered by C-layer methods such as ``gp_Embed()``. RuntimeError: if a prior invocation of this method already failed. @@ -77,22 +77,22 @@ def ascii(graph) -> str: """Produces an ASCII string rendition of the graph, if it is planar. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` + :py:class:`~planarity.express.planarity.PGraph` and calls its - :py:meth:`~planarity.classic.planarity.PGraph.ascii` method. + :py:meth:`~planarity.express.planarity.PGraph.ascii` method. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. Returns: An ASCII string rendition of a planar graph. Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if an error was encountered by C-layer methods such as ``gp_Embed()`` or ``gp_DrawPlanar_RenderToString()``. RuntimeError: if a prior invocation of this method already failed. @@ -107,21 +107,21 @@ def draw(graph, labels=True, outfileName=None, **kwargs): """Draws the graph with Matplotlib, if it is planar. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` + :py:class:`~planarity.express.planarity.PGraph` and calls its - :py:meth:`~planarity.classic.planarity.PGraph.draw` method with the + :py:meth:`~planarity.express.planarity.PGraph.draw` method with the given ``labels`` and ``outfileName``. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. labels (bool): If ``True``, vertex labels are rendered in the drawing. Otherwise, vertices are rendered unlabelled in the drawing. outfileName (:obj:`str`): File to which to output a Matplotlib rendering of the planar graph. If not given, then the caller can call :external+matplotlib:py:func:`matplotlib.pyplot.savefig`. **kwargs: Optional figure-level parameters forwarded to - :py:meth:`~planarity.classic.planarity.PGraph.draw`. Supported keys + :py:meth:`~planarity.express.planarity.PGraph.draw`. Supported keys are: * ``figsize`` and ``dpi`` @@ -132,15 +132,15 @@ def draw(graph, labels=True, outfileName=None, **kwargs): * ``edge_linecolor`` * ``transparent`` - Please see the :py:meth:`~planarity.classic.planarity.PGraph.draw` + Please see the :py:meth:`~planarity.express.planarity.PGraph.draw` documentation for more information and for default values. Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. ValueError: if an unsupported keyword argument is given. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. ImportError: if dependencies from Matplotlib fail to be imported. RuntimeError: if an error was encountered by C-layer methods such as ``gp_Embed()``. @@ -164,14 +164,14 @@ def write(graph, path: str = 'stdout', writeMode: int = WRITE_ADJLIST) -> None: """Writes the graph to ``path``. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` + :py:class:`~planarity.express.planarity.PGraph` and calls its - :py:meth:`~planarity.classic.planarity.PGraph.write` method with the + :py:meth:`~planarity.express.planarity.PGraph.write` method with the specified ``path`` and ``writeMode``. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. path (str): Path to which to write graph. Defaults to ``stdout`` stream. writeMode (int): Format to write the graph. Defaults to @@ -179,9 +179,9 @@ def write(graph, path: str = 'stdout', writeMode: int = WRITE_ADJLIST) -> None: Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the C-layer ``gp_Write()`` failed. """ planarity.PGraph(graph).write(path, writeMode) @@ -192,24 +192,24 @@ def mapping(graph) -> dict[int, typing.Any]: """Returns the map of integer vertex labels to their original labels. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` + :py:class:`~planarity.express.planarity.PGraph` and calls its - :py:meth:`~planarity.classic.planarity.PGraph.mapping` method. + :py:meth:`~planarity.express.planarity.PGraph.mapping` method. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. Returns: A mapping between the integers assigned to the vertices, by - :py:class:`~planarity.classic.planarity.PGraph` initialization, + :py:class:`~planarity.express.planarity.PGraph` initialization, and their original labels provided to - :py:class:`~planarity.classic.planarity.PGraph` initialization. + :py:class:`~planarity.express.planarity.PGraph` initialization. Raises: ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. """ return planarity.PGraph(graph).mapping() diff --git a/planarity/classic/planarity_networkx.py b/planarity/express/planarity_networkx.py similarity index 71% rename from planarity/classic/planarity_networkx.py rename to planarity/express/planarity_networkx.py index 06abd4b..88d16c2 100644 --- a/planarity/classic/planarity_networkx.py +++ b/planarity/express/planarity_networkx.py @@ -12,26 +12,26 @@ def kuratowski_subgraph(graph): """Returns a :external+networkx:py:class:`networkx.Graph` containing a minimal non-planar subgraph of the graph. Constructs a - :py:class:`~planarity.classic.planarity.PGraph` + :py:class:`~planarity.express.planarity.PGraph` and calls its - :py:meth:`~planarity.classic.planarity.PGraph.kuratowski_edges` method, then + :py:meth:`~planarity.express.planarity.PGraph.kuratowski_edges` method, then converts the edge-list to a :external+networkx:py:class:`networkx.Graph`. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. Returns: A :external+networkx:py:class:`networkx.Graph` containing a minimal non-planar subgraph of a non-planar graph. Raises: - ValueError: if the given graph is already a - :py:class:`~planarity.classic.planarity.PGraph`. - RuntimeError: if the graph couldn't be converted to a - :py:class:`~planarity.classic.planarity.PGraph`. + ValueError: if the given graph is already a + :py:class:`~planarity.express.planarity.PGraph`. + RuntimeError: if the graph couldn't be converted to a + :py:class:`~planarity.express.planarity.PGraph`. RuntimeError: if an error was encountered by C-layer methods such - as ``gp_Embed()``. + as ``gp_Embed()``. ImportError: On failure to import ``NetworkX``. """ try: @@ -45,15 +45,15 @@ def kuratowski_subgraph(graph): def networkx_graph(pgraph): - """Builds a :external+networkx:py:class:`networkx.Graph` from a :py:class:`~planarity.classic.planarity.PGraph`. + """Builds a :external+networkx:py:class:`networkx.Graph` from a :py:class:`~planarity.express.planarity.PGraph`. Args: - pgraph: A :py:class:`~planarity.classic.planarity.PGraph` to convert to + pgraph: A :py:class:`~planarity.express.planarity.PGraph` to convert to a A :external+networkx:py:class:`networkx.Graph`. Returns: A :external+networkx:py:class:`networkx.Graph` built from a - :py:class:`~planarity.classic.planarity.PGraph`. + :py:class:`~planarity.express.planarity.PGraph`. Raises: ImportError: On failure to import ``NetworkX``. @@ -69,15 +69,15 @@ def networkx_graph(pgraph): def pgraph_graph(graph): - """Builds a :py:class:`~planarity.classic.planarity.PGraph` from the graph. + """Builds a :py:class:`~planarity.express.planarity.PGraph` from the graph. Args: graph: A graph specified in a format that may be converted to a - :py:class:`~planarity.classic.planarity.PGraph`, including a + :py:class:`~planarity.express.planarity.PGraph`, including a :external+networkx:py:class:`networkx.Graph`. Returns: - A :py:class:`~planarity.classic.planarity.PGraph` representing the same + A :py:class:`~planarity.express.planarity.PGraph` representing the same graph as the input ``graph``. """ return planarity.PGraph(graph) diff --git a/pyproject.toml b/pyproject.toml index aa1b136..1bf23e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,4 @@ -# Based on documentation from +# Based on documentation from # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ # and https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html @@ -18,7 +18,7 @@ authors = [ # NOTE: Only one of authors and maintainers is required, per # https://packaging.python.org/en/latest/specifications/pyproject-toml # However, also having maintainers allows a different first maintainer -# to appear, since only the first entry shows on the package page +# to appear, since only the first entry shows on the package page maintainers = [ {name = "Wanda B. K. Boyer", email = "wbkboyer@gmail.com"}, @@ -72,11 +72,11 @@ dynamic = ["version"] include-package-data = false packages = [ "planarity", - "planarity.classic", + "planarity.express", "planarity.full", ] -# NOTE: This enables dynamic metadata, e.g. versioning, to be computed during +# NOTE: This enables dynamic metadata, e.g. versioning, to be computed during # the build process; see: # https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata # NOTE: In the future, we could automatically generate the version number by diff --git a/setup.py b/setup.py index f5515d7..29626f6 100644 --- a/setup.py +++ b/setup.py @@ -4,10 +4,10 @@ # NOTE: As soon as pyproject.toml supports glob notation, we should be able to # remove setup.py and allow setuptools to leverage that configuration. See: # https://github.com/pypa/setuptools/discussions/4154 -classic_sourcefiles = [ - "planarity/classic/planarity.pyx" +express_sourcefiles = [ + "planarity/express/planarity.pyx" ] -classic_sourcefiles.extend(glob("planarity/c/graphLib/**/*.c", recursive=True)) +express_sourcefiles.extend(glob("planarity/c/graphLib/**/*.c", recursive=True)) graphLib_sourcefiles = [ "planarity/full/graphLib.pyx" @@ -18,8 +18,8 @@ extensions = [ Extension( - name="planarity.classic.planarity", - sources=classic_sourcefiles, + name="planarity.express.planarity", + sources=express_sourcefiles, include_dirs=['planarity/c/graphLib'], # extra_compile_args=["-DDEBUG"], # Uncomment if you want to see debugNOTOK() statements ),