Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_N_Qubit_Decomposition_Tabu_Search.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.decomposition.qgd_N_Qubit_Decomposition_Tabu_Search_Wrapper import qgd_N_Qubit_Decomposition_Tabu_Search_Wrapper
32 from squander.gates.qgd_Circuit import qgd_Circuit
33 
34 
35 
36 
38 class qgd_N_Qubit_Decomposition_Tabu_Search(qgd_N_Qubit_Decomposition_Tabu_Search_Wrapper):
39 
40 
41 
50  def __init__( self, Umtx, level_limit_max=8, topology=None, config={}, accelerator_num=0 ):
51 
52 
53  self.qbit_num = int(round( np.log2( len(Umtx) ) ))
54 
55  # validate input parameters
56 
57  topology_validated = list()
58  if isinstance(topology, list) or isinstance(topology, tuple):
59  for item in topology:
60  if isinstance(item, tuple) and len(item) == 2:
61  item_validated = (np.intc(item[0]), np.intc(item[1]))
62  topology_validated.append(item_validated)
63  else:
64  print("Elements of topology should be two-component tuples (int, int)")
65  return
66  elif topology == None:
67  pass
68  else:
69  print("Input parameter topology should be a list of (int, int) describing the connected qubits in the topology")
70  return
71 
72 
73  # config
74  if not( type(config) is dict):
75  print("Input parameter config should be a dictionary describing the following hyperparameters:") #TODO
76  return
77 
78  # call the constructor of the wrapper class
79  super().__init__(Umtx, self.qbit_num, level_limit_max, topology=topology_validated, config=config, accelerator_num=accelerator_num)
80 
81 
82 
86 
87  # call the C wrapper function
88  super().Start_Decomposition()
89 
90 
91 
94  def Reorder_Qubits( self, qbit_list ):
95 
96  # call the C wrapper function
97  super().Reorder_Qubits(qbit_list)
98 
99 
100 
102  def List_Gates(self):
103 
104  # call the C wrapper function
105  super().List_Gates()
106 
107 
110  def get_Qiskit_Circuit( self ):
111 
112  from squander import Qiskit_IO
113 
114  squander_circuit = self.get_Circuit()
115  parameters = self.get_Optimized_Parameters()
116 
117  return Qiskit_IO.get_Qiskit_Circuit( squander_circuit, parameters )
118 
119 
120 
121 
124  def set_Unitary( self, Umtx_arr ):
125 
126  return super().set_Unitary( Umtx_arr )
127 
128 
129 
132  def set_Optimization_Tolerance( self, tolerance ):
133 
134  return super().set_Optimization_Tolerance( tolerance )
135 
136 
138  def get_Unitary( self ):
139 
140  return super().get_Unitary()
141 
142 
145  def export_Unitary( self, filename ):
146 
147  return super().export_Unitary(filename)
148 
149 
150 
152  def get_Parameter_Num( self ):
153 
154  return super().get_Parameter_Num()
155 
156 
158  def get_Global_Phase( self ):
159 
160  return super().get_Global_Phase()
161 
162 
165  def set_Global_Phase( self, new_global_phase ):
166 
167  return super().set_Global_Phase(new_global_phase)
168 
170  def get_Project_Name( self ):
171 
172  return super().get_Project_Name()
173 
174 
177  def set_Project_Name( self, project_name_new ):
178 
179  return super().set_Project_Name(project_name_new)
180 
183 
184  return super().apply_Global_Phase_Factor()
185 
186 
187 
190  def set_Optimizer( self, optimizer="BFGS" ):
191 
192  # Set the optimizer
193  super().set_Optimizer(optimizer)
194 
195 
196 
199  def get_Matrix( self, parameters = None ):
200 
201 
202  if parameters is None:
203  print( "get_Matrix: arary of input parameters is None")
204  return None
205 
206  return super().get_Matrix( parameters )
207 
208 
211  def set_Cost_Function_Variant( self, costfnc=0 ):
212 
213  # Set the optimizer
214  super().set_Cost_Function_Variant(costfnc=costfnc)
215 
216 
217 
218 
221  def set_Trace_Offset( self, trace_offset=0 ):
222 
223  # Set the trace offset
224  super().set_Trace_Offset(trace_offset=trace_offset)
225 
226 
227 
230  def get_Trace_Offset( self ):
231 
232  # Set the optimizer
233  return super().get_Trace_Offset()
234 
235 
236 
240 
241  return super().get_Optimized_Parameters()
242 
243 
244 
247  def set_Optimized_Parameters(self, new_params):
248 
249  super().set_Optimized_Parameters(new_params)
250 
251 
252 
255  def Optimization_Problem( self, parameters=None ):
256 
257  if parameters is None:
258  print( "Optimization_Problem: array of input parameters is None")
259  return None
260 
261  # evaluate the cost function and gradients
262  cost_function = super().Optimization_Problem(parameters)
263 
264 
265  return cost_function
266 
267 
268 
271  def Optimization_Problem_Grad( self, parameters=None ):
272 
273  if parameters is None:
274  print( "Optimization_Problem: array of input parameters is None")
275  return None
276 
277  # evaluate the cost function and gradients
278  grad = super().Optimization_Problem_Grad(parameters)
279 
280  grad = grad.reshape( (-1,))
281 
282  return grad
283 
284 
285 
288  def Optimization_Problem_Combined( self, parameters=None ):
289 
290  if parameters is None:
291  print( "Optimization_Problem_Combined: array of input parameters is None")
292  return None
293 
294  # evaluate the cost function and gradients
295  cost_function, grad = super().Optimization_Problem_Combined(parameters)
296 
297  grad = grad.reshape( (-1,))
298 
299  return cost_function, grad
300 
301 
303  def get_Num_of_Iters(self):
304 
305  return super().get_Num_of_Iters()
306 
307 
310  def set_Max_Iterations(self, max_iters):
311 
312  super().set_Max_Iterations(max_iters)
313 
314 
317  def set_Cost_Function_Variant(self, cost_func):
318 
319  super().set_Cost_Function_Variant(cost_func)
320 
321 
322 
326 
327  return super().get_Decomposition_Error()
328 
329 
330 
331 
337  def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None ):
338 
339  qbit_num = self.get_Qbit_Num()
340 
341  qubit_list_validated = list()
342  if isinstance(qubit_list, list) or isinstance(qubit_list, tuple):
343  for item in qubit_list:
344  if isinstance(item, int):
345  qubit_list_validated.append(item)
346  qubit_list_validated = list(set(qubit_list_validated))
347  else:
348  print("Elements of qbit_list should be integers")
349  return
350  elif qubit_list == None:
351  qubit_list_validated = [ x for x in range(qbit_num) ]
352 
353  else:
354  print("Elements of qbit_list should be integers")
355  return
356 
357 
358  if parameters is None:
359  print( "get_Second_Renyi_entropy: array of input parameters is None")
360  return None
361 
362 
363  if input_state is None:
364  matrix_size = 1 << qbit_num
365  input_state = np.zeros( (matrix_size,1) )
366  input_state[0] = 1
367 
368  # evaluate the entropy
369  entropy = super().get_Second_Renyi_Entropy( parameters, input_state, qubit_list_validated)
370 
371 
372  return entropy
373 
374 
375 
376 
379  def get_Qbit_Num(self):
380 
381  return super().get_Qbit_Num()
def get_Parameter_Num(self)
Call to get the number of free parameters in the gate structure used for the decomposition.
A QGD Python interface class for the decomposition of N-qubit unitaries into U3 and CNOT gates...
def get_Decomposition_Error(self)
Call to get the error of the decomposition.
def Optimization_Problem_Grad(self, parameters=None)
Call to evaluate the gradient components.
def get_Trace_Offset(self)
Call to get the trace offset used in the cost function.
def set_Cost_Function_Variant(self, costfnc=0)
Call to set the optimizer used in the gate synthesis process.
def set_Project_Name(self, project_name_new)
Call to set the name of the SQUANDER project.
def export_Unitary(self, filename)
Call to export unitary matrix to binary file.
def get_Project_Name(self)
Call to get the name of the SQUANDER project.
def set_Unitary(self, Umtx_arr)
Call to set unitary matrix from a numpy array.
def set_Optimization_Tolerance(self, tolerance)
Call to set the error tolerance of the decomposition.
def Reorder_Qubits(self, qbit_list)
Call to reorder the qubits in the matrix of the gate.
def set_Trace_Offset(self, trace_offset=0)
Call to set the trace offset used in the cost function.
def Start_Decomposition(self)
Wrapper function to call the start_decomposition method of C++ class N_Qubit_Decomposition.
def set_Global_Phase(self, new_global_phase)
Call to set global phase.
def set_Optimizer(self, optimizer="BFGS")
Call to set the optimizer used in the gate synthesis process.
def get_Qiskit_Circuit(self)
Export the unitary decomposition into Qiskit format.
def List_Gates(self)
Call to print the gates decomposing the initial unitary.
def set_Optimized_Parameters(self, new_params)
Call to set the parameters which are used as a starting point in the optimization.
def get_Qbit_Num(self)
Call to get the number of qubits in the circuit.
def apply_Global_Phase_Factor(self)
Call to apply global phase on Unitary matrix.
def get_Matrix(self, parameters=None)
Call to retrieve the unitary of the circuit.
def Optimization_Problem_Combined(self, parameters=None)
Call to evaluate the cost function and the gradient components.
def get_Optimized_Parameters(self)
Call to get the optimized parameters set in numpy array.
def __init__(self, Umtx, level_limit_max=8, topology=None, config={}, accelerator_num=0)
Constructor of the class.
def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None)
Call to get the second Rényi entropy.
def set_Max_Iterations(self, max_iters)
Call to set the maximum number of iterations for each optimization loop.
def Optimization_Problem(self, parameters=None)
Call to evaluate the cost function.