Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
test_fmo.py
Go to the documentation of this file.
1 '''
2 Copyright 2020 Peter Rakyta, Ph.D.
3 
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see http://www.gnu.org/licenses/.
18 
19 '''
20 
21 import numpy as np
22 import scipy.constants as cnst
23 import scipy.linalg as linalg
24 
25 from squander import N_Qubit_Decomposition
26 from squander import N_Qubit_Decomposition_custom
27 
28 from qiskit import QuantumCircuit
29 from qiskit.visualization import plot_histogram
30 from squander import utils
31 
32 try:
33  from mpi4py import MPI
34  MPI_imported = True
35 except ModuleNotFoundError:
36  MPI_imported = False
37 
38 
40  """This is a test class of the python iterface to the decompsition classes of the QGD package"""
41 
42 
44  """
45  Add layers to disentangle the 3rd qubit from the others
46  linear chain with IBM native operations
47 
48  """
49 
50  from squander import Circuit
51 
52  qbit_num = 3
53 
54  # creating an instance of the wrapper class Circuit
55  Circuit_ret = Circuit( qbit_num )
56 
57 
58  for idx in range(0,layer_num,2):
59 
60  # creating an instance of the wrapper class Circuit
61  Layer = Circuit( qbit_num )
62 
63 #Rz Ry Rz = Rz Sx Rz Sx^+ Rz
64  #Layer.add_U3(2, True, True, True)
65  Layer.add_RZ( 2 )
66  Layer.add_SX( 2 )
67  Layer.add_RZ( 2 )
68  Layer.add_X( 2 )
69  Layer.add_SX( 2 )
70  Layer.add_RZ( 2 )
71 
72  #Layer.add_U3(1, True, True, True)
73  Layer.add_RZ( 1 )
74  #Layer.add_RY( 1 )
75  Layer.add_SX( 1 )
76  Layer.add_RZ( 1 )
77  Layer.add_X( 1 )
78  Layer.add_SX( 1 )
79  Layer.add_RZ( 1 )
80 
81  # add CNOT gate to the block
82  Layer.add_CNOT( 1, 2)
83 
84  Circuit_ret.add_Circuit( Layer )
85 
86 
87  # creating an instance of the wrapper class Circuit
88  Layer = Circuit( qbit_num )
89 
90  #Layer.add_U3(1, True, True, True)
91  Layer.add_RZ( 1 )
92  #Layer.add_RY( 1 )
93  Layer.add_SX( 1 )
94  Layer.add_RZ( 1 )
95  Layer.add_X( 1 )
96  Layer.add_SX( 1 )
97  Layer.add_RZ( 1 )
98 
99  #Layer.add_U3(0, True, True, True)
100  Layer.add_RZ( 0 )
101  #Layer.add_RY( 0 )
102  Layer.add_SX( 0 )
103  Layer.add_RZ( 0 )
104  Layer.add_X( 0 )
105  Layer.add_SX( 0 )
106  Layer.add_RZ( 0 )
107 
108  # add CNOT gate to the block
109  Layer.add_CNOT( 0, 1)
110 
111  Circuit_ret.add_Circuit( Layer )
112 
113 
114 
115  return Circuit_ret
116 
117 
118 
119  def create_custom_gate_structure_lin_2qubit(self, layer_num=1, Circuit_ret=None):
120  """
121  Add layers to disentangle the final 2 qubits
122  linear chain with IBM native operations
123 
124  """
125 
126  from squander import Circuit
127 
128 
129  qbit_num = 2
130 
131  if Circuit_ret == None:
132  # creating an instance of the wrapper class Circuit
133  Circuit_ret = Circuit( qbit_num )
134 
135 
136  for idx in range(layer_num):
137 
138  # creating an instance of the wrapper class Circuit
139  Layer = Circuit( qbit_num )
140 
141  #Layer.add_U3(1, True, True, True)
142  Layer.add_RZ( 1 )
143  #Layer.add_RY( 1 )
144  Layer.add_SX( 1 )
145  Layer.add_RZ( 1 )
146  Layer.add_X( 1 )
147  Layer.add_SX( 1 )
148  Layer.add_RZ( 1 )
149 
150  #Layer.add_U3(0, True, True, True)
151  Layer.add_RZ( 0 )
152  #Layer.add_RY( 0 )
153  Layer.add_SX( 0 )
154  Layer.add_RZ( 0 )
155  Layer.add_X( 0 )
156  Layer.add_SX( 0 )
157  Layer.add_RZ( 0 )
158 
159  # add CNOT gate to the block
160  Layer.add_CNOT( 0, 1)
161 
162  Circuit_ret.add_Circuit( Layer )
163 
164 
165 
166  return Circuit_ret
167 
168 
169  def create_custom_gate_structure_finalyzing(self, qbit_num, Circuit_ret=None):
170  """
171  Rotating qubits into state |0>
172 
173  """
174 
175  from squander import Circuit
176 
177 
178 
179  if Circuit_ret == None:
180  # creating an instance of the wrapper class Circuit
181  Circuit_ret = Circuit( qbit_num )
182 
183 
184  for idx in range(qbit_num):
185 
186  # creating an instance of the wrapper class Circuit
187  Layer = Circuit( qbit_num )
188 
189  #Layer.add_U3(idx, True, True, True)
190  Layer.add_RZ( idx )
191  #Layer.add_RY( idx )
192  Layer.add_SX( idx )
193  Layer.add_RZ( idx )
194  Layer.add_X( idx )
195  Layer.add_SX( idx )
196  Layer.add_RZ( idx )
197 
198  Circuit_ret.add_Circuit( Layer )
199 
200 
201 
202  return Circuit_ret
203 
204 
205 
206 
207  def get_Unitary(self, H): # H is the Hamiltonian to be exponentiated, t is the timestamp
208  X = -(2j)*cnst.pi*H #the Hamiltonian is in 1/cm
209  U = linalg.expm(X)
210  U = np.hstack((U,np.zeros((7,1))))
211  U = np.vstack((U,np.zeros((1,8))))
212  U[-1][-1] = 1
213  #print( np.dot(U, U.conj().T))
214  return U
215 
216 
217 
219 
220  # the number of qubits spanning the unitary
221  qbit_num = 3
222 
223  # determine the soze of the unitary to be decomposed
224  matrix_size = int(2**qbit_num)
225 
226  # creating my unitary to be decomposed
227  # first is the hamiltonian
228  H = np.array(
229  [[240, -87.7, 5.5, -5.9, 6.7, -13.7, -9.9],
230  [-87.7, 315, 30.8, 8.2, 0.7, 11.8, 4.3],
231  [ 5.5, 30.8, 0, -53.5, -2.2, -9.6, 6.0],
232  [-5.9, 8.2, -53.5, 130, -70.7, -17.0, -63.3],
233  [ 6.7, 0.7, -2.2, -70.7, 285, 81.1, -1.3],
234  [-13.7, 11.8, -9.6, -17.0, 81.1, 435, 39.7],
235  [-9.9, 4.3, 6.0, -63.3, -1.3, 39.7, 245]])
236 
237 
238  Umtx = self.get_Unitary(H)
239 
240  # create custom gate structure for the decomposition
241  gate_structure = self.create_custom_gate_structure_lin_3qubit(14)
242  gate_structure = self.create_custom_gate_structure_lin_2qubit(3, gate_structure)
243  gate_structure = self.create_custom_gate_structure_finalyzing(qbit_num, gate_structure)
244 
245 
246 
247  #Python map containing hyper-parameters
248  config = { 'max_outer_iterations': 1,
249  'max_inner_iterations' : 500,
250  'optimization_tolerance': 1e-8,
251  'agent_num': 20}
252 
253 
254 
255  # creating an instance of the C++ class
256  decomp = N_Qubit_Decomposition_custom( Umtx.conj().T, initial_guess="zeros", config=config )
257 
258  # adding custom gate structure to the decomposition
259  decomp.set_Gate_Structure( gate_structure )
260 
261 
262  # setting the tolerance of the optimization process. The final error of the decomposition would scale with the square root of this value.
263  decomp.set_Optimization_Tolerance( 1e-6 )
264 
265  # set the number of block to be optimized in one shot
266  decomp.set_Optimization_Blocks( 50 )
267 
268  decomp.set_Cost_Function_Variant( 3 )
269 
270  decomp.set_Optimizer("BFGS")
271 
272  decomp.set_Verbose(3)
273 
274  # starting the decomposition
275  decomp.Start_Decomposition()
276 
277 
278 
279 
280  # list the decomposing operations
281  #decomp.List_Gates()
282 
283 
284  # get the decomposing operations
285  quantum_circuit = decomp.get_Qiskit_Circuit()
286  #print(quantum_circuit )
287 
288  # test the decomposition of the matrix
289  # the unitary matrix from the result object
290  decomposed_matrix = np.asarray( utils.get_unitary_from_qiskit_circuit( quantum_circuit ) )
291  product_matrix = np.dot(Umtx,decomposed_matrix.conj().T)
292  phase = np.angle(product_matrix[0,0])
293  product_matrix = product_matrix*np.exp(-1j*phase)
294 
295  product_matrix = np.eye(matrix_size)*2 - product_matrix - product_matrix.conj().T
296  # the error of the decomposition
297  decomposition_error = (np.real(np.trace(product_matrix)))/2
298 
299  print('The error of the decomposition is ' + str(decomposition_error))
300 
301  assert( decomposition_error < 1e-3 )
302 
303 
304 
305 
306 
307 
308 
309 
310 
311 
312 
313 
314 
315 
316 
317 
318 
319 
320 
321 
def create_custom_gate_structure_lin_3qubit(self, layer_num=2)
Definition: test_fmo.py:43
def create_custom_gate_structure_lin_2qubit(self, layer_num=1, Circuit_ret=None)
Definition: test_fmo.py:119
def create_custom_gate_structure_finalyzing(self, qbit_num, Circuit_ret=None)
Definition: test_fmo.py:169