From 403dcbc21fe93bd261bb6a4b9b8b367873192b53 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 28 May 2021 16:18:00 -0400 Subject: [PATCH 1/2] Bump cibuildwheel version to latest release (#345) * Bump cibuildwheel version to latest release This commit bumps the cibuildwheel version we use for the release jobs to the latest release. * DNM: Test wheel builds * Update ppc64le job to not preinstall rust on host * Add permissions workaround from test job to ppc64le wheel job * Add quotes to travis job test requirements * Revert "DNM: Test wheel builds" This reverts commit 3857ee55ece37f1109cadbc6cdbe569d338a7030. --- .github/workflows/wheels.yml | 6 +++--- .travis.yml | 18 +++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 4188952062..3bd96f63aa 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -45,7 +45,7 @@ jobs: toolchain: stable - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==1.9.0 twine + python -m pip install cibuildwheel==1.11.1 twine - name: Build wheels run: | python -m cibuildwheel --output-dir wheelhouse @@ -73,7 +73,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build wheels - uses: joerick/cibuildwheel@v1.9.0 + uses: joerick/cibuildwheel@v1.11.1 env: CIBW_BEFORE_ALL: rustup target add aarch64-apple-darwin CIBW_ARCHS_MACOS: arm64 universal2 @@ -113,7 +113,7 @@ jobs: run: rustup default stable-i686-pc-windows-msvc - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==1.9.0 twine + python -m pip install cibuildwheel==1.11.1 twine - name: Build wheels run: | python -m cibuildwheel --output-dir wheelhouse diff --git a/.travis.yml b/.travis.yml index 29d901620c..82efc41f96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,12 +66,12 @@ jobs: - CIBW_BEFORE_BUILD="pip install -U setuptools-rust" - CIBW_SKIP="cp27-* cp34-* cp35-* pp*" - CIBW_ENVIRONMENT='PATH="$PATH:$HOME/.cargo/bin"' - - CIBW_TEST_REQUIRES=networkx testtools fixtures + - CIBW_TEST_REQUIRES="networkx testtools fixtures" - TWINE_USERNAME=retworkx-ci - CIBW_TEST_COMMAND="python -m unittest discover {project}/tests" if: tag IS present script: - - pip install -U twine importlib-metadata keyring cibuildwheel==1.9.0 + - pip install -U twine importlib-metadata keyring cibuildwheel==1.11.1 - cibuildwheel --output-dir wheelhouse - twine upload wheelhouse/* - stage: deploy @@ -80,11 +80,7 @@ jobs: services: - docker before_install: - - which python - - sh tools/install_rust.sh - - export PATH=~/.cargo/bin:$PATH - - which python - - pip install -U pip virtualenv tox + - sudo chown travis -R $TRAVIS_HOME/.cargo install: - echo "" env: @@ -92,12 +88,12 @@ jobs: - CIBW_BEFORE_BUILD="bash {project}/tools/install_rust.sh" - CIBW_SKIP="cp27-* cp34-* cp35-* pp*" - CIBW_ENVIRONMENT='PATH="$PATH:$HOME/.cargo/bin"' - - CIBW_TEST_REQUIRES=networkx testtools fixtures + - CIBW_TEST_REQUIRES="networkx testtools fixtures" - TWINE_USERNAME=retworkx-ci - CIBW_TEST_COMMAND="python -m unittest discover {project}/tests" if: tag IS present script: - - pip install -U twine importlib-metadata keyring cibuildwheel==1.9.0 + - pip install -U twine importlib-metadata keyring cibuildwheel==1.11.1 - cibuildwheel --output-dir wheelhouse - twine upload wheelhouse/* - stage: deploy @@ -114,11 +110,11 @@ jobs: - CIBW_BEFORE_BUILD="pip install -U setuptools-rust" - CIBW_SKIP="cp27-* cp34-* cp35-* pp*" - CIBW_ENVIRONMENT='PATH="$PATH:$HOME/.cargo/bin"' - - CIBW_TEST_REQUIRES=networkx testtools fixtures + - CIBW_TEST_REQUIRES="networkx testtools fixtures" - TWINE_USERNAME=retworkx-ci - CIBW_TEST_COMMAND="python -m unittest discover {project}/tests" if: tag IS present script: - - sudo pip install -U twine importlib-metadata keyring cibuildwheel==1.9.0 + - sudo pip install -U twine importlib-metadata keyring cibuildwheel==1.11.1 - cibuildwheel --output-dir wheelhouse - twine upload wheelhouse/* From aad90f77f15912eb5b534467e3346a4781987c4e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sat, 29 May 2021 06:25:54 -0400 Subject: [PATCH 2/2] Add visualization to retworkx for networkx users guide (#344) * Add visualization to retworkx for networkx users guide This commit adds a section to the retworkx for networkx users guide on visualizations. This is important especially for the ``mpl_draw`` function since it's mostly identical to the ``networkx_drawer`` function but there are some key differences to keep in mind. This commit also makes small change to the graphviz drawer, the name of the function is renamed from `pydot_draw` to `graphviz_draw` and the Python module is renamed from `pydot` to `graphviz`. This was done to be more descriptive as graphviz is the backend not pydot. It also makes it easier to migrate away from pydot in the future if we needed to since the key piece is using graphviz not a particular python interface to it. Fixes #341 * Update release note too * Rename optional target to graphviz --- .gitignore | 2 + docs/source/api.rst | 2 + docs/source/networkx.rst | 64 +++++++++++++++++++ docs/source/visualization.rst | 2 +- .../notes/pydot-drawer-c5df0aa830679748.yaml | 6 +- retworkx/visualization/__init__.py | 4 +- .../visualization/{pydot.py => graphviz.py} | 8 +-- setup.py | 2 +- .../{test_pydot.py => test_graphviz.py} | 36 +++++------ 9 files changed, 97 insertions(+), 29 deletions(-) rename retworkx/visualization/{pydot.py => graphviz.py} (97%) rename tests/visualization/{test_pydot.py => test_graphviz.py} (78%) diff --git a/.gitignore b/.gitignore index cebcd6d062..40098e2a7c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ retworkx/*pyd *.stestr/ *.ps *.png +*.svg +*.jpg diff --git a/docs/source/api.rst b/docs/source/api.rst index 7dbad1350c..9f86309315 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -145,6 +145,8 @@ type functions in the algorithms API but can be run with a retworkx.random_layout retworkx.spring_layout +.. _layout-functions: + Layout Functions ================ diff --git a/docs/source/networkx.rst b/docs/source/networkx.rst index 9bd8a60b9b..b114f6aa00 100644 --- a/docs/source/networkx.rst +++ b/docs/source/networkx.rst @@ -306,6 +306,70 @@ This difference with retworkx is primarily because numpy exposes a public C interface which retworkx can interface with directly, while the other libraries and types only expose Python APIs. +Visualization Functions +----------------------- + +NetworkX provides a native drawer with a matplotlib drawer (the +``networkx_drawer*`` functions) and then functions to interface with +``pygraphviz`` and ``pydot`` to enable visualization with graphviz via those +libraries (in addition to functions to serialize graphs in formats other +graph visualization tools can use). NetworkX also provides several functions +`layout functions `__ +for generating different layouts that can be used for visualizing the graph. + + +retworkx has drawer functions with 2 visualization backends, matplotlib +(:func:`~retworkx.visualization.mpl_draw`) and graphviz +(:func:`~retworkx.visualization.graphviz_draw`). Unlike networkx the +:func:`~retworkx.visualization.graphviz_draw` will handle calling graphviz and +generate an image file. For layout functions retworkx has a similar variety of +:ref:`layout-functions`, however it should be noted that retworkx's functions +are strictly 2 dimensional. The also return a :class:`~retworkx.Pos2DMapping` +custom return type which acts as read-only dictionary (which is different from +networkx which returns a normal dictionary that can be modified). + +Matplotlib Drawers +^^^^^^^^^^^^^^^^^^ + +The retwork function :func:`~retworkx.visualization.mpl_draw` function is +basically equivalent to the networkx function ``draw_networkx`` (it was +actually originally forked from the networkx drawer). However, there are some +key differences to keep in mind between the networkx and retworkx matplotlib +drawer. + +``networkx.draw_networkx`` and ``retworkx.mpl_draw`` differences: + +.. list-table:: + :header-rows: 1 + + * - networkx + - retworkx + - Notes + * - ``nodelist`` + - ``node_list`` + - + * - ``edgelist`` + - ``edge_list`` + - + * - ``arrowsize`` + - ``arrow_size`` + - + * - ``labels`` + - ``labels`` + - For ``networkx_drawer`` ``labels`` is a dict of nodes to their label, + while retworkx's ``mpl_drawer`` ``labels`` is a callback function + that will be passed a node's data payload and expected to return the + node's label + * - ``networkx.draw_networkx_edge_labels()`` + - ``edge_labels`` + - NetworkX's ``networkx_drawer`` doesn't have an option for edge labels + and instead adding labels is only exposed via a separate function + ``draw_networkx_edge_labels()`` which requires the ``pos`` dictionary + from the original visualization to be used. retworkx's ``edge_labels`` + kwarg takes a callback function that will be passed an edge's data + payload and expected to return the label. + + .. _networkx_converter: Converting from a networkx graph diff --git a/docs/source/visualization.rst b/docs/source/visualization.rst index 2b0ff2f864..f979a10ee6 100644 --- a/docs/source/visualization.rst +++ b/docs/source/visualization.rst @@ -10,4 +10,4 @@ Visualization API :toctree: stubs retworkx.visualization.mpl_draw - retworkx.visualization.pydot_draw + retworkx.visualization.graphviz_draw diff --git a/releasenotes/notes/pydot-drawer-c5df0aa830679748.yaml b/releasenotes/notes/pydot-drawer-c5df0aa830679748.yaml index e3e9faff40..3ca07d9e4d 100644 --- a/releasenotes/notes/pydot-drawer-c5df0aa830679748.yaml +++ b/releasenotes/notes/pydot-drawer-c5df0aa830679748.yaml @@ -2,7 +2,7 @@ features: - | Added a new `Graphviz `__ based drawer function, - :func:`~retworkx.visualization.pydot_draw`, to the + :func:`~retworkx.visualization.graphviz_draw`, to the :mod:`retworkx.visualization` module. This function requires that Graphviz is installed locally and adds two new optional dependencies, `pydot `__ which is used to call Graphviz @@ -15,7 +15,7 @@ features: .. jupyter-execute:: import retworkx - from retworkx.visualization import pydot_draw + from retworkx.visualization import graphviz_draw def node_attr(node): if node == 0: @@ -26,4 +26,4 @@ features: return {'color': 'red', 'fillcolor': 'red', 'style': 'filled'} graph = retworkx.generators.directed_star_graph(weights=list(range(32))) - pydot_draw(graph, node_attr_fn=node_attr, method='sfdp') + graphviz_draw(graph, node_attr_fn=node_attr, method='sfdp') diff --git a/retworkx/visualization/__init__.py b/retworkx/visualization/__init__.py index 15ea521889..5090c4452c 100644 --- a/retworkx/visualization/__init__.py +++ b/retworkx/visualization/__init__.py @@ -10,8 +10,8 @@ __all__ = [ "mpl_draw", - "pydot_draw", + "graphviz_draw", ] from .matplotlib import mpl_draw -from .pydot import pydot_draw +from .graphviz import graphviz_draw diff --git a/retworkx/visualization/pydot.py b/retworkx/visualization/graphviz.py similarity index 97% rename from retworkx/visualization/pydot.py rename to retworkx/visualization/graphviz.py index 2bbc42e456..20c4d3ca24 100644 --- a/retworkx/visualization/pydot.py +++ b/retworkx/visualization/graphviz.py @@ -17,10 +17,10 @@ except ImportError: HAS_PYDOT = False -__all__ = ["pydot_draw"] +__all__ = ["graphviz_draw"] -def pydot_draw( +def graphviz_draw( graph, node_attr_fn=None, edge_attr_fn=None, @@ -30,7 +30,7 @@ def pydot_draw( method=None, ): """Draw a :class:`~retworkx.PyGraph` or :class:`~retworkx.PyDiGraph` object - using graphviz via pydot + using graphviz .. note:: @@ -87,7 +87,7 @@ def pydot_draw( """ if not HAS_PYDOT: raise ImportError( - "Pydot and Pillow are necessary to use pydot_draw() " + "Pydot and Pillow are necessary to use graphviz_draw() " "it can be installed with 'pip install pydot pillow'" ) try: diff --git a/setup.py b/setup.py index 26c6df3b9f..13f007592e 100644 --- a/setup.py +++ b/setup.py @@ -54,6 +54,6 @@ def readme(): install_requires=['numpy>=1.16.0'], extras_require={ 'mpl': ['matplotlib>=3.0'], - 'pydot': ['pydot>=1.4', 'pillow>=5.4'], + 'graphviz': ['pydot>=1.4', 'pillow>=5.4'], } ) diff --git a/tests/visualization/test_pydot.py b/tests/visualization/test_graphviz.py similarity index 78% rename from tests/visualization/test_pydot.py rename to tests/visualization/test_graphviz.py index 5c5891e8f2..6dbc97819f 100644 --- a/tests/visualization/test_pydot.py +++ b/tests/visualization/test_graphviz.py @@ -15,7 +15,7 @@ import unittest import retworkx -from retworkx.visualization import pydot_draw +from retworkx.visualization import graphviz_draw try: import pydot @@ -37,12 +37,12 @@ def _save_image(image, path): @unittest.skipUnless( HAS_PYDOT, "pydot and graphviz are required for running these tests" ) -class TestPyDotDraw(unittest.TestCase): +class TestGraphvizDraw(unittest.TestCase): def test_draw_no_args(self): graph = retworkx.generators.star_graph(24) - image = pydot_draw(graph) + image = graphviz_draw(graph) self.assertIsInstance(image, PIL.Image.Image) - _save_image(image, "test_pydot_draw.png") + _save_image(image, "test_graphviz_draw.png") def test_draw_node_attr_fn(self): graph = retworkx.PyGraph() @@ -63,9 +63,9 @@ def test_draw_node_attr_fn(self): } ) graph.add_edge(0, 1, dict(label="1", name="1")) - image = pydot_draw(graph, lambda node: node) + image = graphviz_draw(graph, lambda node: node) self.assertIsInstance(image, PIL.Image.Image) - _save_image(image, "test_pydot_draw_node_attr.png") + _save_image(image, "test_graphviz_draw_node_attr.png") def test_draw_edge_attr_fn(self): graph = retworkx.PyGraph() @@ -86,9 +86,9 @@ def test_draw_edge_attr_fn(self): } ) graph.add_edge(0, 1, dict(label="1", name="1")) - image = pydot_draw(graph, lambda node: node, lambda edge: edge) + image = graphviz_draw(graph, lambda node: node, lambda edge: edge) self.assertIsInstance(image, PIL.Image.Image) - _save_image(image, "test_pydot_draw_edge_attr.png") + _save_image(image, "test_graphviz_draw_edge_attr.png") def test_draw_graph_attr(self): graph = retworkx.PyGraph() @@ -110,32 +110,32 @@ def test_draw_graph_attr(self): ) graph.add_edge(0, 1, dict(label="1", name="1")) graph_attr = {"bgcolor": "red"} - image = pydot_draw( + image = graphviz_draw( graph, lambda node: node, lambda edge: edge, graph_attr ) self.assertIsInstance(image, PIL.Image.Image) - _save_image(image, "test_pydot_draw_graph_attr.png") + _save_image(image, "test_graphviz_draw_graph_attr.png") def test_image_type(self): graph = retworkx.directed_gnp_random_graph(50, 0.8) - image = pydot_draw(graph, image_type="jpg") + image = graphviz_draw(graph, image_type="jpg") self.assertIsInstance(image, PIL.Image.Image) - _save_image(image, "test_pydot_draw_image_type.jpg") + _save_image(image, "test_graphviz_draw_image_type.jpg") def test_method(self): graph = retworkx.directed_gnp_random_graph(50, 0.8) - image = pydot_draw(graph, method="sfdp") + image = graphviz_draw(graph, method="sfdp") self.assertIsInstance(image, PIL.Image.Image) - _save_image(image, "test_pydot_method.png") + _save_image(image, "test_graphviz_method.png") def test_filename(self): graph = retworkx.generators.grid_graph(20, 20) - pydot_draw( + graphviz_draw( graph, - filename="test_pydot_filename.svg", + filename="test_graphviz_filename.svg", image_type="svg", method="neato", ) - self.assertTrue(os.path.isfile("test_pydot_filename.svg")) + self.assertTrue(os.path.isfile("test_graphviz_filename.svg")) if not SAVE_IMAGES: - self.addCleanup(os.remove, "test_pydot_filename.svg") + self.addCleanup(os.remove, "test_graphviz_filename.svg")