Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_N_Qubit_Decomposition_Tree_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_Tree_Search_Wrapper import qgd_N_Qubit_Decomposition_Tree_Search_Wrapper
32 from squander.gates.qgd_Circuit import qgd_Circuit
33 
34 
35 
36 
38 class qgd_N_Qubit_Decomposition_Tree_Search(qgd_N_Qubit_Decomposition_Tree_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 
85 
86  # call the C wrapper function
87  super().Start_Decomposition()
88 
89 
90 
91 
92 
95  def Reorder_Qubits( self, qbit_list ):
96 
97  # call the C wrapper function
98  super().Reorder_Qubits(qbit_list)
99 
100 
101 
103  def List_Gates(self):
104 
105  # call the C wrapper function
106  super().List_Gates()
107 
108 
111  def get_Qiskit_Circuit( self ):
112 
113  from squander import Qiskit_IO
114 
115  squander_circuit = self.get_Circuit()
116  parameters = self.get_Optimized_Parameters()
117 
118  return Qiskit_IO.get_Qiskit_Circuit( squander_circuit, parameters )
119 
120 
121 
122 
123 
124 
127  def set_Unitary( self, Umtx_arr ):
128 
129  return super().set_Unitary( Umtx_arr )
130 
131 
132 
135  def set_Optimization_Tolerance( self, tolerance ):
136 
137  return super().set_Optimization_Tolerance( tolerance )
138 
139 
141  def get_Unitary( self ):
142 
143  return super().get_Unitary()
144 
145 
148  def export_Unitary( self, filename ):
149 
150  return super().export_Unitary(filename)
151 
152 
153 
155  def get_Parameter_Num( self ):
156 
157  return super().get_Parameter_Num()
158 
159 
161  def get_Global_Phase( self ):
162 
163  return super().get_Global_Phase()
164 
165 
168  def set_Global_Phase( self, new_global_phase ):
169 
170  return super().set_Global_Phase(new_global_phase)
171 
173  def get_Project_Name( self ):
174 
175  return super().get_Project_Name()
176 
177 
180  def set_Project_Name( self, project_name_new ):
181 
182  return super().set_Project_Name(project_name_new)
183 
186 
187  return super().apply_Global_Phase_Factor()
188 
189 
190 
193  def set_Optimizer( self, optimizer="BFGS" ):
194 
195  # Set the optimizer
196  super().set_Optimizer(optimizer)
197 
198 
199 
202  def get_Matrix( self, parameters = None ):
203 
204 
205  if parameters is None:
206  print( "get_Matrix: arary of input parameters is None")
207  return None
208 
209  return super().get_Matrix( parameters )
210 
211 
214  def set_Cost_Function_Variant( self, costfnc=0 ):
215 
216  # Set the optimizer
217  super().set_Cost_Function_Variant(costfnc=costfnc)
218 
219 
220 
221 
224  def set_Trace_Offset( self, trace_offset=0 ):
225 
226  # Set the trace offset
227  super().set_Trace_Offset(trace_offset=trace_offset)
228 
229 
230 
233  def get_Trace_Offset( self ):
234 
235  # Set the optimizer
236  return super().get_Trace_Offset()
237 
238 
239 
243 
244  return super().get_Optimized_Parameters()
245 
246 
247 
250  def set_Optimized_Parameters(self, new_params):
251 
252  super().set_Optimized_Parameters(new_params)
253 
254 
255 
258  def Optimization_Problem( self, parameters=None ):
259 
260  if parameters is None:
261  print( "Optimization_Problem: array of input parameters is None")
262  return None
263 
264  # evaluate the cost function and gradients
265  cost_function = super().Optimization_Problem(parameters)
266 
267 
268  return cost_function
269 
270 
271 
274  def Optimization_Problem_Grad( self, parameters=None ):
275 
276  if parameters is None:
277  print( "Optimization_Problem: array of input parameters is None")
278  return None
279 
280  # evaluate the cost function and gradients
281  grad = super().Optimization_Problem_Grad(parameters)
282 
283  grad = grad.reshape( (-1,))
284 
285  return grad
286 
287 
288 
291  def Optimization_Problem_Combined( self, parameters=None ):
292 
293  if parameters is None:
294  print( "Optimization_Problem_Combined: array of input parameters is None")
295  return None
296 
297  # evaluate the cost function and gradients
298  cost_function, grad = super().Optimization_Problem_Combined(parameters)
299 
300  grad = grad.reshape( (-1,))
301 
302  return cost_function, grad
303 
304 
306  def get_Num_of_Iters(self):
307 
308  return super().get_Num_of_Iters()
309 
310 
313  def set_Max_Iterations(self, max_iters):
314 
315  super().set_Max_Iterations(max_iters)
316 
317 
320  def set_Cost_Function_Variant(self, cost_func):
321 
322  super().set_Cost_Function_Variant(cost_func)
323 
324 
325 
329 
330  return super().get_Decomposition_Error()
331 
332 
333 
334 
340  def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None ):
341 
342  qbit_num = self.get_Qbit_Num()
343 
344  qubit_list_validated = list()
345  if isinstance(qubit_list, list) or isinstance(qubit_list, tuple):
346  for item in qubit_list:
347  if isinstance(item, int):
348  qubit_list_validated.append(item)
349  qubit_list_validated = list(set(qubit_list_validated))
350  else:
351  print("Elements of qbit_list should be integers")
352  return
353  elif qubit_list == None:
354  qubit_list_validated = [ x for x in range(qbit_num) ]
355 
356  else:
357  print("Elements of qbit_list should be integers")
358  return
359 
360 
361  if parameters is None:
362  print( "get_Second_Renyi_entropy: array of input parameters is None")
363  return None
364 
365 
366  if input_state is None:
367  matrix_size = 1 << qbit_num
368  input_state = np.zeros( (matrix_size,1) )
369  input_state[0] = 1
370 
371  # evaluate the entropy
372  entropy = super().get_Second_Renyi_Entropy( parameters, input_state, qubit_list_validated)
373 
374 
375  return entropy
376 
377 
378 
379 
382  def get_Qbit_Num(self):
383 
384  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.
def set_Optimized_Parameters(self, new_params)
Call to set the parameters which are used as a starting point in the optimization.
def List_Gates(self)
Call to print the gates decomposing the initial unitary.
def get_Optimized_Parameters(self)
Call to get the optimized parameters set in numpy array.
def get_Qiskit_Circuit(self)
Export the unitary decomposition into Qiskit format.
def Optimization_Problem_Combined(self, parameters=None)
Call to evaluate the cost function and the gradient components.
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 get_Decomposition_Error(self)
Call to get the error of the decomposition.
def get_Trace_Offset(self)
Call to get the trace offset used in the cost function.
def __init__(self, Umtx, level_limit_max=8, topology=None, config={}, accelerator_num=0)
Constructor of the class.
def export_Unitary(self, filename)
Call to export unitary matrix to binary file.
def Reorder_Qubits(self, qbit_list)
Call to reorder the qubits in the matrix of the gate.
def get_Qbit_Num(self)
Call to get the number of qubits in the circuit.
def set_Cost_Function_Variant(self, costfnc=0)
Call to set the optimizer used in the gate synthesis process.
def Start_Decomposition(self)
Wrapper function to call the start_decomposition method of C++ class.
def Optimization_Problem(self, parameters=None)
Call to evaluate the cost function.
def get_Project_Name(self)
Call to get the name of the SQUANDER project.
def apply_Global_Phase_Factor(self)
Call to apply global phase on Unitary matrix.
def set_Trace_Offset(self, trace_offset=0)
Call to set the trace offset used in the cost function.
def Optimization_Problem_Grad(self, parameters=None)
Call to evaluate the gradient components.
def set_Optimization_Tolerance(self, tolerance)
Call to set the error tolerance of the decomposition.
def get_Matrix(self, parameters=None)
Call to retrieve the unitary of the circuit.
def set_Project_Name(self, project_name_new)
Call to set the name of the SQUANDER project.
def set_Unitary(self, Umtx_arr)
Call to set unitary matrix from a numpy array.
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.
A QGD Python interface class for the decomposition of N-qubit unitaries into U3 and CNOT gates...