Quantum Gate Decomposer  v1.3
Powerful decomposition of almost any unitary into U3 and CNOT gates
N_Qubit_Decomposition.py
Go to the documentation of this file.
1 
3 """
4 Created on Tue Jun 30 15:44:26 2020
5 Copyright (C) 2020 Peter Rakyta, Ph.D.
6 
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see http://www.gnu.org/licenses/.
19 
20 @author: Peter Rakyta, Ph.D.
21 """
22 
23 
25 
26 
27 import ctypes
28 import numpy as np
29 from os import path
30 
31 
32 
33 #load qgd C library for the decomposition
34 
35 if ( path.exists('.libs/libqgd.so') ):
36  library_path = '.libs/libqgd.so'
37 elif ( path.exists('lib64/libqgd.so') ):
38  library_path = 'lib64/libqgd.so'
39 elif ( path.exists('lib/libqgd.so') ):
40  library_path = 'lib/libqgd.so'
41 else:
42  print("Quantum Gate Decomposer library not found.")
43  exit()
44 
45 
46 _qgd_library = ctypes.cdll.LoadLibrary(library_path)
47 
48 
49 
50 
51 # defining the input/output arguments of the interface methods
52 _qgd_library.iface_new_N_Qubit_Decomposition.argtypes = (ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.c_int, ctypes.c_bool, ctypes.c_int,)
53 _qgd_library.iface_new_N_Qubit_Decomposition.restype = ctypes.c_void_p
54 _qgd_library.iface_start_decomposition.argtypes = (ctypes.c_void_p,)
55 _qgd_library.iface_delete_N_Qubit_Decomposition.argtypes = (ctypes.c_void_p,)
56 _qgd_library.iface_set_identical_blocks.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int,)
57 _qgd_library.iface_set_iteration_loops.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int,)
58 _qgd_library.iface_set_max_layer_num.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int,)
59 _qgd_library.iface_list_operations.argtypes = (ctypes.c_void_p, ctypes.c_int,)
60 _qgd_library.iface_get_operation_num.argtypes = ( ctypes.c_void_p, )
61 _qgd_library.iface_get_operation_num.restype = ctypes.c_int
62 _qgd_library.iface_get_operation.argtypes = ( ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_double), )
63 _qgd_library.iface_get_operation.restype = ctypes.c_int
64 _qgd_library.iface_set_verbose.argtypes = ( ctypes.c_void_p, ctypes.c_bool, )
65 _qgd_library.iface_set_optimalization_block.argtypes = ( ctypes.c_void_p, ctypes.c_int, )
66 
67 
68 
71 
72 
73 
79  def __init__( self, Umtx, optimize_layer_num=False, initial_guess="zeros" ):
80 
81 
82  self.qbit_num = int(round( np.log2( len(Umtx) ) ))
83 
84  matrix_size = int(2**self.qbit_num)
85 
86 
87  # arranging all the elements of the matrix into one row (row major order)
88  Umtx_real = np.real(Umtx).reshape(matrix_size*matrix_size)
89  Umtx_imag = np.imag(Umtx).reshape(matrix_size*matrix_size)
90 
91  # definig ctypes array storing the real and imaginary parts of the unitary
92  array_type = ctypes.c_double * (matrix_size*matrix_size)
93  string_length = len(initial_guess)
94 
95  # convertin intial guess into numerical input
96  if initial_guess=="zeros":
97  initial_guess_num = 0
98  elif initial_guess=="random":
99  initial_guess_num = 1
100  elif initial_guess=="close_to_zero":
101  initial_guess_num = 2
102  else:
103  initial_guess_num = 0
104 
105 
106  self.c_instance = _qgd_library.iface_new_N_Qubit_Decomposition( array_type(*Umtx_real), array_type(*Umtx_imag), ctypes.c_int(self.qbit_num), ctypes.c_bool(optimize_layer_num), ctypes.c_int(initial_guess_num) )
107 
108 
109 
111  def __del__(self):
112 
113  # call to release the instance of the class
114  _qgd_library.iface_delete_N_Qubit_Decomposition( self.c_instance )
115 
116 
117 
118 
122  def start_decomposition(self, finalize_decomposition=True):
123 
124  # start the decomposition routine in the C-class
125  _qgd_library.iface_start_decomposition( self.c_instance )
126 
127 
128 
131  def set_max_layer_num(self, max_layer_num ):
132 
133  for qbit in max_layer_num.keys() :
134  # Set the maximal number of layers throug the C-interface
135  _qgd_library.iface_set_max_layer_num( self.c_instance, ctypes.c_int(qbit), ctypes.c_int(max_layer_num.get(qbit,0)) )
136 
137 
140  def set_iteration_loops(self, iteration_loops ):
141 
142  for qbit in iteration_loops.keys() :
143  # Set the number of iteration loops throug the C-interface
144  _qgd_library.iface_set_iteration_loops( self.c_instance, ctypes.c_int(qbit), ctypes.c_int(iteration_loops.get(qbit,3)) )
145 
146 
147 
150  def set_identical_blocks(self, identical_blocks ):
151 
152  for qbit in identical_blocks.keys() :
153  # Set the number of identical successive blocks throug the C-interface
154  _qgd_library.iface_set_identical_blocks( self.c_instance, ctypes.c_int(qbit), ctypes.c_int(identical_blocks.get(qbit,1)) )
155 
156 
157 
160  def list_operations(self, start_index=1 ):
161 
162  _qgd_library.iface_list_operations( self.c_instance, ctypes.c_int(start_index) );
163 
164 
165 
168  def get_quantum_circuit( self ):
169 
170  from qiskit import QuantumCircuit
171 
172  # creating Qiskit quantum circuit
173  circuit = QuantumCircuit(self.qbit_num)
174 
175  # fill up the quantum circuit witj operations
176 
177  # create Ctypes compatible wrapper variables for the operation parameters
178  operation_type = ctypes.c_int()
179  target_qbit = ctypes.c_int()
180  control_qbit = ctypes.c_int()
181 
182  array_type = ctypes.c_double * 3
183  parameters = array_type(*np.array([0,0,0]))
184 
185  number_of_decomposing_operations = _qgd_library.iface_get_operation_num( self.c_instance )
186  op_idx = ctypes.c_int(number_of_decomposing_operations-1)
187 
188  while True:
189 
190  # retrive the parameters of the op_idx-th operation
191  status = _qgd_library.iface_get_operation( self.c_instance, op_idx, ctypes.byref(operation_type), ctypes.byref(target_qbit), ctypes.byref(control_qbit), parameters )
192 
193  if not ( status == 0 ) :
194  break
195 
196  #print( "op_type python: " + str(operation_type.value))
197  #print("status: " + str(status))
198  #if ( operation_type.value == 2 ) :
199  # for i in parameters: print(i, end=" ")
200  # print(' ')
201  # print( parameters[0] )
202 
203  if operation_type.value == 1:
204  # adding CNOT operation to the quantum circuit
205  circuit.cx(control_qbit.value, target_qbit.value)
206 
207  elif operation_type.value == 2:
208  # adding U3 operation to the quantum circuit
209  circuit.u3(parameters[0], parameters[1], parameters[2], target_qbit.value)
210  pass
211 
212  op_idx.value = op_idx.value - 1
213 
214 
215  return circuit
216 
217 
218 
221  def set_verbose( self, verbose ):
222 
223  _qgd_library.iface_set_verbose( self.c_instance, ctypes.c_bool(verbose) )
224 
225 
226 
227 
230  def set_optimalization_block( self, optimalization_block ):
231 
232  _qgd_library.iface_set_optimalization_block( self.c_instance, ctypes.c_int(optimalization_block) )
233 
234 
235 
236 
237 
238 
239 
def set_max_layer_num(self, max_layer_num)
Set the maximal number of layers used in the subdecomposition of the qbit-th qubit.
def set_iteration_loops(self, iteration_loops)
Set the number of iteration loops during the subdecomposition of the qbit-th qubit.
def __init__(self, Umtx, optimize_layer_num=False, initial_guess="zeros")
c_instance
the instance of the N_Qubit_Decomposition C class
def set_optimalization_block(self, optimalization_block)
Call to set the number of blocks to be optimized in one shot.
def start_decomposition(self, finalize_decomposition=True)
Start the disentanglig process of the least significant two qubit unitary.
def set_verbose(self, verbose)
Set the verbosity of the N_Qubit_Decomposition class.
def set_identical_blocks(self, identical_blocks)
Set the number of identical successive blocks during the subdecomposition of the qbit-th qubit.
A QGD Python interface class for the decomposition of N-qubit unitaries into U3 and CNOT gates.
def __del__(self)
Destructor of the class.
def list_operations(self, start_index=1)
Lists the operations decomposing the initial unitary.
def get_quantum_circuit(self)
Export the unitary decomposition into Qiskit format.