Skip to content

Commit

Permalink
Export circuit png with dpi (#139)
Browse files Browse the repository at this point in the history
New method QubitCircuit.draw() to export svg and png images. Density in dpi can be an argument from the user for conversion to png (defaults to 100dpi).
  • Loading branch information
srinathkp committed Apr 30, 2022
1 parent bb7d15c commit 6dc4415
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 15 deletions.
60 changes: 49 additions & 11 deletions src/qutip_qip/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import numbers
import warnings
import inspect
import os
from functools import partialmethod

import numpy as np
from copy import deepcopy
Expand Down Expand Up @@ -2053,31 +2055,67 @@ def latex_code(self):
# conversion is available, so the user doesn't get exceptions on display
# because IPython tried to do something behind their back.

def _raw_png(self):
return _latex.image_from_latex(self.latex_code(), "png")
def _raw_img(self, file_type="png", dpi=100):
return _latex.image_from_latex(self.latex_code(), file_type, dpi)

if "png" in _latex.CONVERTERS:
_repr_png_ = _raw_png
_repr_png_ = _raw_img

if "svg" in _latex.CONVERTERS:
_repr_svg_ = partialmethod(_raw_img, file_type="svg", dpi=None)

@property
def png(self):
"""
Return the png file
"""
return DisplayImage(self._raw_png(), embed=True)

def _raw_svg(self):
return _latex.image_from_latex(self.latex_code(), "svg")

if "svg" in _latex.CONVERTERS:
_repr_svg_ = _raw_svg
return DisplayImage(self._repr_png_(), embed=True)

@property
def svg(self):
"""
Return the svg file
"""
return DisplaySVG(self._raw_svg())
return DisplaySVG(self._repr_svg_())

def draw(
self,
file_type="png",
dpi=None,
file_name="exported_pic",
file_path="",
):
"""
Export circuit object as an image file in a supported format.
Parameters
----------
file_type : Provide a supported image file_type eg: "svg"/"png".
Default : "png".
dpi : Image density in Dots per inch(dpi)
Applicable for PNG, NA for SVG.
Default : None, though it's set to 100 internally for PNG
file_name : Filename of the exported image.
Default : "exported_pic"
file_path : Path to which the file has to be exported.
Default : ""
Note : User should have write access to the location.
"""

if file_type == "svg":
mode = "w"
else:
mode = "wb"
if file_type == "png" and not dpi:
dpi = 100
image_data = self._raw_img(file_type, dpi)
with open(
os.path.join(file_path, file_name + "." + file_type), mode
) as f:
f.write(image_data)

def _to_qasm(self, qasm_out):
"""
Expand Down
15 changes: 11 additions & 4 deletions src/qutip_qip/circuit_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def _make_converter(configuration):
return None
mode = "rb" if configuration.binary else "r"

def converter(file_stem):
def converter(file_stem, dpi=100):
"""
Convert a file located in the current directory named `<file_stem>.pdf`
to an image format with the name `<file_stem>.xxx`, where `xxx` is
Expand All @@ -149,10 +149,17 @@ def converter(file_stem):
----------
file_stem : str
The basename of the PDF file to be converted.
dpi : int/float
Image density in dots per inch. Ignored for SVG.
"""
in_file = file_stem + ".pdf"
out_file = file_stem + "." + configuration.file_type
_run_command((which, *configuration.arguments, in_file, out_file))
if "-density" in configuration.arguments:
arguments = list(configuration.arguments)
arguments[arguments.index("-density") + 1] = str(dpi)
else:
arguments = configuration.arguments
_run_command((which, *arguments, in_file, out_file))
with open(out_file, mode) as file:
return file.read()

Expand All @@ -172,7 +179,7 @@ def converter(file_stem):

if _pdflatex is not None:

def image_from_latex(code, file_type="png"):
def image_from_latex(code, file_type="png", dpi=100):
"""
Convert the LaTeX `code` into an image format, defined by the
`file_type`. Returns a string or bytes object, depending on whether
Expand Down Expand Up @@ -240,7 +247,7 @@ def image_from_latex(code, file_type="png"):
raise ValueError(
"".join(["Unknown output format: '", file_type, "'."])
)
out = CONVERTERS[file_type](filename)
out = CONVERTERS[file_type](filename, dpi)
finally:
# Leave the temporary directory before it is removed (necessary
# on Windows, but it doesn't hurt on POSIX).
Expand Down
31 changes: 31 additions & 0 deletions tests/test_circuit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pytest
import os
import shutil
import numpy as np
from pathlib import Path

Expand Down Expand Up @@ -702,3 +704,32 @@ def test_latex_code_non_reversed(self):
exp = ' & & \\meter & \\qw \\\\ \n & ' + \
'& \\qw \\cwx[-1] & \\qw \\\\ \n'
assert qc.latex_code() == self._latex_template % exp

@pytest.mark.skipif(
shutil.which("pdflatex") is None,
reason="requires pdflatex"
)
def test_export_image(self):

def cleanup(*img_files):
for img_file in img_files:
if img_file in os.listdir('.'):
os.remove(img_file)

file_png_200 = "exported_pic_200.png"
file_png_400 = "exported_pic_400.png"
file_svg = "exported_pic.svg"
cleanup(file_png_200, file_png_400, file_svg)

qc = QubitCircuit(2, reverse_states=False)
qc.add_gate("CSIGN", controls=[0], targets=[1])
qc.draw("png", 200, file_png_200.split('.')[0], "")
qc.draw("png", 400.5, file_png_400.split('.')[0], "")
qc.draw("svg", 450, file_svg.split('.')[0], "")

assert file_png_200 in os.listdir('.')
assert file_png_400 in os.listdir('.')
assert os.stat(file_png_200).st_size < os.stat(file_png_400).st_size
assert file_svg in os.listdir('.')

cleanup(file_png_200, file_png_400, file_svg)

0 comments on commit 6dc4415

Please sign in to comment.