diff --git a/docs/conf.py b/docs/conf.py index cde110ef266e..dfcdee9c6281 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,6 +32,7 @@ "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", "jupyter_sphinx", "sphinx_autodoc_typehints", "reno.sphinxext", @@ -59,6 +60,9 @@ # (e.g., if this is set to ['foo.'], then foo.bar is shown under B, not F). modindex_common_prefix = ["qiskit."] +intersphinx_mapping = { + 'retworkx': ('https://qiskit.org/documentation/retworkx/', None), +} # -- Options for HTML output ------------------------------------------------- diff --git a/qiskit/visualization/dag_visualization.py b/qiskit/visualization/dag_visualization.py index 327f9f172dec..c016e8b292dc 100644 --- a/qiskit/visualization/dag_visualization.py +++ b/qiskit/visualization/dag_visualization.py @@ -15,10 +15,7 @@ """ Visualization function for DAG circuit representation. """ - -import os -import sys -import tempfile +from retworkx.visualization import graphviz_draw from qiskit.dagcircuit.dagnode import DAGOpNode, DAGInNode, DAGOutNode from qiskit.circuit import Qubit @@ -26,78 +23,14 @@ from qiskit.exceptions import InvalidFileError from .exceptions import VisualizationError -FILENAME_EXTENSIONS = { - "bmp", - "canon", - "cgimage", - "cmap", - "cmapx", - "cmapx_np", - "dot", - "dot_json", - "eps", - "exr", - "fig", - "gd", - "gd2", - "gif", - "gv", - "icns", - "ico", - "imap", - "imap_np", - "ismap", - "jp2", - "jpe", - "jpeg", - "jpg", - "json", - "json0", - "mp", - "pct", - "pdf", - "pic", - "pict", - "plain", - "plain-ext", - "png", - "pov", - "ps", - "ps2", - "psd", - "sgi", - "svg", - "svgz", - "tga", - "tif", - "tiff", - "tk", - "vdx", - "vml", - "vmlz", - "vrml", - "wbmp", - "webp", - "xdot", - "xdot1.2", - "xdot1.4", - "xdot_json", -} - -@_optionals.HAS_PYDOT.require_in_call +@_optionals.HAS_GRAPHVIZ.require_in_call def dag_drawer(dag, scale=0.7, filename=None, style="color"): """Plot the directed acyclic graph (dag) to represent operation dependencies in a quantum circuit. - Note this function leverages - `pydot `_ to generate the graph, which - means that having `Graphviz `_ installed on your - system is required for this to work. - - The current release of Graphviz can be downloaded here: . - Download the version of the software that matches your environment and follow the instructions - to install Graph Visualization Software (Graphviz) on your operating system. + This function calls the :func:`~retworkx.visualization.graphviz_draw` function from the ``retworkx`` + package to draw the DAG. Args: dag (DAGCircuit): The dag to draw. @@ -112,7 +45,6 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"): Raises: VisualizationError: when style is not recognized. - MissingOptionalLibraryError: when pydot or pillow are not installed. InvalidFileError: when filename provided is not valid Example: @@ -135,7 +67,6 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"): dag = circuit_to_dag(circ) dag_drawer(dag) """ - import pydot # NOTE: use type str checking to avoid potential cyclical import # the two tradeoffs ere that it will not handle subclasses and it is @@ -225,38 +156,16 @@ def edge_attr_func(edge): e["label"] = label return e - dot_str = dag._multi_graph.to_dot(node_attr_func, edge_attr_func, graph_attrs) - dot = pydot.graph_from_dot_data(dot_str)[0] - + image_type = None if filename: if "." not in filename: raise InvalidFileError("Parameter 'filename' must be in format 'name.extension'") - extension = filename.split(".")[-1] - if extension not in FILENAME_EXTENSIONS: - raise InvalidFileError( - "Filename extension must be one of: " + " ".join(FILENAME_EXTENSIONS) - ) - dot.write(filename, format=extension) - return None - elif ("ipykernel" in sys.modules) and ("spyder" not in sys.modules): - _optionals.HAS_PIL.require_now("dag_drawer") - from PIL import Image - - with tempfile.TemporaryDirectory() as tmpdirname: - tmp_path = os.path.join(tmpdirname, "dag.png") - dot.write_png(tmp_path) - with Image.open(tmp_path) as test_image: - image = test_image.copy() - os.remove(tmp_path) - return image - else: - _optionals.HAS_PIL.require_now("dag_drawer") - from PIL import Image - - with tempfile.TemporaryDirectory() as tmpdirname: - tmp_path = os.path.join(tmpdirname, "dag.png") - dot.write_png(tmp_path) - image = Image.open(tmp_path) - image.show() - os.remove(tmp_path) - return None + image_type = filename.split(".")[-1] + return graphviz_draw( + dag._multi_graph, + node_attr_func, + edge_attr_func, + graph_attrs, + filename, + image_type, + ) diff --git a/test/python/visualization/test_dag_drawer.py b/test/python/visualization/test_dag_drawer.py index eea4f94fb38f..462492f71f1c 100644 --- a/test/python/visualization/test_dag_drawer.py +++ b/test/python/visualization/test_dag_drawer.py @@ -38,10 +38,12 @@ def setUp(self): circuit.cx(qr[0], qr[1]) self.dag = circuit_to_dag(circuit) + @unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed") def test_dag_drawer_invalid_style(self): """Test dag draw with invalid style.""" self.assertRaises(VisualizationError, dag_drawer, self.dag, style="multicolor") + @unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed") def test_dag_drawer_checks_filename_correct_format(self): """filename must contain name and extension""" with self.assertRaisesRegex( @@ -49,9 +51,14 @@ def test_dag_drawer_checks_filename_correct_format(self): ): dag_drawer(self.dag, filename="aaabc") + @unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed") def test_dag_drawer_checks_filename_extension(self): """filename must have a valid extension""" - with self.assertRaisesRegex(InvalidFileError, "Filename extension must be one of: .*"): + with self.assertRaisesRegex( + ValueError, + "The specified value for the image_type argument, 'abc' is not a " + "valid choice. It must be one of: .*", + ): dag_drawer(self.dag, filename="aa.abc") @unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed")