1
1
import numpy as np
2
2
import matplotlib .pyplot as plt
3
3
from matplotlib .colors import hsv_to_rgb
4
+ from matplotlib .patches import Circle , Rectangle
4
5
from .gates import Gates
5
6
6
7
@@ -15,9 +16,10 @@ def __init__(self, num_qubits: int):
15
16
# Statevector of length 2^n, start in |0...0>
16
17
self .state = np .zeros (2 ** num_qubits , dtype = complex )
17
18
self .state [0 ] = 1.0
19
+ self ._circuit = []
18
20
19
21
def __sizeof__ (self ):
20
- return self .state .nbytes + 8 # 8 bytes for the int n
22
+ return self .state .nbytes + sum ( op . __sizeof__ () for op in self . _circuit ) + 8
21
23
22
24
def reset (self ):
23
25
self .state = np .zeros (2 ** self .n , dtype = complex )
@@ -43,46 +45,59 @@ def _apply_gate(self, U: np.ndarray, qubits: list):
43
45
# Single-qubit gates
44
46
def x (self , q : int ):
45
47
self ._apply_gate (Gates .X , [q ])
48
+ self ._circuit .append (("X" , [q ]))
46
49
47
50
def y (self , q : int ):
48
51
self ._apply_gate (Gates .Y , [q ])
52
+ self ._circuit .append (("Y" , [q ]))
49
53
50
54
def z (self , q : int ):
51
55
self ._apply_gate (Gates .Z , [q ])
56
+ self ._circuit .append (("Z" , [q ]))
52
57
53
58
def h (self , q : int ):
54
59
self ._apply_gate (Gates .H , [q ])
60
+ self ._circuit .append (("H" , [q ]))
55
61
56
62
def s (self , q : int ):
57
63
self ._apply_gate (Gates .S , [q ])
64
+ self ._circuit .append (("S" , [q ]))
58
65
59
66
def t (self , q : int ):
60
67
self ._apply_gate (Gates .T , [q ])
68
+ self ._circuit .append (("T" , [q ]))
61
69
62
70
def u (self , theta : float , phi : float , lam : float , q : int ):
63
71
self ._apply_gate (Gates .U (theta , phi , lam ), [q ])
72
+ self ._circuit .append (("U" , [q ], (theta , phi , lam )))
64
73
65
74
# Two-qubit gates
66
75
def cx (self , control : int , target : int ):
67
76
self ._apply_gate (Gates .controlled_gate (Gates .X ), [control , target ])
77
+ self ._circuit .append (("CX" , [control , target ]))
68
78
69
79
def cu (self , theta : float , phi : float , lam : float , control : int , target : int ):
70
80
self ._apply_gate (
71
81
Gates .controlled_gate (Gates .U (theta , phi , lam )), [control , target ]
72
82
)
83
+ self ._circuit .append (("CU" , [control , target ], (theta , phi , lam )))
73
84
74
85
def swap (self , q1 : int , q2 : int ):
75
86
self ._apply_gate (Gates .SWAP_matrix (), [q1 , q2 ])
87
+ self ._circuit .append (("SWAP" , [q1 , q2 ]))
76
88
77
89
def iswap (self , q1 : int , q2 : int ):
78
90
self ._apply_gate (Gates .iSWAP_matrix (), [q1 , q2 ])
91
+ self ._circuit .append (("iSWAP" , [q1 , q2 ]))
79
92
80
93
# Three-qubit gates
81
94
def toffoli (self , c1 : int , c2 : int , t : int ):
82
95
self ._apply_gate (Gates .Toffoli_matrix (), [c1 , c2 , t ])
96
+ self ._circuit .append (("TOFFOLI" , [c1 , c2 , t ]))
83
97
84
98
def fredkin (self , c : int , t1 : int , t2 : int ):
85
99
self ._apply_gate (Gates .Fredkin_matrix (), [c , t1 , t2 ])
100
+ self ._circuit .append (("FREDKIN" , [c , t1 , t2 ]))
86
101
87
102
# Simulation & measurement
88
103
def run (self , shots : int = 100 ) -> dict [str , int ]:
@@ -114,3 +129,77 @@ def plot_state(self):
114
129
cb = plt .colorbar (plt .cm .ScalarMappable (cmap = "hsv" ), ax = ax )
115
130
cb .set_label ("Phase (radians mod 2π)" )
116
131
plt .show ()
132
+
133
+ def draw (self , ax : plt .Axes = None , figsize : tuple [int , int ] = None ):
134
+ if ax is None :
135
+ if not figsize :
136
+ figsize = (max (8 , len (self ._circuit )), self .n + 1 )
137
+ fig , ax = plt .subplots (figsize = figsize )
138
+ for q in range (self .n ):
139
+ ax .hlines (q , - 0.5 , len (self ._circuit ) - 0.5 , color = "k" )
140
+ cC = lambda x , y : ax .add_patch (Circle ((x , y ), 0.08 , fc = "k" , zorder = 3 ))
141
+ xT = lambda x , y : (
142
+ ax .add_patch (Circle ((x , y ), 0.18 , fc = "w" , ec = "k" , zorder = 3 )),
143
+ ax .plot (
144
+ [x - 0.1 , x + 0.1 ],
145
+ [y - 0.1 , y + 0.1 ],
146
+ "k" ,
147
+ [x - 0.1 , x + 0.1 ],
148
+ [y + 0.1 , y - 0.1 ],
149
+ "k" ,
150
+ zorder = 4 ,
151
+ ),
152
+ )
153
+ box = lambda x , y , t : (
154
+ ax .add_patch (
155
+ Rectangle (
156
+ (x - 0.3 , y - 0.3 ), 0.6 , 0.6 , fc = "lightblue" , ec = "k" , zorder = 3
157
+ )
158
+ ),
159
+ ax .text (x , y , t , ha = "center" , va = "center" , zorder = 4 ),
160
+ )
161
+ for i , (g , qs , * pars ) in enumerate (self ._circuit ):
162
+ if g in "XYZHST" :
163
+ box (i , qs [0 ], g )
164
+ elif g == "U" :
165
+ box (
166
+ i , qs [0 ], f"U\n ({ pars [0 ][0 ]:.2g} ,{ pars [0 ][1 ]:.2g} ,{ pars [0 ][2 ]:.2g} )"
167
+ )
168
+ elif g in ("CX" , "CU" ):
169
+ ax .vlines (i , * sorted (qs ), color = "k" )
170
+ cC (i , qs [0 ])
171
+ if g == "CX" :
172
+ xT (i , qs [1 ])
173
+ else :
174
+ box (
175
+ i ,
176
+ qs [1 ],
177
+ f"U\n ({ pars [0 ][0 ]:.2g} ,{ pars [0 ][1 ]:.2g} ,{ pars [0 ][2 ]:.2g} )" ,
178
+ )
179
+ elif g in ("SWAP" , "iSWAP" ):
180
+ ax .vlines (i , * sorted (qs ), color = "k" )
181
+ xT (i , qs [0 ])
182
+ xT (i , qs [1 ])
183
+ if g == "iSWAP" :
184
+ ax .text (i , sum (qs ) / 2 , "i" , ha = "center" , va = "center" , zorder = 4 )
185
+ elif g == "TOFFOLI" :
186
+ ax .vlines (i , min (qs ), max (qs ), color = "k" )
187
+ cC (i , qs [0 ])
188
+ cC (i , qs [1 ])
189
+ xT (i , qs [2 ])
190
+ elif g == "FREDKIN" :
191
+ ax .vlines (i , min (qs ), max (qs ), color = "k" )
192
+ cC (i , qs [0 ])
193
+ xT (i , qs [1 ])
194
+ xT (i , qs [2 ])
195
+ else :
196
+ box (i , qs [0 ], g )
197
+ for q in range (self .n ):
198
+ ax .text (- 1 , q , f"q{ q } " , ha = "right" , va = "center" )
199
+ ax .set_xlim (- 1 , len (self ._circuit ))
200
+ ax .set_ylim (- 0.5 , self .n - 0.5 )
201
+ ax .invert_yaxis ()
202
+ ax .axis ("off" )
203
+ ax .set_title ("Circuit Diagram" )
204
+ plt .tight_layout ()
205
+ plt .show ()
0 commit comments