Skip to content

Commit 87a07ec

Browse files
authored
Merge pull request #241 from gadhvirushiraj/dev-rushi2
Shifting LaTeX Code and Draw API Refactor
2 parents 0366851 + 557094f commit 87a07ec

File tree

5 files changed

+481
-437
lines changed

5 files changed

+481
-437
lines changed

src/qutip_qip/circuit/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Circuit representation and simulation at the gate level."""
22

3+
import warnings
4+
35
from .circuit import *
4-
from .circuit_latex import *
56
from .circuitsimulator import *
67
from ..operations import Gate, Measurement
8+
from .texrenderer import *
79

810

911
def _add_deprecation(fun, msg):

src/qutip_qip/circuit/circuit.py

+32-165
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import numpy as np
1212
from copy import deepcopy
1313

14-
from . import circuit_latex as _latex
14+
from .texrenderer import TeXRenderer, CONVERTERS
1515
from ._decompose import _resolve_to_universal, _resolve_2q_basis
1616
from ..operations import (
1717
Gate,
@@ -112,6 +112,12 @@ def __init__(
112112
"{{str: gate_function}}, not {}".format(user_gates)
113113
)
114114

115+
if "png" in CONVERTERS:
116+
self._repr_png_ = self._generate_png
117+
118+
if "svg" in CONVERTERS:
119+
self._repr_svg_ = self._generate_svg
120+
115121
def add_state(self, state, targets=None, state_type="input"):
116122
"""
117123
Add an input or ouput state to the circuit. By default all the input
@@ -930,137 +936,8 @@ def compute_unitary(self):
930936
circuit_unitary = result.get_final_states()[0]
931937
return circuit_unitary
932938

933-
def latex_code(self):
934-
rows = []
935-
936-
ops = self.gates
937-
col = []
938-
for op in ops:
939-
if isinstance(op, Gate):
940-
gate = op
941-
col = []
942-
_swap_processing = False
943-
for n in range(self.N + self.num_cbits):
944-
if gate.targets and n in gate.targets:
945-
if len(gate.targets) > 1:
946-
if gate.name == "SWAP":
947-
if _swap_processing:
948-
col.append(r" \qswap \qw")
949-
continue
950-
distance = abs(
951-
gate.targets[1] - gate.targets[0]
952-
)
953-
if self.reverse_states:
954-
distance = -distance
955-
col.append(r" \qswap \qwx[%d] \qw" % distance)
956-
_swap_processing = True
957-
958-
elif (
959-
self.reverse_states and n == max(gate.targets)
960-
) or (
961-
not self.reverse_states
962-
and n == min(gate.targets)
963-
):
964-
col.append(
965-
r" \multigate{%d}{%s} "
966-
% (
967-
len(gate.targets) - 1,
968-
_gate_label(gate),
969-
)
970-
)
971-
else:
972-
col.append(
973-
r" \ghost{%s} " % (_gate_label(gate))
974-
)
975-
976-
elif gate.name == "CNOT":
977-
col.append(r" \targ ")
978-
elif gate.name == "CY":
979-
col.append(r" \targ ")
980-
elif gate.name == "CZ":
981-
col.append(r" \targ ")
982-
elif gate.name == "CS":
983-
col.append(r" \targ ")
984-
elif gate.name == "CT":
985-
col.append(r" \targ ")
986-
elif gate.name == "TOFFOLI":
987-
col.append(r" \targ ")
988-
else:
989-
col.append(r" \gate{%s} " % _gate_label(gate))
990-
991-
elif gate.controls and n in gate.controls:
992-
control_tag = (-1 if self.reverse_states else 1) * (
993-
gate.targets[0] - n
994-
)
995-
col.append(r" \ctrl{%d} " % control_tag)
996-
997-
elif (
998-
gate.classical_controls
999-
and (n - self.N) in gate.classical_controls
1000-
):
1001-
control_tag = (-1 if self.reverse_states else 1) * (
1002-
gate.targets[0] - n
1003-
)
1004-
col.append(r" \ctrl{%d} " % control_tag)
1005-
1006-
elif not gate.controls and not gate.targets:
1007-
# global gate
1008-
if (self.reverse_states and n == self.N - 1) or (
1009-
not self.reverse_states and n == 0
1010-
):
1011-
col.append(
1012-
r" \multigate{%d}{%s} "
1013-
% (
1014-
self.N - 1,
1015-
_gate_label(gate),
1016-
)
1017-
)
1018-
else:
1019-
col.append(r" \ghost{%s} " % (_gate_label(gate)))
1020-
else:
1021-
col.append(r" \qw ")
1022-
1023-
else:
1024-
measurement = op
1025-
col = []
1026-
for n in range(self.N + self.num_cbits):
1027-
if n in measurement.targets:
1028-
col.append(r" \meter")
1029-
elif (n - self.N) == measurement.classical_store:
1030-
sgn = 1 if self.reverse_states else -1
1031-
store_tag = sgn * (n - measurement.targets[0])
1032-
col.append(r" \qw \cwx[%d] " % store_tag)
1033-
else:
1034-
col.append(r" \qw ")
1035-
1036-
col.append(r" \qw ")
1037-
rows.append(col)
1038-
1039-
input_states_quantum = [
1040-
r"\lstick{\ket{" + x + "}}" if x is not None else ""
1041-
for x in self.input_states[: self.N]
1042-
]
1043-
input_states_classical = [
1044-
r"\lstick{" + x + "}" if x is not None else ""
1045-
for x in self.input_states[self.N :]
1046-
]
1047-
input_states = input_states_quantum + input_states_classical
1048-
1049-
code = ""
1050-
n_iter = (
1051-
reversed(range(self.N + self.num_cbits))
1052-
if self.reverse_states
1053-
else range(self.N + self.num_cbits)
1054-
)
1055-
for n in n_iter:
1056-
code += r" & %s" % input_states[n]
1057-
for m in range(len(ops)):
1058-
code += r" & %s" % rows[m][n]
1059-
code += r" & \qw \\ " + "\n"
1060-
1061-
return _latex_template % code
939+
# This slightly convoluted dance with the conversion formats is because
1062940

1063-
# This slightly convoluted dance with the conversion formats is because
1064941
# image conversion has optional dependencies. We always want the `png` and
1065942
# `svg` methods to be available so that they are discoverable by the user,
1066943
# however if one is called without the required dependency, then they'll
@@ -1069,14 +946,11 @@ def latex_code(self):
1069946
# conversion is available, so the user doesn't get exceptions on display
1070947
# because IPython tried to do something behind their back.
1071948

1072-
def _raw_img(self, file_type="png", dpi=100):
1073-
return _latex.image_from_latex(self.latex_code(), file_type, dpi)
949+
def _generate_png(self):
950+
return TeXRenderer(self).raw_img(file_type="png")
1074951

1075-
if "png" in _latex.CONVERTERS:
1076-
_repr_png_ = _raw_img
1077-
1078-
if "svg" in _latex.CONVERTERS:
1079-
_repr_svg_ = partialmethod(_raw_img, file_type="svg", dpi=None)
952+
def _generate_svg(self):
953+
return TeXRenderer(self).raw_img(file_type="svg")
1080954

1081955
@property
1082956
def png(self):
@@ -1094,6 +968,7 @@ def svg(self):
1094968

1095969
def draw(
1096970
self,
971+
renderer="latex",
1097972
file_type="png",
1098973
dpi=None,
1099974
file_name="exported_pic",
@@ -1104,6 +979,9 @@ def draw(
1104979
1105980
Parameters
1106981
----------
982+
renderer : choose the renderer for the circuit.
983+
Default: 'latex'
984+
1107985
file_type : Provide a supported image file_type eg: "svg"/"png".
1108986
Default : "png".
1109987
@@ -1119,17 +997,23 @@ def draw(
1119997
Note : User should have write access to the location.
1120998
"""
1121999

1122-
if file_type == "svg":
1123-
mode = "w"
1000+
if renderer == "latex":
1001+
if file_type == "svg":
1002+
mode = "w"
1003+
else:
1004+
mode = "wb"
1005+
if file_type == "png" and not dpi:
1006+
dpi = 100
1007+
latex = TeXRenderer(self)
1008+
image_data = latex.raw_img(file_type, dpi)
1009+
with open(
1010+
os.path.join(file_path, file_name + "." + file_type), mode
1011+
) as f:
1012+
f.write(image_data)
11241013
else:
1125-
mode = "wb"
1126-
if file_type == "png" and not dpi:
1127-
dpi = 100
1128-
image_data = self._raw_img(file_type, dpi)
1129-
with open(
1130-
os.path.join(file_path, file_name + "." + file_type), mode
1131-
) as f:
1132-
f.write(image_data)
1014+
raise ValueError(
1015+
f"Unknown renderer '{renderer}' not supported. Please choose from 'latex', 'matplotlib', 'text'."
1016+
)
11331017

11341018
def _to_qasm(self, qasm_out):
11351019
"""
@@ -1154,20 +1038,3 @@ def _to_qasm(self, qasm_out):
11541038

11551039
for op in self.gates:
11561040
op._to_qasm(qasm_out)
1157-
1158-
1159-
_latex_template = r"""
1160-
\documentclass[border=3pt]{standalone}
1161-
\usepackage[braket]{qcircuit}
1162-
\begin{document}
1163-
\Qcircuit @C=1cm @R=1cm {
1164-
%s}
1165-
\end{document}
1166-
"""
1167-
1168-
1169-
def _gate_label(gate):
1170-
gate_label = gate.latex_str
1171-
if gate.arg_label is not None:
1172-
return r"%s(%s)" % (gate_label, gate.arg_label)
1173-
return r"%s" % gate_label

0 commit comments

Comments
 (0)