Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_Circuit.py
Go to the documentation of this file.
1 
3 """
4 Created on Tue Jun 30 15:44:26 2020
5 Copyright 2020 Peter Rakyta, Ph.D.
6 
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10 
11  http://www.apache.org/licenses/LICENSE-2.0
12 
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see http://www.gnu.org/licenses/.
21 
22 @author: Peter Rakyta, Ph.D.
23 """
24 
25 
27 
28 
29 import numpy as np
30 from os import path
31 from squander.gates.qgd_Circuit_Wrapper import qgd_Circuit_Wrapper
32 
33 from squander.gates.qgd_CROT import qgd_CROT
34 
35 
36 from squander.gates.gates_Wrapper import (
37  U1,
38  U2,
39  U3,
40  H,
41  X,
42  Y,
43  Z,
44  T,
45  Tdg,
46  CH,
47  CNOT,
48  CZ,
49  R,
50  RX,
51  RY,
52  RZ,
53  SX,
54  SYC,
55  CRY )
56 
57 
58 
60 class qgd_Circuit(qgd_Circuit_Wrapper):
61 
62 
63 
67 
68  def __init__( self, qbit_num ):
69 
70  # call the constructor of the wrapper class
71  super().__init__( qbit_num )
72 
73  """
74  def __getstate__(self):
75  # Return a dictionary of the object's state
76 
77  return super().__getstate__()
78 
79  def __setstate__(self, state):
80 
81  print( state )
82  super().__setstate__( state )
83 
84 
85  def __new__(cls, *args, **kwargs):
86 
87  print( "NEEEEEEEEEEEEEW", args)
88  return super().__new__(cls, *args, **kwargs)
89  """
90 
91 #@brief Call to add a U1 gate to the front of the gate structure.
92 #@param self A pointer pointing to an instance of the class qgd_Circuit.
93 #@param Input argument: target_qbit (int)
94 
95  def add_U1( self, target_qbit):
96 
97  # call the C wrapper function
98  super().add_U1(target_qbit)
99 
100 #@brief Call to add a U2 gate to the front of the gate structure.
101 #@param self A pointer pointing to an instance of the class qgd_Circuit.
102 #@param Input argument: target_qbit (int)
103 
104  def add_U2( self, target_qbit):
105 
106  # call the C wrapper function
107  super().add_U2(target_qbit)
108 
109 #@brief Call to add a U3 gate to the front of the gate structure.
110 #@param self A pointer pointing to an instance of the class qgd_Circuit.
111 #@param Input argument: target_qbit (int)
112 
113  def add_U3( self, target_qbit):
114 
115  # call the C wrapper function
116  super().add_U3(target_qbit)
117 
118 #@brief Call to add a RX gate to the front of the gate structure.
119 #@param self A pointer pointing to an instance of the class qgd_Circuit.
120 #@param Input arguments: target_qbit (int).
121 
122  def add_RX( self, target_qbit):
123 
124  # call the C wrapper function
125  super().add_RX(target_qbit)
126 
127 #@brief Call to add a R gate to the front of the gate structure.
128 #@param self A pointer pointing to an instance of the class qgd_Circuit.
129 #@param Input arguments: target_qbit (int).
130  def add_R( self, target_qbit):
131 
132  # call the C wrapper function
133  super().add_R(target_qbit)
134 
135 #@brief Call to add a RY gate to the front of the gate structure.
136 #@param self A pointer pointing to an instance of the class qgd_Circuit.
137 #@param Input arguments: target_qbit (int).
138 
139  def add_RY( self, target_qbit):
140 
141  # call the C wrapper function
142  super().add_RY(target_qbit)
143 
144 #@brief Call to add a RZ gate to the front of the gate structure.
145 #@param self A pointer pointing to an instance of the class qgd_Circuit.
146 #@param Input arguments: target_qbit (int).
147 
148  def add_RZ( self, target_qbit):
149 
150  # call the C wrapper function
151  super().add_RZ(target_qbit)
152 
153 #@brief Call to add a CNOT gate to the front of the gate structure.
154 #@param self A pointer pointing to an instance of the class qgd_Circuit.
155 #@param Input arguments: target_qbit (int), control_qbit (int).
156 
157  def add_CNOT( self, target_qbit, control_qbit):
158 
159  # call the C wrapper function
160  super().add_CNOT(target_qbit, control_qbit)
161 
162 #@brief Call to add a CZ gate to the front of the gate structure.
163 #@param self A pointer pointing to an instance of the class qgd_Circuit.
164 #@param Input arguments: target_qbit (int), control_qbit (int).
165 
166  def add_CZ( self, target_qbit, control_qbit):
167 
168  # call the C wrapper function
169  super().add_CZ(target_qbit, control_qbit)
170 
171 #@brief Call to add a CH gate to the front of the gate structure.
172 #@param self A pointer pointing to an instance of the class qgd_Circuit.
173 #@param Input arguments: target_qbit (int), control_qbit (int).
174 
175  def add_CH( self, target_qbit, control_qbit):
176 
177  # call the C wrapper function
178  super().add_CH(target_qbit, control_qbit)
179 
180 #@brief Call to add a SYC gate to the front of the gate structure.
181 #@param self A pointer pointing to an instance of the class qgd_Circuit.
182 #@param Input arguments: target_qbit (int), control_qbit (int).
183 
184  def add_SYC( self, target_qbit, control_qbit):
185 
186  # call the C wrapper function
187  super().add_SYC(target_qbit, control_qbit)
188 
189 
190 #@brief Call to add a Hadamard gate to the front of the gate structure.
191 #@param self A pointer pointing to an instance of the class qgd_Circuit.
192 #@param Input arguments: target_qbit (int)
193 
194  def add_H( self, target_qbit):
195 
196  # call the C wrapper function
197  super().add_H(target_qbit)
198 
199 #@brief Call to add a X gate to the front of the gate structure.
200 #@param self A pointer pointing to an instance of the class qgd_Circuit.
201 #@param Input arguments: target_qbit (int)
202 
203  def add_X( self, target_qbit):
204 
205  # call the C wrapper function
206  super().add_X(target_qbit)
207 
208 #@brief Call to add a Y gate to the front of the gate structure.
209 #@param self A pointer pointing to an instance of the class qgd_Circuit.
210 #@param Input arguments: target_qbit (int).
211 
212  def add_Y( self, target_qbit):
213 
214  # call the C wrapper function
215  super().add_Y(target_qbit)
216 
217 #@brief Call to add a Z gate to the front of the gate structure.
218 #@param self A pointer pointing to an instance of the class qgd_Circuit.
219 #@param Input arguments: target_qbit (int).
220 
221  def add_Z( self, target_qbit):
222 
223  # call the C wrapper function
224  super().add_Z(target_qbit)
225 
226 #@brief Call to add a SX gate to the front of the gate structure.
227 #@param self A pointer pointing to an instance of the class qgd_Circuit.
228 #@param Input arguments: target_qbit (int).
229 
230  def add_SX( self, target_qbit):
231 
232  # call the C wrapper function
233  super().add_SX(target_qbit)
234 
235 #@brief Call to add a T gate to the front of the gate structure.
236 #@param self A pointer pointing to an instance of the class qgd_Circuit.
237 #@param Input arguments: target_qbit (int).
238 
239  def add_T( self, target_qbit):
240 
241  # call the C wrapper function
242  super().add_T(target_qbit)
243 
244 #@brief Call to add a T gate to the front of the gate structure.
245 #@param self A pointer pointing to an instance of the class qgd_Circuit.
246 #@param Input arguments: target_qbit (int).
247 
248  def add_Tdg( self, target_qbit):
249 
250  # call the C wrapper function
251  super().add_Tdg(target_qbit)
252 
253 #@brief Call to add adaptive gate to the front of the gate structure.
254 #@param self A pointer pointing to an instance of the class qgd_Circuit.
255 #@param Input arguments: target_qbit (int), control_qbit (int).
256 
257  def add_adaptive( self, target_qbit, control_qbit):
258 
259  # call the C wrapper function
260  super().add_adaptive(target_qbit, control_qbit)
261 
262 #@brief Call to add a CROT gate to the front of the gate structure.
263 #@param self A pointer pointing to an instance of the class qgd_Circuit.
264 #@param Input arguments: target_qbit (int).
265 
266  def add_CROT( self, target_qbit, control_qbit, subtype):
267 
268  # call the C wrapper function
269  super(qgd_Circuit, self).add_CROT(target_qbit, control_qbit, subtype)
270 
271 #@brief Call to add adaptive gate to the front of the gate structure.
272 #@param self A pointer pointing to an instance of the class qgd_Circuit.
273 #@param Input arguments: target_qbit (int), control_qbit (int).
274 
275  def add_Circuit( self, gate):
276 
277  # call the C wrapper function
278  super().add_Circuit(gate)
279 
280 #@brief Call to retrieve the matrix of the operation.
281 #@param self A pointer pointing to an instance of the class qgd_Circuit.
282 #@param Input arguments: parameters_mtx.
283 
284  def get_Matrix( self, parameters_mtx):
285 
286  # call the C wrapper function
287  return super().get_Matrix(parameters_mtx)
288 
289 
290 #@brief Call to get the parameters of the matrices.
291 #@param self A pointer pointing to an instance of the class qgd_Circuit.
292 
293  def get_Parameter_Num( self):
294 
295  # call the C wrapper function
296  return super().get_Parameter_Num()
297 
298 
299 
300 #@brief Call to apply the gate operation on the input matrix
301 #@param Input arguments: parameters_mtx, unitary_mtx.
302  def apply_to( self, parameters_mtx, unitary_mtx, parallel=1):
303 
304  # call the C wrapper function
305  super().apply_to( parameters_mtx, unitary_mtx, parallel=parallel )
306 
307 
308 
309 
315  def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None ):
316 
317  # validate input parameters
318 
319  qbit_num = self.get_Qbit_Num()
320 
321  qubit_list_validated = list()
322  if isinstance(qubit_list, list) or isinstance(qubit_list, tuple):
323  for item in qubit_list:
324  if isinstance(item, int):
325  qubit_list_validated.append(item)
326  qubit_list_validated = list(set(qubit_list_validated))
327  else:
328  print("Elements of qbit_list should be integers")
329  return
330  elif qubit_list == None:
331  qubit_list_validated = [ x for x in range(qbit_num) ]
332 
333  else:
334  print("Elements of qbit_list should be integers")
335  return
336 
337 
338  if parameters is None:
339  print( "get_Second_Renyi_entropy: array of input parameters is None")
340  return None
341 
342 
343  if input_state is None:
344  matrix_size = 1 << qbit_num
345  input_state = np.zeros( (matrix_size,1), dtype=np.complex128 )
346  input_state[0] = 1
347 
348  # evaluate the entropy
349  entropy = super().get_Second_Renyi_Entropy( parameters, input_state, qubit_list_validated)
350 
351 
352  return entropy
353 
354 
355 
356 
359  def get_Qbit_Num(self):
360 
361  return super().get_Qbit_Num()
362 
363 
364 
367  def get_Qbits(self):
368 
369  return super().get_Qbits()
370 
371 
372 
375  def get_Gates(self):
376 
377  return super().get_Gates()
378 
379 
380 
383  def get_Gate_Nums(self):
384 
385  return super().get_Gate_Nums()
386 
387 
388 
393  def Remap_Qbits(self, qbit_map, qbit_num=None):
394 
395  if qbit_num == None:
396  qbit_num = self.get_Qbit_Num()
397 
398 
399  return super().Remap_Qbits( qbit_map, qbit_num)
400 
401 
402 
403 
404 #@brief Call to get the starting index of the parameters in the parameter array corresponding to the circuit in which the current gate is incorporated.
406 
407  # call the C wrapper function
408  return super().get_Parameter_Start_Index()
409 
410 
411 #@brief Method to get the list of parent gate indices. Then the parent gates can be obtained from the list of gates involved in the circuit.
412  def get_Parents( self, gate):
413 
414  # call the C wrapper function
415  return super().get_Parents( gate )
416 
417 
418 #@brief Method to get the list of child gate indices. Then the children gates can be obtained from the list of gates involved in the circuit.
419  def get_Children( self, gate):
420 
421  # call the C wrapper function
422  return super().get_Children( gate )
423 
424 
425 #@brief TODO: Jakab
426  def get_Circuit_Depth(self):
427  used_gates_idx = []
428  gates = self.get_Gates()
429  depth = 0
430  gate_start_idx = 0
431  gate_groups = []
432  while (len(used_gates_idx) != len(gates)):
433  involved_qbits=[]
434  gate_start_idx_prev = gate_start_idx
435  gate_idx = gate_start_idx
436  depth += 1
437  control_last_single=[False]*self.qbit_num
438  gate_group=[]
439  while((len(involved_qbits)<self.qbit_num) and gate_idx<len(gates)):
440  if gate_idx not in used_gates_idx:
441  target_qbit = gates[gate_idx].get_Target_Qbit()
442  control_qbit = gates[gate_idx].get_Control_Qbit()
443  gate = gates[gate_idx]
444  if isinstance( gate, qgd_CROT ):
445  if ((control_qbit in involved_qbits) and control_last_single[control_qbit]==False) and (target_qbit not in involved_qbits):
446  involved_qbits.append(target_qbit)
447  used_gates_idx.append(gate_idx)
448  gate_group.append(gate_idx)
449  control_last_single[control_qbit]=False
450  elif ((control_qbit in involved_qbits) and control_last_single[control_qbit]==True) and (target_qbit not in involved_qbits):
451  involved_qbits.append(target_qbit)
452  if (gate_start_idx == gate_start_idx_prev):
453  gate_start_idx=gate_idx
454  elif (control_qbit not in involved_qbits) and (target_qbit not in involved_qbits):
455  involved_qbits.append(target_qbit)
456  involved_qbits.append(control_qbit)
457  used_gates_idx.append(gate_idx)
458  gate_group.append(gate_idx)
459  control_last_single[control_qbit]=False
460  elif (gate_start_idx == gate_start_idx_prev):
461  gate_start_idx=gate_idx
462  else:
463  if control_qbit!=-1:
464  if (control_qbit not in involved_qbits) and (target_qbit not in involved_qbits):
465  involved_qbits.append(target_qbit)
466  involved_qbits.append(control_qbit)
467  used_gates_idx.append(gate_idx)
468  gate_group.append(gate_idx)
469  elif (gate_start_idx == gate_start_idx_prev):
470  gate_start_idx=gate_idx
471  else:
472  control_last_single[target_qbit]=True
473  if target_qbit not in involved_qbits:
474  involved_qbits.append(target_qbit)
475  used_gates_idx.append(gate_idx)
476  control_last_single[target_qbit]=True
477  gate_group.append(gate_idx)
478  elif (gate_start_idx == gate_start_idx_prev):
479  gate_start_idx=gate_idx
480  gate_idx+=1
481  gate_groups.append(gate_group)
482  return depth
483 
484 
485 #@brief Add a generic gate to the circuit
486  def add_Gate(self,qgd_gate):
487 
488 
489  if isinstance(qgd_gate,H):
490  self.add_H(qgd_gate.get_Target_Qbit())
491  elif isinstance(qgd_gate,X):
492  self.add_X(qgd_gate.get_Target_Qbit())
493  elif isinstance(qgd_gate,Y):
494  self.add_Y(qgd_gate.get_Target_Qbit())
495  elif isinstance(qgd_gate,Z):
496  self.add_Z(qgd_gate.get_Target_Qbit())
497  elif isinstance(qgd_gate,CH):
498  self.add_CH(qgd_gate.get_Target_Qbit(),qgd_gate.get_Control_Qbit())
499  elif isinstance(qgd_gate,CZ):
500  self.add_CZ(qgd_gate.get_Target_Qbit(),qgd_gate.get_Control_Qbit())
501  elif isinstance(qgd_gate,RX):
502  self.add_RX(qgd_gate.get_Target_Qbit())
503  elif isinstance(qgd_gate,RY):
504  self.add_RY(qgd_gate.get_Target_Qbit())
505  elif isinstance(qgd_gate,RZ):
506  self.add_RZ(qgd_gate.get_Target_Qbit())
507  elif isinstance(qgd_gate,SX):
508  self.add_SX(qgd_gate.get_Target_Qbit())
509  elif isinstance(qgd_gate,U1):
510  self.add_U1(qgd_gate.get_Target_Qbit())
511  elif isinstance(qgd_gate,U2):
512  self.add_U2(qgd_gate.get_Target_Qbit())
513  elif isinstance(qgd_gate,U3):
514  self.add_U3(qgd_gate.get_Target_Qbit())
515  elif isinstance(qgd_gate,CRY):
516  self.add_CRY(qgd_gate.get_Target_Qbit(),qgd_gate.get_Control_Qbit())
517  elif isinstance(qgd_gate,CNOT):
518  self.add_CNOT(qgd_gate.get_Target_Qbit(),qgd_gate.get_Control_Qbit())
519  elif isinstance(qgd_gate,T):
520  self.add_T(qgd_gate.get_Target_Qbit())
521  elif isinstance(qgd_gate,Tdg):
522  self.add_Tdg(qgd_gate.get_Target_Qbit())
523  elif isinstance(qgd_gate,R):
524  self.add_R(qgd_gate.get_Target_Qbit())
525  elif isinstance(qgd_gate,qgd_CROT):
526  self.add_CROT(qgd_gate.get_Target_Qbit(),qgd_gate.get_Control_Qbit(),qgd_gate.subtype)
527  else:
528  raise Exception("Cannot add gate: unimplemented gate type")
def add_Y(self, target_qbit)
Definition: qgd_Circuit.py:212
def get_Parameter_Start_Index(self)
Definition: qgd_Circuit.py:405
def add_CNOT(self, target_qbit, control_qbit)
Definition: qgd_Circuit.py:157
def get_Qbit_Num(self)
Call to get the number of qubits in the circuit.
Definition: qgd_Circuit.py:359
def add_SX(self, target_qbit)
Definition: qgd_Circuit.py:230
A QGD Python interface class for the Gates_Block.
Definition: qgd_Circuit.py:60
def add_SYC(self, target_qbit, control_qbit)
Definition: qgd_Circuit.py:184
def add_T(self, target_qbit)
Definition: qgd_Circuit.py:239
def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None)
Call to get the second Rényi entropy.
Definition: qgd_Circuit.py:315
def add_CH(self, target_qbit, control_qbit)
Definition: qgd_Circuit.py:175
def add_X(self, target_qbit)
Definition: qgd_Circuit.py:203
def get_Gates(self)
Call to get the list of gates (or subcircuits) in the circuit.
Definition: qgd_Circuit.py:375
def get_Gate_Nums(self)
Call to get statisctics on the gate counts in the circuit.
Definition: qgd_Circuit.py:383
def add_RY(self, target_qbit)
Definition: qgd_Circuit.py:139
def add_R(self, target_qbit)
Definition: qgd_Circuit.py:130
def add_RZ(self, target_qbit)
Definition: qgd_Circuit.py:148
def add_CROT(self, target_qbit, control_qbit, subtype)
Definition: qgd_Circuit.py:266
def Remap_Qbits(self, qbit_map, qbit_num=None)
Call to remap the qubits in the circuit.
Definition: qgd_Circuit.py:393
def get_Matrix(self, parameters_mtx)
Definition: qgd_Circuit.py:284
def add_CZ(self, target_qbit, control_qbit)
Definition: qgd_Circuit.py:166
def add_H(self, target_qbit)
Definition: qgd_Circuit.py:194
def add_RX(self, target_qbit)
Definition: qgd_Circuit.py:122
def add_adaptive(self, target_qbit, control_qbit)
Definition: qgd_Circuit.py:257
def apply_to(self, parameters_mtx, unitary_mtx, parallel=1)
Definition: qgd_Circuit.py:302
def add_Tdg(self, target_qbit)
Definition: qgd_Circuit.py:248
def get_Parents(self, gate)
Definition: qgd_Circuit.py:412
def __init__(self, qbit_num)
Constructor of the class.
Definition: qgd_Circuit.py:68
def get_Qbits(self)
Call to get the list of qubits involved in the circuit.
Definition: qgd_Circuit.py:367
def get_Children(self, gate)
Definition: qgd_Circuit.py:419
def add_U1(self, target_qbit)
Definition: qgd_Circuit.py:95
def add_U3(self, target_qbit)
Definition: qgd_Circuit.py:113
def add_Circuit(self, gate)
Definition: qgd_Circuit.py:275
def get_Control_Qbit(self)
Definition: qgd_CROT.py:87
def add_U2(self, target_qbit)
Definition: qgd_Circuit.py:104
def add_Z(self, target_qbit)
Definition: qgd_Circuit.py:221
def add_Gate(self, qgd_gate)
Definition: qgd_Circuit.py:486
def get_Target_Qbit(self)
Definition: qgd_CROT.py:80