def _latex_circuit_drawer(circuit, scale=0.7, filename=None, style=None, plot_barriers=True, reverse_bits=False, justify=None, idle_wires=True, with_layout=True): """Draw a quantum circuit based on latex (Qcircuit package) Requires version >=2.6.0 of the qcircuit LaTeX package. Args: circuit (QuantumCircuit): a quantum circuit scale (float): scaling factor filename (str): file path to save image to style (dict or str): dictionary of style or file name of style file reverse_bits (bool): When set to True reverse the bit order inside registers for the output visualization. plot_barriers (bool): Enable/disable drawing barriers in the output circuit. Defaults to True. justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how the circuit should be justified. idle_wires (bool): Include idle wires. Default is True. with_layout (bool): Include layout information, with labels on the physical layout. Default: True Returns: PIL.Image: an in-memory representation of the circuit diagram Raises: OSError: usually indicates that ```pdflatex``` or ```pdftocairo``` is missing. CalledProcessError: usually points errors during diagram creation. ImportError: if pillow is not installed """ tmpfilename = 'circuit' with tempfile.TemporaryDirectory() as tmpdirname: tmppath = os.path.join(tmpdirname, tmpfilename + '.tex') _generate_latex_source(circuit, filename=tmppath, scale=scale, style=style, plot_barriers=plot_barriers, reverse_bits=reverse_bits, justify=justify, idle_wires=idle_wires, with_layout=with_layout) try: subprocess.run([ "pdflatex", "-halt-on-error", "-output-directory={}".format(tmpdirname), "{}".format(tmpfilename + '.tex') ], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True) except OSError as ex: if ex.errno == errno.ENOENT: logger.warning('WARNING: Unable to compile latex. ' 'Is `pdflatex` installed? ' 'Skipping latex circuit drawing...') raise except subprocess.CalledProcessError as ex: with open('latex_error.log', 'wb') as error_file: error_file.write(ex.stdout) logger.warning('WARNING Unable to compile latex. ' 'The output from the pdflatex command can ' 'be found in latex_error.log') raise else: if not HAS_PIL: raise ImportError('The latex drawer needs pillow installed. ' 'Run "pip install pillow" before using the ' 'latex drawer.') try: base = os.path.join(tmpdirname, tmpfilename) subprocess.run([ "pdftocairo", "-singlefile", "-png", "-q", base + '.pdf', base ]) image = Image.open(base + '.png') image = utils._trim(image) os.remove(base + '.png') if filename: image.save(filename, 'PNG') except OSError as ex: if ex.errno == errno.ENOENT: logger.warning('WARNING: Unable to convert pdf to image. ' 'Is `poppler` installed? ' 'Skipping circuit drawing...') raise return image
def _latex_circuit_drawer( circuit, scale=0.7, style=None, filename=None, plot_barriers=True, reverse_bits=False, justify=None, idle_wires=True, with_layout=True, initial_state=False, cregbundle=False, ): """Draw a quantum circuit based on latex (Qcircuit package) Requires version >=2.6.0 of the qcircuit LaTeX package. Args: circuit (QuantumCircuit): a quantum circuit scale (float): scaling factor style (dict or str): dictionary of style or file name of style file filename (str): file path to save image to reverse_bits (bool): When set to True reverse the bit order inside registers for the output visualization. plot_barriers (bool): Enable/disable drawing barriers in the output circuit. Defaults to True. justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how the circuit should be justified. idle_wires (bool): Include idle wires. Default is True. with_layout (bool): Include layout information, with labels on the physical layout. Default: True initial_state (bool): Optional. Adds |0> in the beginning of the line. Default: `False`. cregbundle (bool): Optional. If set True, bundle classical registers. Default: ``False``. Returns: PIL.Image: an in-memory representation of the circuit diagram Raises: OSError: usually indicates that ```pdflatex``` or ```pdftocairo``` is missing. CalledProcessError: usually points to errors during diagram creation. MissingOptionalLibraryError: if pillow is not installed """ tmpfilename = "circuit" with tempfile.TemporaryDirectory() as tmpdirname: tmppath = os.path.join(tmpdirname, tmpfilename + ".tex") _generate_latex_source( circuit, filename=tmppath, scale=scale, style=style, plot_barriers=plot_barriers, reverse_bits=reverse_bits, justify=justify, idle_wires=idle_wires, with_layout=with_layout, initial_state=initial_state, cregbundle=cregbundle, ) try: subprocess.run( [ "pdflatex", "-halt-on-error", f"-output-directory={tmpdirname}", f"{tmpfilename + '.tex'}", ], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True, ) except OSError as ex: if ex.errno == errno.ENOENT: logger.warning( "WARNING: Unable to compile latex. " "Is `pdflatex` installed? " "Skipping latex circuit drawing..." ) raise except subprocess.CalledProcessError as ex: with open("latex_error.log", "wb") as error_file: error_file.write(ex.stdout) logger.warning( "WARNING Unable to compile latex. " "The output from the pdflatex command can " "be found in latex_error.log" ) raise else: if not HAS_PIL: raise MissingOptionalLibraryError( libname="pillow", name="latex drawer", pip_install="pip install pillow", ) try: base = os.path.join(tmpdirname, tmpfilename) subprocess.run( ["pdftocairo", "-singlefile", "-png", "-q", base + ".pdf", base], check=True ) image = Image.open(base + ".png") image = utils._trim(image) os.remove(base + ".png") if filename: if filename.endswith(".pdf"): os.rename(base + ".pdf", filename) else: image.save(filename, "PNG") except (OSError, subprocess.CalledProcessError) as ex: logger.warning( "WARNING: Unable to convert pdf to image. " "Is `poppler` installed? " "Skipping circuit drawing..." ) raise return image
def pass_manager_drawer(pass_manager, filename, style=None, raw=False): """ Draws the pass manager. This function needs `pydot <https://github.com/erocarrera/pydot>`, which in turn needs Graphviz <https://www.graphviz.org/>` to be installed. Args: pass_manager (PassManager): the pass manager to be drawn filename (str): file path to save image to style (dict or OrderedDict): keys are the pass classes and the values are the colors to make them. An example can be seen in the DEFAULT_STYLE. An ordered dict can be used to ensure a priority coloring when pass falls into multiple categories. Any values not included in the provided dict will be filled in from the default dict raw (Bool) : True if you want to save the raw Dot output not an image. The default is False. Returns: PIL.Image or None: an in-memory representation of the pass manager. Or None if no image was generated or PIL is not installed. Raises: ImportError: when nxpd or pydot not installed. VisualizationError: If raw=True and filename=None. Example: .. code-block:: %matplotlib inline from qiskit import QuantumCircuit from qiskit.compiler import transpile from qiskit.transpiler import PassManager from qiskit.visualization import pass_manager_drawer from qiskit.transpiler.passes import Unroller circ = QuantumCircuit(3) circ.ccx(0, 1, 2) circ.draw() pass_ = Unroller(['u1', 'u2', 'u3', 'cx']) pm = PassManager(pass_) new_circ = pm.run(circ) new_circ.draw(output='mpl') pass_manager_drawer(pm, "passmanager.jpg") """ try: import subprocess _PROC = subprocess.Popen(['dot', '-V'], # pylint: disable=invalid-name stdout=subprocess.PIPE, stderr=subprocess.PIPE) _PROC.communicate() if _PROC.returncode != 0: has_graphviz = False else: has_graphviz = True except Exception: # pylint: disable=broad-except # this is raised when the dot command cannot be found, which means GraphViz # isn't installed has_graphviz = False HAS_GRAPHVIZ = has_graphviz # pylint: disable=invalid-name try: import pydot if not HAS_GRAPHVIZ: raise ImportError except ImportError: raise ImportError("pass_manager_drawer requires pydot and graphviz. " "Run 'pip install pydot'. " "Graphviz can be installed using 'brew install graphviz' on Mac" " or by downloading it from the website.") passes = pass_manager.passes() if not style: style = DEFAULT_STYLE # create the overall graph graph = pydot.Dot() # identifiers for nodes need to be unique, so assign an id # can't just use python's id in case the exact same pass was # appended more than once component_id = 0 prev_node = None for index, controller_group in enumerate(passes): # label is the name of the flow controller parameter label = "[%s] %s" % (index, ', '.join(controller_group['flow_controllers'])) # create the subgraph for this controller subgraph = pydot.Cluster(str(component_id), label=label, fontname='helvetica', labeljust='l') component_id += 1 for pass_ in controller_group['passes']: # label is the name of the pass node = pydot.Node(str(component_id), label=str(type(pass_).__name__), color=_get_node_color(pass_, style), shape="rectangle", fontname='helvetica') subgraph.add_node(node) component_id += 1 # the arguments that were provided to the pass when it was created arg_spec = inspect.getfullargspec(pass_.__init__) # 0 is the args, 1: to remove the self arg args = arg_spec[0][1:] num_optional = len(arg_spec[3]) if arg_spec[3] else 0 # add in the inputs to the pass for arg_index, arg in enumerate(args): nd_style = 'solid' # any optional args are dashed # the num of optional counts from the end towards the start of the list if arg_index >= (len(args) - num_optional): nd_style = 'dashed' input_node = pydot.Node(component_id, label=arg, color="black", shape="ellipse", fontsize=10, style=nd_style, fontname='helvetica') subgraph.add_node(input_node) component_id += 1 subgraph.add_edge(pydot.Edge(input_node, node)) # if there is a previous node, add an edge between them if prev_node: subgraph.add_edge(pydot.Edge(prev_node, node)) prev_node = node graph.add_subgraph(subgraph) if raw: if filename: graph.write(filename, format='raw') return None else: raise VisualizationError("if format=raw, then a filename is required.") if not HAS_PIL and filename: # linter says this isn't a method - it is graph.write_png(filename) # pylint: disable=no-member return None with tempfile.TemporaryDirectory() as tmpdirname: tmppath = os.path.join(tmpdirname, 'pass_manager.png') # linter says this isn't a method - it is graph.write_png(tmppath) # pylint: disable=no-member image = Image.open(tmppath) image = utils._trim(image) os.remove(tmppath) if filename: image.save(filename, 'PNG') return image
def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): """ Draws the pass manager. This function needs `pydot <https://github.com/erocarrera/pydot>`, which in turn needs Graphviz <https://www.graphviz.org/>` to be installed. Args: pass_manager (PassManager): the pass manager to be drawn filename (str): file path to save image to style (dict or OrderedDict): keys are the pass classes and the values are the colors to make them. An example can be seen in the DEFAULT_STYLE. An ordered dict can be used to ensure a priority coloring when pass falls into multiple categories. Any values not included in the provided dict will be filled in from the default dict raw (Bool) : True if you want to save the raw Dot output not an image. The default is False. Returns: PIL.Image or None: an in-memory representation of the pass manager. Or None if no image was generated or PIL is not installed. Raises: MissingOptionalLibraryError: when nxpd or pydot not installed. VisualizationError: If raw=True and filename=None. Example: .. code-block:: %matplotlib inline from qiskit import QuantumCircuit from qiskit.compiler import transpile from qiskit.transpiler import PassManager from qiskit.visualization import pass_manager_drawer from qiskit.transpiler.passes import Unroller circ = QuantumCircuit(3) circ.ccx(0, 1, 2) circ.draw() pass_ = Unroller(['u1', 'u2', 'u3', 'cx']) pm = PassManager(pass_) new_circ = pm.run(circ) new_circ.draw(output='mpl') pass_manager_drawer(pm, "passmanager.jpg") """ import pydot passes = pass_manager.passes() if not style: style = DEFAULT_STYLE # create the overall graph graph = pydot.Dot() # identifiers for nodes need to be unique, so assign an id # can't just use python's id in case the exact same pass was # appended more than once component_id = 0 prev_node = None for index, controller_group in enumerate(passes): # label is the name of the flow controller parameter label = "[{}] {}".format( index, ", ".join(controller_group["flow_controllers"])) # create the subgraph for this controller subgraph = pydot.Cluster(str(component_id), label=label, fontname="helvetica", labeljust="l") component_id += 1 for pass_ in controller_group["passes"]: # label is the name of the pass node = pydot.Node( str(component_id), label=str(type(pass_).__name__), color=_get_node_color(pass_, style), shape="rectangle", fontname="helvetica", ) subgraph.add_node(node) component_id += 1 # the arguments that were provided to the pass when it was created arg_spec = inspect.getfullargspec(pass_.__init__) # 0 is the args, 1: to remove the self arg args = arg_spec[0][1:] num_optional = len(arg_spec[3]) if arg_spec[3] else 0 # add in the inputs to the pass for arg_index, arg in enumerate(args): nd_style = "solid" # any optional args are dashed # the num of optional counts from the end towards the start of the list if arg_index >= (len(args) - num_optional): nd_style = "dashed" input_node = pydot.Node( component_id, label=arg, color="black", shape="ellipse", fontsize=10, style=nd_style, fontname="helvetica", ) subgraph.add_node(input_node) component_id += 1 subgraph.add_edge(pydot.Edge(input_node, node)) # if there is a previous node, add an edge between them if prev_node: subgraph.add_edge(pydot.Edge(prev_node, node)) prev_node = node graph.add_subgraph(subgraph) if raw: if filename: graph.write(filename, format="raw") return None else: raise VisualizationError( "if format=raw, then a filename is required.") if not _optionals.HAS_PIL and filename: # pylint says this isn't a method - it is graph.write_png(filename) # pylint: disable=no-member return None _optionals.HAS_PIL.require_now("pass manager drawer") with tempfile.TemporaryDirectory() as tmpdirname: from PIL import Image tmppath = os.path.join(tmpdirname, "pass_manager.png") # pylint says this isn't a method - it is graph.write_png(tmppath) # pylint: disable=no-member image = Image.open(tmppath) image = utils._trim(image) os.remove(tmppath) if filename: image.save(filename, "PNG") return image
def _latex_circuit_drawer( circuit, scale=0.7, style=None, filename=None, plot_barriers=True, reverse_bits=False, justify=None, idle_wires=True, with_layout=True, initial_state=False, cregbundle=False, ): """Draw a quantum circuit based on latex (Qcircuit package) Requires version >=2.6.0 of the qcircuit LaTeX package. Args: circuit (QuantumCircuit): a quantum circuit scale (float): scaling factor style (dict or str): dictionary of style or file name of style file filename (str): file path to save image to reverse_bits (bool): When set to True reverse the bit order inside registers for the output visualization. plot_barriers (bool): Enable/disable drawing barriers in the output circuit. Defaults to True. justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how the circuit should be justified. idle_wires (bool): Include idle wires. Default is True. with_layout (bool): Include layout information, with labels on the physical layout. Default: True initial_state (bool): Optional. Adds |0> in the beginning of the line. Default: `False`. cregbundle (bool): Optional. If set True, bundle classical registers. Default: ``False``. Returns: PIL.Image: an in-memory representation of the circuit diagram Raises: MissingOptionalLibraryError: if pillow, pdflatex, or poppler are not installed VisualizationError: if one of the conversion utilities failed for some internal or file-access reason. """ tmpfilename = "circuit" with tempfile.TemporaryDirectory() as tmpdirname: tmppath = os.path.join(tmpdirname, tmpfilename + ".tex") _generate_latex_source( circuit, filename=tmppath, scale=scale, style=style, plot_barriers=plot_barriers, reverse_bits=reverse_bits, justify=justify, idle_wires=idle_wires, with_layout=with_layout, initial_state=initial_state, cregbundle=cregbundle, ) if not HAS_PDFLATEX: raise MissingOptionalLibraryError( libname="pdflatex", name="LaTeX circuit drawing", msg= "You will likely need to install a full LaTeX distribution for your system", ) if not HAS_PDFTOCAIRO: raise MissingOptionalLibraryError( libname="pdftocairo", name="LaTeX circuit drawing", msg="This is part of the 'poppler' set of PDF utilities", ) if not HAS_PIL: raise MissingOptionalLibraryError( libname="pillow", name="LaTeX circuit drawing", pip_install="pip install pillow", ) try: subprocess.run( [ "pdflatex", "-halt-on-error", f"-output-directory={tmpdirname}", f"{tmpfilename + '.tex'}", ], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True, ) except OSError as exc: # OSError should generally not occur, because it's usually only triggered if `pdflatex` # doesn't exist as a command, but we've already checked that. raise VisualizationError( "`pdflatex` command could not be run.") from exc except subprocess.CalledProcessError as exc: with open("latex_error.log", "wb") as error_file: error_file.write(exc.stdout) logger.warning( "Unable to compile LaTeX. Perhaps you are missing the `qcircuit` package." " The output from the `pdflatex` command is in `latex_error.log`." ) raise VisualizationError( "`pdflatex` call did not succeed: see `latex_error.log`." ) from exc base = os.path.join(tmpdirname, tmpfilename) try: subprocess.run( [ "pdftocairo", "-singlefile", "-png", "-q", base + ".pdf", base ], check=True, ) except (OSError, subprocess.CalledProcessError) as exc: message = "`pdftocairo` failed to produce an image." logger.warning(message) raise VisualizationError(message) from exc image = Image.open(base + ".png") image = utils._trim(image) if filename: if filename.endswith(".pdf"): os.rename(base + ".pdf", filename) else: try: image.save(filename) except (ValueError, OSError) as exc: raise VisualizationError( f"Pillow could not write the image file '{filename}'." ) from exc return image
def pass_manager_drawer(pass_manager, filename, style=None, raw=False): """ Draws the pass manager. This function needs `pydot <https://github.com/erocarrera/pydot>`, which in turn needs Graphviz <https://www.graphviz.org/>` to be installed. Args: pass_manager (PassManager): the pass manager to be drawn filename (str): file path to save image to style (dict or OrderedDict): keys are the pass classes and the values are the colors to make them. An example can be seen in the DEFAULT_STYLE. An ordered dict can be used to ensure a priority coloring when pass falls into multiple categories. Any values not included in the provided dict will be filled in from the default dict raw (Bool) : True if you want to save the raw Dot output not an image. The default is False. Returns: PIL.Image or None: an in-memory representation of the pass manager. Or None if no image was generated or PIL is not installed. Raises: ImportError: when nxpd or pydot not installed. """ try: import pydot if not HAS_GRAPHVIZ: raise ImportError except ImportError: raise ImportError( "pass_manager_drawer requires pydot and graphviz. " "Run 'pip install pydot'. " "Graphviz can be installed using 'brew install graphviz' on Mac" " or by downloading it from the website.") passes = pass_manager.passes() if not style: style = DEFAULT_STYLE # create the overall graph graph = pydot.Dot() # identifiers for nodes need to be unique, so assign an id # can't just use python's id in case the exact same pass was # appended more than once component_id = 0 prev_node = None for controller_group in passes: # label is the name of the flow controller (without the word controller) label = controller_group['type'].__name__.replace('Controller', '') # create the subgraph for this controller subgraph = pydot.Cluster(str(component_id), label=label, fontname='helvetica') component_id += 1 for pass_ in controller_group['passes']: # label is the name of the pass node = pydot.Node(str(component_id), label=str(type(pass_).__name__), color=_get_node_color(pass_, style), shape="rectangle", fontname='helvetica') subgraph.add_node(node) component_id += 1 # the arguments that were provided to the pass when it was created arg_spec = inspect.getfullargspec(pass_.__init__) # 0 is the args, 1: to remove the self arg args = arg_spec[0][1:] num_optional = len(arg_spec[3]) if arg_spec[3] else 0 # add in the inputs to the pass for arg_index, arg in enumerate(args): nd_style = 'solid' # any optional args are dashed # the num of optional counts from the end towards the start of the list if arg_index >= (len(args) - num_optional): nd_style = 'dashed' input_node = pydot.Node(component_id, label=arg, color="black", shape="ellipse", fontsize=10, style=nd_style, fontname='helvetica') subgraph.add_node(input_node) component_id += 1 subgraph.add_edge(pydot.Edge(input_node, node)) # if there is a previous node, add an edge between them if prev_node: subgraph.add_edge(pydot.Edge(prev_node, node)) prev_node = node graph.add_subgraph(subgraph) if raw and filename: graph.write(filename, format='raw') if not HAS_PIL and filename: # linter says this isn't a method - it is graph.write_png(filename) # pylint: disable=no-member return None with tempfile.TemporaryDirectory() as tmpdirname: tmppath = os.path.join(tmpdirname, 'pass_manager.png') # linter says this isn't a method - it is graph.write_png(tmppath) # pylint: disable=no-member image = Image.open(tmppath) image = utils._trim(image) os.remove(tmppath) if filename: image.save(filename, 'PNG') return image