Skip to content

Issue #89 - upgrade graphLib to match EAPS 5.1.0.0 - #113

Merged
john-boyer-phd merged 16 commits into
masterfrom
Issue89-EAPS-5100-Upgrade
Oct 16, 2026
Merged

john-boyer-phd merged 16 commits into
masterfrom
Issue89-EAPS-5100-Upgrade

Conversation

@wbkboyer

@wbkboyer wbkboyer commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Resolves #89

Co-authored with @john-boyer-phd

Type of change

Please check only relevant options:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Changes

Added

  • examples/full/demo_embedding_facelister.py - Demonstrates how to obtain the YAML representation of planar embedding's face list.

Updated

  • doc/source/conf.py
    • napoleon_include_private_with_doc and napoleon_include_special_with_doc set to False so that we no longer include magic methods coming from Cython compilation (e.g. __setstate_cython__(), __reduce_cython__()) nor private methods without docstrings.
    • Fixed breaking issue with make latexpdf due to max depth of unordered lists (i.e. added latex_elements config for maxlistdepth, which must be at least 7 currently).
  • planarity/__init__.py
    • Split out exposing planarity.full.graphLib.gp_GetRandomNumber() from planarity.full.graphLib.gp_GetQuietMode() and planarity.full.graphLib.gp_SetQuietMode(), since they're not conceptually related.

    • Surfaced constants:

      • GRAPHFLAGS_DIRECTEDEDGEDETECTED
      • EDGE_TYPE_CROSS
      • DFSUTILS_NAME
      • GRAPHFLAGS_EXTENDEDWITH_DFSUTILS
      • GRAPHFLAGS_DFSNUMBERED
      • GRAPHFLAGS_SORTEDBYDFI
      • GRAPHFLAGS_LOWPOINTSCOMPUTED
      • GRAPHFLAGS_DFSNUMBERED_DIRECTED
      • DFSMODE_UNDIRECTED
      • DFSMODE_DIRECTED

      NOTE: @john-boyer-phd confirms that we will not surface EMBEDFLAGS_SEARCHFORK5, EMBEDFLAGS_SEARCHFORK5MINOR, EMBEDFLAGS_MAXIMALPLANARSUBGRAPH, EMBEDFLAGS_PROJECTIVEPLANAR, nor EMBEDFLAGS_TOROIDAL at this time

    • Removed call to "private" planarity.full.graphLib.__seed_rng() (which has been removed), since srand() should now be called upon graphLib module initialization when users import (from) planarity package.

Logs demonstrating efficacy of seeding the random number generator on graphLib module initialization
(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_random_int.py 
Some pseudo-random numbers between 1 and 10:
6
10
8
10
(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_random_int.py
Some pseudo-random numbers between 1 and 10:
2
6
6
5
(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_random_int.py
Some pseudo-random numbers between 1 and 10:
1
5
2
9
(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_random_int.py
Some pseudo-random numbers between 1 and 10:
7
6
7
2
(.venv) wbkboyer@Wandas-MacBook-Pro planarity % cd ..
(.venv) wbkboyer@Wandas-MacBook-Pro git % python
Python 3.14.6 (v3.14.6:c63aec69bd5, Jun 10 2026, 08:07:54) [Clang 21.0.0 (clang-2100.1.1.101)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import planarity
>>> print(planarity.gp_GetRandomNumber(1, 10))
10
>>> print(planarity.gp_GetRandomNumber(1, 10))
4
>>> print(planarity.gp_GetRandomNumber(1, 10))
1
>>> print(planarity.gp_GetRandomNumber(1, 10))
10
>>> print(planarity.gp_GetRandomNumber(1, 10))
8
>>> print(planarity.gp_GetRandomNumber(1, 10))
3
>>> print(planarity.gp_GetRandomNumber(1, 10))
4
>>> exit()
(.venv) wbkboyer@Wandas-MacBook-Pro git % python
Python 3.14.6 (v3.14.6:c63aec69bd5, Jun 10 2026, 08:07:54) [Clang 21.0.0 (clang-2100.1.1.101)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from planarity import gp_GetRandomNumber
>>> print(gp_GetRandomNumber(1, 10))
9
>>> print(gp_GetRandomNumber(1, 10))
6
>>> print(gp_GetRandomNumber(1, 10))
3
>>> print(gp_GetRandomNumber(1, 10))
1
>>> exit()
(.venv) wbkboyer@Wandas-MacBook-Pro git % python
Python 3.14.6 (v3.14.6:c63aec69bd5, Jun 10 2026, 08:07:54) [Clang 21.0.0 (clang-2100.1.1.101)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from planarity import *
>>> print(gp_GetRandomNumber(1, 10))
3
>>> print(gp_GetRandomNumber(1, 10))
6
>>> print(gp_GetRandomNumber(1, 10))
3
>>> print(gp_GetRandomNumber(1, 10))
9
>>> print(gp_GetRandomNumber(1, 10))
4
>>> print(gp_GetRandomNumber(1, 10))
3
>>> print(gp_GetRandomNumber(1, 10))
9
>>> print(gp_GetRandomNumber(1, 10))
8
>>> exit()
  • planarity/express/

    • planarity.pyx
      • Reflow of some docstring line lengths
      • Updated unordered lists to use - rather than *, and use : instead of - within list items for further descriptions.
      • Fixed reference error (i.e. :py:meth:`mapping` should be :py:meth:`~planarity.express.planarity.PGraph.mapping`).
      • With update to doc/source/conf.py, latexpdf build error due to unordered list depth stemming from PGraph.draw() docstring has been fixed
      • Added . to end of Returns statement for PGraph.ascii() docstring
    • planarity_functions.py
      • Reflow of some docstring line lengths
      • Updated unordered lists to use - rather than *, and use : instead of - within list items for further descriptions.
  • planarity/full/

    • cgraphLib.pxd
      • Added definitions for:
        • gp_DynamicInsertEdge()
        • gp_ClearEdgeDirectionFlags()
        • gp_TransposeDirectedGraph()
        • g6_SetOutputErrorFlag()
        • gp_DepthFirstSearchEx() (made sure signature expects unsigned int)
        • gp_CountConnectedComponents()
        • gp_GetVisitedIndex()
        • gp_IsDFSTreeRoot() (added now that this macro calls public gp_GetParent() rather than private gp_GetVertexParent())
        • gp_IsNotDFSTreeRoot() (see preceding)
        • gp_GetVertexFromBicompRoot() (ditto)
        • gp_CountEmbeddingFaces()
        • gp_CreateEmbeddingFaceList()
      • Updated signature for functions that accept unsigned int or that return unsigned int:
        • gp_IsNeighborDirected()
        • gp_FindDirectedEdge()
        • gp_GetGraphFlags()
        • gp_Embed()
        • gp_GetObstructionMinorType()
      • Surfaced constants from their respective C headers:
        • GRAPHFLAGS_DIRECTEDEDGEDETECTED
        • EDGE_TYPE_CROSS
        • GRAPHFLAGS_LOWPOINTSCOMPUTED
        • GRAPHFLAGS_DFSNUMBERED_DIRECTED
        • DFSMODE_UNDIRECTED
        • DFSMODE_DIRECTED
      • Rearranged constants so that they appear in the same order and logical grouping as in the original C graphLib headers
    • graphLib.pyx
      • Removed __seed_rng() function since we don't need to expose this function to users, and import random + calling srand() from within graphLib module works to seed the random number generator at module initialization (only occurs once upon import).
      • Surfaced constants from cgraphLib.pxd definition file:
        • GRAPHFLAGS_DIRECTEDEDGEDETECTED
        • EDGE_TYPE_CROSS
        • GRAPHFLAGS_LOWPOINTSCOMPUTED
        • GRAPHFLAGS_DFSNUMBERED_DIRECTED
        • DFSMODE_UNDIRECTED
        • DFSMODE_DIRECTED
      • Added Cython wrapper functions for C-layer functions with the same names:
        • gp_DynamicInsertEdge()
        • gp_ClearEdgeDirectionFlags()
        • gp_TransposeDirectedGraph()
        • g6_SetOutputErrorFlag()
        • gp_DepthFirstSearchEx()
        • gp_CountConnectedComponents()
        • gp_GetVisitedIndex()
        • gp_IsDFSTreeRoot()
        • gp_IsNotDFSTreeRoot()
        • gp_GetVertexFromBicompRoot()
        • gp_CountEmbeddingFaces()
        • gp_CreateEmbeddingFaceList()
      • Updated signature for functions that accept unsigned int or that return unsigned int:
        • gp_IsNeighborDirected()
        • gp_FindDirectedEdge()
        • gp_Embed()
        • gp_GetObstructionMinorType()
    • graphLib.pxd
      • Surfaced Cython wrapper functions so they are available to other Cython modules (i.e. graph.pyx and g6IterationUtils.pyx):
        • gp_DynamicInsertEdge()
        • gp_ClearEdgeDirectionFlags()
        • gp_TransposeDirectedGraph()
        • g6_SetOutputErrorFlag()
        • gp_DepthFirstSearchEx()
        • gp_CountConnectedComponents()
        • gp_GetVisitedIndex()
        • gp_IsDFSTreeRoot()
        • gp_IsNotDFSTreeRoot()
        • gp_GetVertexFromBicompRoot()
        • gp_CountEmbeddingFaces()
        • gp_CreateEmbeddingFaceList()
      • Updated signature for functions that accept unsigned int or that return unsigned int:
        • gp_IsNeighborDirected()
        • gp_FindDirectedEdge()
        • gp_Embed()
        • gp_GetObstructionMinorType()
    • g6IterationUtils.pyx‎ - added method g6_SetOutputErrorFlag() to call graphLib.pyx wrapper function (removed reference to EAPS PR #313 made using appropriate RST syntax within docstring due to this feedback).
    • graph.pyx
      • Surfaced constants from graphLib.pyx (so that we don't have to reference them by graphLib.XXXX within the module):
        • GRAPHFLAGS_DIRECTEDEDGEDETECTED
        • VERTEX_VISITED_MASK
        • VERTEX_MARKED_MASK
        • EDGE_VISITED_MASK
        • EDGE_MARKED_MASK
        • EDGE_TYPE_MASK
        • EDGE_TYPE_CROSS
        • EDGEFLAG_INVERTED_MASK
        • EDGEFLAG_DIRECTION_MASK
        • DEFAULT_EDGE_CAPACITY_FACTOR
        • GRAPHFLAGS_ZEROBASEDIO
        • DFSUTILS_NAME
        • GRAPHFLAGS_EXTENDEDWITH_DFSUTILS
        • GRAPHFLAGS_DFSNUMBERED
        • GRAPHFLAGS_SORTEDBYDFI
        • GRAPHFLAGS_LOWPOINTSCOMPUTED
        • DFSMODE_UNDIRECTED
        • DFSMODE_DIRECTED
        • GRAPHFLAGS_DFSNUMBERED_DIRECTED
        • PLANARITY_NAME
        • GRAPHFLAGS_EXTENDEDWITH_PLANARITY
        • MINORTYPE_NONE
        • MINORTYPE_A
        • MINORTYPE_B
        • MINORTYPE_C
        • MINORTYPE_D
        • MINORTYPE_E
        • MINORTYPE_E1
        • MINORTYPE_E2
        • MINORTYPE_E3
        • MINORTYPE_E4
        • MINORTYPE_E5
        • MINORTYPE_E6
        • MINORTYPE_E7
        • OUTERPLANARITY_NAME
        • GRAPHFLAGS_EXTENDEDWITH_OUTERPLANARITY
        • DRAWPLANAR_NAME
        • K23SEARCH_NAME
        • K33SEARCH_NAME
        • K4SEARCH_NAME
      • Added wrapper methods within Graph class of graphLib.pyx wrapper functions for C-layer functions with the same names, plus sensible docstrings:
        • gp_DynamicInsertEdge()
        • gp_ClearEdgeDirectionFlags()
        • gp_TransposeDirectedGraph()
        • g6_SetOutputErrorFlag()
        • gp_DepthFirstSearchEx()
        • gp_CountConnectedComponents()
        • gp_GetVisitedIndex()
        • gp_CountEmbeddingFaces()
        • gp_CreateEmbeddingFaceList()
        • gp_GetEmbedFlags()
        • gp_GetObstructionMinorType()
      • Updated wrapper methods to call their correct correspondents:
        • gp_IsDFSTreeRoot()
        • gp_IsNotDFSTreeRoot()
        • gp_GetVertexFromBicompRoot()
      • Updated parameter types and return value typehints for functions that accept unsigned int or that return unsigned int:
        • gp_IsNeighborDirected()
        • gp_FindDirectedEdge()
        • gp_Embed()
      • Updated docstring of gp_GetGraphFlags() to fully enumerate return values.
      • Added EDGE_TYPE_CROSS as valid return type or parameter value for gp_GetEdgeType(), gp_SetEdgeType(), gp_ResetEdgeType()
  • Fixed some whitespace issues and issue with # comment not followed by a space highlighted by cython-lint.

  • Across docstrings in all modules, fixed missing cross-references by searching for "See" (e.g. gp_HideEdge(), gp_DepthFirstSearch())

  • Tried to fixup Raises docstring sections so that each possible way a given exception type can be raised is individually enumerated (e.g gp_WriteToString())

NOTE: I've noticed some issues rendering make latexpdf, namely that the first line of some list items is bolded, and noticed some issues with line breaks in make html output.

Removed

  • N/A

Testing

  • Outline manual or automated tests performed and how it relates to the particular feature added or functionality changed
    • Confirmed change to remove __seed_rng() function from planarity.full.graphLib module, preferencing calling srand() upon module initialization by:
      • Running examples/full/demo_random_int.py
      • In separate IDLE sessions:
        • when I import planarity and then call print(planarity.gp_GetRandomNumber(1, 10)) multiple times, the numbers are all different and don't follow a pattern
        • when I call from planarity import * and call print(gp_GetRandomNumber(1, 10)) multiple times, the numbers are all different and don't follow a pattern
  • Upload logs or provide relevant snippets of terminal output to demo functionality
Output from running examples/full/demo_embedding_facelister.py
(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_embedding_facelister.py -i ~/git/edge-addition-planarity-suite-fork/c/samples/Petersen.0-based.txt -o . -m x

===================================================
This program imports the planarity package, which
is based on the Edge Addition Planarity Suite
version 5.1.0.0, which contains the libPlanarity
graph library version 5:0:1.
===================================================

Unable to produce embedding face list, as input graph '/Users/wbkboyer/git/edge-addition-planarity-suite-fork/c/samples/Petersen.0-based.txt' is nonplanar.

(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_embedding_facelister.py -i ~/git/edge-addition-planarity-suite-fork/c/samples/nauty_example.g6 -o .         

===================================================
This program imports the planarity package, which
is based on the Edge Addition Planarity Suite
version 5.1.0.0, which contains the libPlanarity
graph library version 5:0:1.
===================================================

Planar embedding has 1 face.

(.venv) wbkboyer@Wandas-MacBook-Pro planarity % python examples/full/demo_embedding_facelister.py -i ~/git/edge-addition-planarity-suite-fork/c/samples/drawExample.txt -o .

===================================================
This program imports the planarity package, which
is based on the Edge Addition Planarity Suite
version 5.1.0.0, which contains the libPlanarity
graph library version 5:0:1.
===================================================

Planar embedding has 9 faces.

Petersen.0-based.p.out.txt

nauty_example.face-list.yaml
nauty_example.p.out.txt

drawExample.face-list.yaml
drawExample.p.out.txt

john-boyer-phd and others added 9 commits October 2, 2026 13:23
…parameters and return types (e.g. graph flags, embed flags, edge direction, DFS mode). Created workaround so that `__seed_rng()` function can still be called when one imports `planarity`, but use name mangling to ensure that library users can't `import planarity` or `from planarity import *` and access this private function. Updated sphinx docs generation so that we no longer include `__reduce_cython__()`, `__setstate_cython__()`, nor `__seed_rng()`; it turns out this was likely a `napoleon` extension issue with `napoleon_include_special_with_doc` (but I also set `napoleon_include_private_with_doc` to `False` as well). Also fixed issue with `unsigned` type not being recognized by sphinx (i.e. `nitpicky` config option was highlighting issue), so I added `nitpicky_ignore`. Now exposes `G6WriteIterator`'s `g6_SetOutputErrorFlag()` (which should be used if an error occurs during processing so that we clean up the .g6 output file upon freeing the writer, even if the issue wasn't with the writer itself), as well as `Graph`'s `gp_DynamicInsertEdge()`, `gp_ClearEdgeDirectionFlags()`, `gp_TransposeDirectedGraph()`, `gp_CountConnectedComponents()`, `gp_GetVisitedIndex()`, `gp_GetEmbedFlags()`, and `gp_GetObstructionMinorType()`. Also addressed `FIXME`s in `gp_IsDFSTreeRoot()`, `gp_IsNotDFSTreeRoot()`, and `gp_GetVertexFromBicompRoot()` now that the underlying macros don't rely on a private macro, `gp_GetVertexParent()`.

Confirmed `pytest` ran, and that `examples\full\demo_specific_graph.py` worked for `nauty_example.g6` and `Petersen.0-based.txt`.
…df documentation.

* Fully enumerate options returned by `gp_GetGraphFlags()`.
* Fixed latexpdf build error due to unordered list depth
* Fixed reference error (i.e. :py:meth:`mapping` should be :py:meth:`~planarity.express.planarity.PGraph.mapping`).
* Updated unordered lists to use `-` rather than `*`, and use `:` instead of `-` within list items for further descriptions.
* Changed some line lengths for comments.
* Fixed some whitespace issues and issue with `#` comment not followed by a space highlighted by `cython-lint`.
* Fixed some cross-references by searching for "See".
* Added external link to planarity Issue #91 in docstring for `PGraph.draw()`

I've noticed some issues rendering `make latexpdf`, namely that the first line of some list items is bolded, and noticed some issues with line breaks in `make html` output.
…, since we don't need to expose this function to users, and calling `srand()` from within `graphLib` module works to seed the random number generator at module initialization (only occurs once upon import).

Confirmed by:
- Running `examples/full/demo_random_int.py`
- In separate IDLE sessions:
    - when I `import planarity` and then call `print(planarity.gp_GetRandomNumber(1, 10))` multiple times, the numbers are all different and don't follow a pattern
    - when I call `from planarity import *` and call `print(gp_GetRandomNumber(1, 10))` multiple times, the numbers are all different and don't follow a pattern

@john-boyer-phd john-boyer-phd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great. Just a few minor changes. Most notable is need to add the face lister function.

Comment thread planarity/express/planarity.pyx Outdated
Comment thread planarity/express/planarity.pyx Outdated
Comment thread planarity/full/cgraphLib.pxd Outdated
Comment thread planarity/full/graph.pyx Outdated
Comment thread planarity/full/graph.pyx Outdated
@wbkboyer

wbkboyer commented Oct 14, 2026 •

Copy link
Copy Markdown
Member Author

Per discussion with @john-boyer-phd , flags should be considered as int rather than unsigned int, and so the following function signatures will need to be changed back in cgraphLib.pxd definition file, graphLib.pyx extension, graphLib.pxd definition file, and graph.pyx wrapper methods:

  • gp_IsNeighborDirected() - direction will be considered an int
  • gp_FindDirectedEdge() - direction will be considered an int
  • gp_GetGraphFlags() - will return an int
  • gp_DepthFirstSearchEx() - mode will be considered an int
  • gp_Embed() - embedFlags will be considered an int
  • gp_GetEmbedFlags() - will return an int
  • gp_GetObstructionMinorType() - will return an int

(Note gp_GetQuietMode() and gp_SetQuietMode() already return and accept int in cgraphLib.pxd and graphLib.pyx)

Also, all references in docstrings will have to be updated, and docs/source/conf.py will have nitpick_ignore removed.

In EAPS, an upcoming issue will be created to change the types for embedFlags and graphFlags on graphStruct to be int rather than unsigned, and for flags in both edgeRec and vertexRec structs to be changed to an int, as well as for the above function signatures to be updated, in addition to:

  • gp_GetQuietMode() - will return an int
  • gp_SetQuietMode() - newQuietMode will be considered an int

NOTE: Since QUIETMODE_ALL is defined to be 0XFFFFFFFF, when interpreted as an int will be considered -1; this still works, as it is a unique value from QUIETMODE_NONE, QUIETMODE_ERRORS, and QUIETMODE_MESSAGES, so perhaps we could set #define QUIETMODE_ALL 0X7FFFFFFF instead?

This is all because Python doesn't natively have a notion of an unsigned integer (without using numpy's uint or uint32 or cython's uint) or we likely won't be using the highest order bit for a flag, and if so, can add an additional int flag to whatever struct so we can re-use numerical values in a new context.

EDIT: Updates to cgraphLib.pxd definition file, graphLib.pyx extension, graphLib.pxd definition file, and graph.pyx's Graph wrapper class methods are in 6bc327

…int` rather than `unsigned`/`unsigned int`/`uint`
…f constants surfaced from `graphPlanarity.h` to match header order.
… including incorrect signature in module docstring, removing unused imports, moving output directory creation until after successful embedding operation, and `graph_output_mode` shadowed scope warning.
@wbkboyer
wbkboyer marked this pull request as ready for review October 15, 2026 16:25
Comment thread planarity/full/graph.pyx Outdated
…bed()` has not yet been called, so removing this TODO item.

@john-boyer-phd john-boyer-phd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great, all set!

@john-boyer-phd
john-boyer-phd merged commit 93e0b34 into master Oct 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update with graphLib from EAPS 5.1.0.0 release

2 participants