Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Qiskit_IO.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 squander.gates.qgd_Circuit import qgd_Circuit as Circuit
31 
32 
33 from squander.gates.gates_Wrapper import (
34  U1,
35  U2,
36  U3,
37  H,
38  X,
39  Y,
40  Z,
41  T,
42  S,
43  Sdg,
44  Tdg,
45  R,
46  CH,
47  CNOT,
48  CZ,
49  RX,
50  RY,
51  RZ,
52  SX,
53  SXdg,
54  SYC,
55  CRY,
56  CRZ,
57  CRX,
58  CP,
59  CU,
60  SWAP,
61  CSWAP,
62  CCX,
63  RXX,
64  RYY,
65  RZZ )
66 
67 
68 
69 
70 def scalar(param):
71  """Extract scalar value from array or return as is"""
72  return param.item() if hasattr(param, 'item') else param
73 
74 
75 
79 def get_Qiskit_Circuit( Squander_circuit, parameters ):
80 
81  from qiskit import QuantumCircuit
82 
83  # creating Qiskit quantum circuit
84  circuit = QuantumCircuit(Squander_circuit.get_Qbit_Num() )
85 
86  gates = Squander_circuit.get_Gates()
87 
88  # constructing quantum circuit
89  for gate in gates:
90 
91  if isinstance( gate, CNOT ):
92  # adding CNOT gate to the quantum circuit
93  circuit.cx( gate.get_Control_Qbit(), gate.get_Target_Qbit() )
94 
95  elif isinstance( gate, CRY ):
96  # adding CNOT gate to the quantum circuit
97  parameters_gate = gate.Extract_Parameters( parameters )
98  circuit.cry( parameters_gate[0], gate.get_Control_Qbit(), gate.get_Target_Qbit() )
99 
100  elif isinstance( gate, CRZ ):
101  # adding CNOT gate to the quantum circuit
102  parameters_gate = gate.Extract_Parameters( parameters )
103  circuit.crz( parameters_gate[0], gate.get_Control_Qbit(), gate.get_Target_Qbit() )
104 
105  elif isinstance( gate, CRX ):
106  # adding CNOT gate to the quantum circuit
107  parameters_gate = gate.Extract_Parameters( parameters )
108  circuit.crx( parameters_gate[0], gate.get_Control_Qbit(), gate.get_Target_Qbit() )
109 
110  elif isinstance( gate, CP ):
111  # adding CNOT gate to the quantum circuit
112  parameters_gate = gate.Extract_Parameters( parameters )
113  circuit.cp( parameters_gate[0], gate.get_Control_Qbit(), gate.get_Target_Qbit() )
114 
115  elif isinstance( gate, CZ ):
116  # adding CZ gate to the quantum circuit
117  circuit.cz( gate.get_Control_Qbit(), gate.get_Target_Qbit() )
118 
119  elif isinstance( gate, CH ):
120  # adding CZ gate to the quantum circuit
121  circuit.ch( gate.get_Control_Qbit(), gate.get_Target_Qbit() )
122 
123  elif isinstance( gate, SYC ):
124  # Sycamore gate
125  print("Unsupported gate in the circuit export: Sycamore gate")
126  return None
127 
128  elif isinstance( gate, U1 ):
129  # adding U1 gate to the quantum circuit
130  parameters_gate = gate.Extract_Parameters( parameters )
131  circuit.p( parameters_gate[0], gate.get_Target_Qbit() ) # U1 is equivalent to P gate
132 
133  elif isinstance( gate, U2 ):
134  # adding U2 gate to the quantum circuit
135  parameters_gate = gate.Extract_Parameters( parameters )
136  circuit.u( np.pi/2, parameters_gate[0], parameters_gate[1], gate.get_Target_Qbit() )
137 
138  elif isinstance( gate, U3 ):
139  # adding U3 gate to the quantum circuit
140  parameters_gate = gate.Extract_Parameters( parameters )
141  circuit.u( parameters_gate[0], parameters_gate[1], parameters_gate[2], gate.get_Target_Qbit() )
142 
143  elif isinstance( gate, CU ):
144  # adding U3 gate to the quantum circuit
145  parameters_gate = gate.Extract_Parameters( parameters )
146  circuit.cu( parameters_gate[0], parameters_gate[1], parameters_gate[2], parameters_gate[3], gate.get_Control_Qbit(), gate.get_Target_Qbit() )
147 
148  elif isinstance( gate, RX ):
149  # RX gate
150  parameters_gate = gate.Extract_Parameters( parameters )
151  circuit.rx( parameters_gate[0], gate.get_Target_Qbit() )
152 
153  elif isinstance( gate, RY ):
154  # RY gate
155  parameters_gate = gate.Extract_Parameters( parameters )
156  circuit.ry( parameters_gate[0], gate.get_Target_Qbit() )
157 
158  elif isinstance( gate, RZ ):
159  # RZ gate
160  parameters_gate = gate.Extract_Parameters( parameters )
161  circuit.rz( parameters_gate[0], gate.get_Target_Qbit() )
162 
163  elif isinstance( gate, R ):
164  # R gate
165  parameters_gate = gate.Extract_Parameters( parameters )
166  circuit.r( parameters_gate[0],parameters_gate[1], gate.get_Target_Qbit() )
167  elif isinstance( gate, H ):
168  # Hadamard gate
169  circuit.h( gate.get_Target_Qbit() )
170 
171  elif isinstance( gate, X ):
172  # X gate
173  circuit.x( gate.get_Target_Qbit() )
174 
175  elif isinstance( gate, Y ):
176  # Y gate
177  circuit.y( gate.get_Target_Qbit() )
178 
179  elif isinstance( gate, Z ):
180  # Z gate
181  circuit.z( gate.get_Target_Qbit() )
182 
183  elif isinstance( gate, S ):
184  # S gate
185  circuit.s( gate.get_Target_Qbit() )
186 
187  elif isinstance( gate, Sdg ):
188  # Sdg gate
189  circuit.sdg( gate.get_Target_Qbit() )
190 
191  elif isinstance( gate, SX ):
192  # SX gate
193  circuit.sx( gate.get_Target_Qbit() )
194 
195  elif isinstance( gate, SXdg ):
196  # SXdg gate
197  circuit.sxdg( gate.get_Target_Qbit() )
198 
199  elif isinstance( gate, T ):
200  # T gate
201  circuit.t( gate.get_Target_Qbit() )
202 
203  elif isinstance( gate, Tdg ):
204  # T gate
205  circuit.tdg( gate.get_Target_Qbit() )
206 
207  elif isinstance(gate, CCX):
208  #CCX gate
209  control_qbits = gate.get_Control_Qbits()
210  circuit.ccx(control_qbits[0], control_qbits[1], gate.get_Target_Qbit())
211 
212  elif isinstance(gate, CSWAP):
213  #CCX gate
214  target_qbits = gate.get_Target_Qbits()
215  circuit.cswap( gate.get_Control_Qbit(), target_qbits[0], target_qbits[1])
216 
217  elif isinstance(gate, SWAP):
218  #CCX gate
219  target_qbits = gate.get_Target_Qbits()
220  circuit.swap(target_qbits[0], target_qbits[1])
221 
222  elif isinstance( gate, RXX ):
223  # RXX gate
224  parameters_gate = gate.Extract_Parameters( parameters )
225  target_qbits = gate.get_Target_Qbits()
226  circuit.rxx( parameters_gate[0], target_qbits[0], target_qbits[1] )
227 
228  elif isinstance( gate, RYY ):
229  # RYY gate
230  parameters_gate = gate.Extract_Parameters( parameters )
231  target_qbits = gate.get_Target_Qbits()
232  circuit.ryy( parameters_gate[0], target_qbits[0], target_qbits[1] )
233 
234  elif isinstance( gate, RZZ ):
235  # RZZ gate
236  parameters_gate = gate.Extract_Parameters( parameters )
237  target_qbits = gate.get_Target_Qbits()
238  circuit.rzz( parameters_gate[0], target_qbits[0], target_qbits[1] )
239 
240  elif isinstance( gate, Circuit ):
241  # Sub-circuit gate
242  raise ValueError("Qiskit export of circuits with subcircuit is not supported. Use Circuit::get_Flat_Circuit prior of exporting circuit.")
243 
244  else:
245  print(gate)
246  raise ValueError("Unsupported gate in the circuit export.")
247 
248  return( circuit )
249 
250 
251 
256 def get_Qiskit_Circuit_inverse( Squander_circuit, parameters ):
257 
258  from squander import utils
259  from squander.gates.gates_Wrapper import CR, CROT
260 
261  inv_circuit, inv_parameters = utils.invert_circuit( Squander_circuit, parameters )
262 
263  # CR and CROT have no native Qiskit export; convert them to CNOT basis.
264  # All other gates (including SYC, which invert_circuit already decomposes
265  # to CNOT/U1 primitives) are handled directly by get_Qiskit_Circuit.
266  # Routing CSWAP etc. through circuit_to_CNOT_basis would introduce a
267  # spurious global phase from the SX-based decomposition.
268  if any(isinstance(g, (CR, CROT)) for g in inv_circuit.get_Gates()):
269  cnot_circuit, cnot_parameters = utils.circuit_to_CNOT_basis( inv_circuit, inv_parameters )
270  return get_Qiskit_Circuit( cnot_circuit, cnot_parameters )
271 
272  return get_Qiskit_Circuit( inv_circuit, inv_parameters )
273 
274 
275 
279 
280  from qiskit import QuantumCircuit
281  from qiskit.circuit import ParameterExpression
282 
283 
284 
285 
286  # get the register of qubits
287  q_register = qc_in.qubits
288 
289  # get the size of the register
290  register_size = qc_in.num_qubits
291 
292  # construct the qgd gate structure
293  Circuit_Squander = Circuit(register_size)
294  parameters = list()
295 
296  for gate in qc_in.data:
297 
298  name = gate.operation.name
299  #print('Gate name in Qiskit: ', name )
300  if name == 'u1' or name == 'p':
301  # add U1 gate - lambda parameter used directly
302  qubits = gate.qubits
303  qubit = q_register.index( qubits[0] )
304 
305  params = gate.operation.params
306 
307  for param in params:
308  parameters = parameters + [float(param)]
309 
310  Circuit_Squander.add_U1( qubit )
311 
312  elif name == 'u2':
313  # add U2 gate - phi and lambda parameters used directly
314  qubits = gate.qubits
315  qubit = q_register.index( qubits[0] )
316 
317  params = gate.operation.params
318 
319  for param in params:
320  parameters = parameters + [float(param)]
321 
322  Circuit_Squander.add_U2( qubit )
323 
324  elif name == 'u3' or name == 'u':
325  # add U3 gate - theta/2 handled internally by U3 gate implementation
326  qubits = gate.qubits
327  qubit = q_register.index( qubits[0] )
328 
329  params = gate.operation.params
330  params[0] = params[0]/2 #SQUADER works with theta/2
331 
332  for param in params:
333  parameters = parameters + [float(param)]
334 
335  Circuit_Squander.add_U3( qubit )
336 
337  elif name == 'cu' :
338  # add CU gate - theta/2 handled internally by U3 gate implementation
339  qubits = gate.qubits
340  qubit0 = q_register.index( qubits[0] )
341  qubit1 = q_register.index( qubits[1] )
342 
343  params = gate.operation.params
344  params[0] = params[0]/2 #SQUADER works with theta/2
345 
346  for param in params:
347  parameters = parameters + [float(param)]
348 
349  Circuit_Squander.add_CU( qubit1, qubit0 )
350 
351  elif name == 'cx':
352  # add cx gate
353  qubits = gate.qubits
354  qubit0 = q_register.index( qubits[0] )
355  qubit1 = q_register.index( qubits[1] )
356  Circuit_Squander.add_CNOT( qubit1, qubit0 )
357 
358  elif name == "cry":
359 
360  qubits = gate.qubits
361  qubit0 = q_register.index( qubits[0] )
362  qubit1 = q_register.index( qubits[1] )
363 
364  params = gate.operation.params
365  params[0] = params[0]/2 #SQUADER works with theta/2
366 
367  for param in params:
368  parameters = parameters + [float(param)]
369 
370  Circuit_Squander.add_CRY( qubit1, qubit0 )
371 
372  elif name == "crz":
373 
374  qubits = gate.qubits
375  qubit0 = q_register.index( qubits[0] )
376  qubit1 = q_register.index( qubits[1] )
377 
378  params = gate.operation.params
379  params[0] = params[0]/2 #SQUADER works with theta/2
380 
381  for param in params:
382  parameters = parameters + [float(param)]
383 
384  Circuit_Squander.add_CRZ( qubit1, qubit0 )
385 
386  elif name == "crx":
387 
388  qubits = gate.qubits
389  qubit0 = q_register.index( qubits[0] )
390  qubit1 = q_register.index( qubits[1] )
391 
392  params = gate.operation.params
393  params[0] = params[0]/2 #SQUADER works with theta/2
394 
395  for param in params:
396  parameters = parameters + [float(param)]
397 
398  Circuit_Squander.add_CRX( qubit1, qubit0 )
399 
400 
401  elif name == "cu1" or name == "cp":
402 
403  qubits = gate.qubits
404  qubit0 = q_register.index( qubits[0] )
405  qubit1 = q_register.index( qubits[1] )
406 
407  params = gate.operation.params
408  params[0] = params[0]
409 
410  for param in params:
411  parameters = parameters + [float(param)]
412 
413  Circuit_Squander.add_CP( qubit1, qubit0 )
414 
415  elif name == "cz":
416  qubits = gate.qubits
417  qubit0 = q_register.index( qubits[0] )
418  qubit1 = q_register.index( qubits[1] )
419  Circuit_Squander.add_CZ( qubit1, qubit0 )
420 
421  elif name == "ch":
422  qubits = gate.qubits
423  qubit0 = q_register.index( qubits[0] )
424  qubit1 = q_register.index( qubits[1] )
425  Circuit_Squander.add_CH( qubit1, qubit0 )
426 
427  elif name == "rx":
428  qubits = gate.qubits
429  qubit = q_register.index( qubits[0] )
430 
431  params = gate.operation.params
432  params[0] = params[0]/2 #SQUADER works with theta/2
433 
434  for param in params:
435  parameters = parameters + [float(param)]
436 
437  Circuit_Squander.add_RX( qubit )
438 
439  elif name == "ry":
440  qubits = gate.qubits
441  qubit = q_register.index( qubits[0] )
442 
443  params = gate.operation.params
444  params[0] = params[0]/2 #SQUADER works with theta/2
445 
446  for param in params:
447  parameters = parameters + [float(param)]
448 
449  Circuit_Squander.add_RY( qubit )
450 
451  elif name == "rz" :
452  qubits = gate.qubits
453  qubit = q_register.index( qubits[0] )
454 
455  params = gate.operation.params
456  params[0] = params[0]/2 #SQUADER works with phi/2
457 
458  for param in params:
459  parameters = parameters + [float(param)]
460 
461  Circuit_Squander.add_RZ( qubit )
462 
463  elif name == "h":
464  qubits = gate.qubits
465  qubit = q_register.index( qubits[0] )
466 
467  Circuit_Squander.add_H( qubit )
468 
469  elif name == "x":
470  qubits = gate.qubits
471  qubit = q_register.index( qubits[0] )
472 
473  Circuit_Squander.add_X( qubit )
474 
475  elif name == "y":
476  qubits = gate.qubits
477  qubit = q_register.index( qubits[0] )
478 
479  Circuit_Squander.add_Y( qubit )
480 
481  elif name == "z":
482  qubits = gate.qubits
483  qubit = q_register.index( qubits[0] )
484 
485  Circuit_Squander.add_Z( qubit )
486 
487  elif name == "s":
488  qubits = gate.qubits
489  qubit = q_register.index( qubits[0] )
490 
491  Circuit_Squander.add_S( qubit )
492 
493  elif name == "sdg":
494  qubits = gate.qubits
495  qubit = q_register.index( qubits[0] )
496 
497  Circuit_Squander.add_Sdg( qubit )
498 
499  elif name == "sx":
500  qubits = gate.qubits
501  qubit = q_register.index( qubits[0] )
502 
503  Circuit_Squander.add_SX( qubit )
504 
505  elif name == "sxdg":
506  qubits = gate.qubits
507  qubit = q_register.index(qubits[0])
508 
509  # Native SXdg support is exposed via qgd_Circuit.
510  Circuit_Squander.add_SXdg(qubit)
511 
512  elif name == "t":
513  qubits = gate.qubits
514  qubit = q_register.index( qubits[0] )
515 
516  Circuit_Squander.add_T( qubit )
517 
518  elif name == "tdg":
519  qubits = gate.qubits
520  qubit = q_register.index( qubits[0] )
521 
522  Circuit_Squander.add_Tdg( qubit )
523 
524  elif name == "r":
525  qubits = gate.qubits
526  qubit = q_register.index( qubits[0] )
527 
528  params = gate.operation.params
529  params[0] = params[0]/2 #SQUADER works with theta/2
530 
531  for param in params:
532  parameters = parameters + [float(param)]
533 
534  Circuit_Squander.add_R( qubit )
535 
536  elif name == 'ccx':
537  # add cx gate
538  qubits = gate.qubits
539  qubit0 = q_register.index( qubits[0] )
540  qubit1 = q_register.index( qubits[1] )
541  qubit2 = q_register.index( qubits[2] )
542  Circuit_Squander.add_CCX( qubit2, [qubit1, qubit0] )
543 
544  elif name == 'cswap':
545  # add cx gate
546  qubits = gate.qubits
547  qubit0 = q_register.index( qubits[0] )
548  qubit1 = q_register.index( qubits[1] )
549  qubit2 = q_register.index( qubits[2] )
550  Circuit_Squander.add_CSWAP( [qubit2, qubit1], [qubit0] )
551 
552  elif name == 'swap':
553  # add cx gate
554  qubits = gate.qubits
555  qubit0 = q_register.index( qubits[0] )
556  qubit1 = q_register.index( qubits[1] )
557  Circuit_Squander.add_SWAP( [qubit1, qubit0] )
558  elif name == "rxx":
559  qubits = gate.qubits
560  qubit0 = q_register.index( qubits[0] )
561  qubit1 = q_register.index( qubits[1] )
562 
563  params = gate.operation.params
564  params[0] = params[0]/2 #SQUADER works with theta/2
565 
566  for param in params:
567  parameters = parameters + [float(param)]
568 
569  Circuit_Squander.add_RXX( [qubit0, qubit1] )
570 
571  elif name == "ryy":
572  qubits = gate.qubits
573  qubit0 = q_register.index( qubits[0] )
574  qubit1 = q_register.index( qubits[1] )
575 
576  params = gate.operation.params
577  params[0] = params[0]/2 #SQUADER works with theta/2
578 
579  for param in params:
580  parameters = parameters + [float(param)]
581 
582  Circuit_Squander.add_RYY( [qubit0, qubit1] )
583 
584  elif name == "rzz":
585  qubits = gate.qubits
586  qubit0 = q_register.index( qubits[0] )
587  qubit1 = q_register.index( qubits[1] )
588 
589  params = gate.operation.params
590  params[0] = params[0]/2 #SQUADER works with theta/2
591 
592  for param in params:
593  parameters = parameters + [float(param)]
594 
595  Circuit_Squander.add_RZZ( [qubit0, qubit1] )
596 
597  else:
598  print(f"convert_Qiskit_to_Squander: Unimplemented gate: {name}")
599 
600 
601  parameters = np.asarray(parameters, dtype=np.float64)
602 
603  return Circuit_Squander, parameters
604 
605 
606 
607 
def convert_Qiskit_to_Squander(qc_in)
Call to import initial quantum circuit in QISKIT format to be further comporessed.
Definition: Qiskit_IO.py:278
def get_Qiskit_Circuit_inverse(Squander_circuit, parameters)
Export the inverse of a Squander circuit into Qiskit format.
Definition: Qiskit_IO.py:256
def get_Qiskit_Circuit(Squander_circuit, parameters)
Export the unitary decomposition into Qiskit format.
Definition: Qiskit_IO.py:79