Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
test_QX2.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """
3 Created on Fri Jun 26 14:42:56 2020
4 Copyright 2020 Peter Rakyta, Ph.D.
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9 
10  http://www.apache.org/licenses/LICENSE-2.0
11 
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see http://www.gnu.org/licenses/.
20 
21 @author: Peter Rakyta, Ph.D.
22 """
23 
25 
26 
27 
28 # cerate unitary q-bit matrix
29 from scipy.stats import unitary_group
30 import numpy as np
31 from squander import utils
32 
33 
34 try:
35  from mpi4py import MPI
36  MPI_imported = True
37 except ModuleNotFoundError:
38  MPI_imported = False
39 
40 
41 
42 
44  """This is a test class of the python iterface to the decompsition classes of the QGD package"""
45 
46 
47 
48 
49 
51  r"""
52  This method is called by pytest.
53  Test to define custom gate structure in the decomposition
54 
55  """
56 
57  from squander import N_Qubit_Decomposition
58 
59  # the number of qubits spanning the unitary
60  qbit_num = 4
61 
62  # determine the soze of the unitary to be decomposed
63  matrix_size = int(2**qbit_num)
64 
65  # creating a random unitary to be decomposed
66  Umtx = unitary_group.rvs(matrix_size)
67 
68  # creating an instance of the C++ class
69  decomp = N_Qubit_Decomposition( Umtx.conj().T )
70 
71  # list of reordered qubits (original: (0,1,2,3) )
72  reordered_qbits = (2,3,1,0)
73 
74  # adding custom gate structure to the decomposition
75  decomp.Reorder_Qubits( reordered_qbits )
76 
77 
78  # create custom gate structure
79  gate_structure = { 4: self.create_custom_gate_structure_QX2(4), 3: self.create_custom_gate_structure_QX2(3)}
80 
81 
82  # adding custom gate structure to the decomposition
83  decomp.set_Gate_Structure( gate_structure )
84 
85 
86  # set the maximal number of layers in the decomposition
87  decomp.set_Max_Layer_Num( {4: 60, 3:16} )
88 
89  # set the number of block to be optimized in one shot
90  decomp.set_Optimization_Blocks( 20 )
91 
92  # starting the decomposition
93  decomp.Start_Decomposition()
94 
95  # list of reordered qubits to revert the initial order
96  revert_qbits = (3,2,0,1)
97 
98  # adding custom gate structure to the decomposition
99  decomp.Reorder_Qubits( revert_qbits )
100 
101  # list the decomposing operations
102  decomp.List_Gates()
103 
104  # get the decomposing operations
105  quantum_circuit = decomp.get_Qiskit_Circuit()
106 
107  import numpy.linalg as LA
108 
109  # test the decomposition of the matrix
110  # the unitary matrix from the result object
111  decomposed_matrix = np.asarray( utils.get_unitary_from_qiskit_circuit( quantum_circuit ) )
112  product_matrix = np.dot(Umtx,decomposed_matrix.conj().T)
113  phase = np.angle(product_matrix[0,0])
114  product_matrix = product_matrix*np.exp(-1j*phase)
115 
116  product_matrix = np.eye(matrix_size)*2 - product_matrix - product_matrix.conj().T
117  # the error of the decomposition
118  decomposition_error = (np.real(np.trace(product_matrix)))/2
119 
120  print('The error of the decomposition is ' + str(decomposition_error))
121 
122  assert( decomposition_error < 1e-3 )
123 
124 
125  def create_custom_gate_structure_QX2(self, qbit_num):
126  r"""
127  This method is called to create custom gate structure for the decomposition on IBM QX2
128 
129  """
130 
131  from squander import Circuit
132 
133  # creating an instance of the wrapper class Circuit
134  Circuit_ret = Circuit( qbit_num )
135 
136  disentangle_qbit = qbit_num - 1
137 
138 
139 
140  for qbit in range(0, disentangle_qbit ):
141 
142  # creating an instance of the wrapper class Circuit
143  Layer = Circuit( qbit_num )
144 
145 
146  if qbit == 0:
147 
148  # add U3 fate to the block
149  Layer.add_U3( 0 )
150  Layer.add_U3( disentangle_qbit )
151 
152  # add CNOT gate to the block
153  Layer.add_CNOT( 0, disentangle_qbit)
154 
155  elif qbit == 1:
156 
157  # add U3 fate to the block
158  Layer.add_U3( 0 )
159  Layer.add_U3( 1 )
160 
161  # add CNOT gate to the block
162  Layer.add_CNOT( 0, 1)
163 
164 
165 
166  elif qbit == 2:
167 
168  # add U3 fate to the block
169  Layer.add_U3( 2 )
170  Layer.add_U3( disentangle_qbit )
171 
172  # add CNOT gate to the block
173  Layer.add_CNOT( 2, disentangle_qbit )
174 
175 
176 
177  Circuit_ret.add_Circuit( Layer )
178 
179  return Circuit_ret
180 
181 
182 
183 
def create_custom_gate_structure_QX2(self, qbit_num)
Definition: test_QX2.py:125
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...