11
11
import numpy as np
12
12
from copy import deepcopy
13
13
14
- from . import circuit_latex as _latex
14
+ from .texrenderer import TeXRenderer , CONVERTERS
15
15
from ._decompose import _resolve_to_universal , _resolve_2q_basis
16
16
from ..operations import (
17
17
Gate ,
@@ -112,6 +112,12 @@ def __init__(
112
112
"{{str: gate_function}}, not {}" .format (user_gates )
113
113
)
114
114
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
+
115
121
def add_state (self , state , targets = None , state_type = "input" ):
116
122
"""
117
123
Add an input or ouput state to the circuit. By default all the input
@@ -930,137 +936,8 @@ def compute_unitary(self):
930
936
circuit_unitary = result .get_final_states ()[0 ]
931
937
return circuit_unitary
932
938
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
1062
940
1063
- # This slightly convoluted dance with the conversion formats is because
1064
941
# image conversion has optional dependencies. We always want the `png` and
1065
942
# `svg` methods to be available so that they are discoverable by the user,
1066
943
# however if one is called without the required dependency, then they'll
@@ -1069,14 +946,11 @@ def latex_code(self):
1069
946
# conversion is available, so the user doesn't get exceptions on display
1070
947
# because IPython tried to do something behind their back.
1071
948
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" )
1074
951
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" )
1080
954
1081
955
@property
1082
956
def png (self ):
@@ -1094,6 +968,7 @@ def svg(self):
1094
968
1095
969
def draw (
1096
970
self ,
971
+ renderer = "latex" ,
1097
972
file_type = "png" ,
1098
973
dpi = None ,
1099
974
file_name = "exported_pic" ,
@@ -1104,6 +979,9 @@ def draw(
1104
979
1105
980
Parameters
1106
981
----------
982
+ renderer : choose the renderer for the circuit.
983
+ Default: 'latex'
984
+
1107
985
file_type : Provide a supported image file_type eg: "svg"/"png".
1108
986
Default : "png".
1109
987
@@ -1119,17 +997,23 @@ def draw(
1119
997
Note : User should have write access to the location.
1120
998
"""
1121
999
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 )
1124
1013
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
+ )
1133
1017
1134
1018
def _to_qasm (self , qasm_out ):
1135
1019
"""
@@ -1154,20 +1038,3 @@ def _to_qasm(self, qasm_out):
1154
1038
1155
1039
for op in self .gates :
1156
1040
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