##############################################################################
# Copyright 2019 Rigetti Computing
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##############################################################################
import os
import shutil
import subprocess
import tempfile
from typing import Any, Optional
from IPython.display import Image
from pyquil.latex._diagram import DiagramSettings
from pyquil.latex._main import to_latex
from pyquil.quil import Program
[docs]
def display(circuit: Program, settings: Optional[DiagramSettings] = None, **image_options: Any) -> Image:
"""Display a PyQuil circuit as an IPython image object.
.. note::
For this to work, you need two external programs, ``pdflatex`` and ``convert``,
to be installed and accessible via your shell path.
Further, your LaTeX installation should include class and style files for ``standalone``,
``geometry``, ``tikz``, and ``quantikz``. If it does not, you need to install
these yourself.
:param circuit: The circuit to be drawn, represented as a pyquil program.
:param settings: An optional object of settings controlling diagram rendering and layout.
:return: PNG image render of the circuit.
"""
# The conversion process relies on two passes: first, 'pdflatex' is called to
# render the tikz to a pdf. Second, Imagemagick's 'convert' is called to translate
# this to a png image.
pdflatex_path = shutil.which("pdflatex")
convert_path = shutil.which("convert")
if pdflatex_path is None:
raise FileNotFoundError("Unable to locate 'pdflatex'.")
if convert_path is None:
raise FileNotFoundError("Unable to locate 'convert'.")
with tempfile.TemporaryDirectory() as tmpdir:
with open(os.path.join(tmpdir, "diagram.tex"), "w") as texfile:
texfile.write(to_latex(circuit, settings))
result = subprocess.run(
[pdflatex_path, "-halt-on-error", "-output-directory", tmpdir, texfile.name], # noqa: S603 - valid input
stdout=subprocess.PIPE,
)
if result.returncode != 0:
msg = f"'pdflatex' terminated with return code {result.returncode}."
# NOTE: pdflatex writes all error messages to stdout
if result.stdout:
msg += " Transcript:\n\n" + result.stdout.decode("utf-8")
raise RuntimeError(msg)
png = os.path.join(tmpdir, "diagram.png")
pdf = os.path.join(tmpdir, "diagram.pdf")
result = subprocess.run([convert_path, "-density", "300", pdf, png], stderr=subprocess.PIPE) # noqa: S603
if result.returncode != 0:
msg = f"'convert' terminated with return code {result.returncode}."
if result.stderr:
msg += " Transcript:\n\n" + result.stderr.decode("utf-8")
raise RuntimeError(msg)
return Image(filename=png, **image_options)