Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_N_Qubit_Decomposition_custom.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_custom_Wrapper import qgd_N_Qubit_Decomposition_custom_Wrapper
32 
33 
34 
35 
37 class qgd_N_Qubit_Decomposition_custom(qgd_N_Qubit_Decomposition_custom_Wrapper):
38 
39 
40 
46  def __init__( self, Umtx, initial_guess="RANDOM", config={}, accelerator_num=0 ):
47 
48 
49  self.qbit_num = int(round( np.log2( len(Umtx) ) ))
50 
51  # config
52  if not( type(config) is dict):
53  print("Input parameter config should be a dictionary describing the following hyperparameters:") #TODO
54  return
55 
56  # call the constructor of the wrapper class
57  super(qgd_N_Qubit_Decomposition_custom, self).__init__(Umtx, self.qbit_num, initial_guess, config=config, accelerator_num=accelerator_num)
58 
59 
60 
61 
64 
65  # call the C wrapper function
66  super(qgd_N_Qubit_Decomposition_custom, self).Start_Decomposition()
67 
68 
69 
72  def Reorder_Qubits( self, qbit_list ):
73 
74  # call the C wrapper function
75  super(qgd_N_Qubit_Decomposition_custom, self).Reorder_Qubits(qbit_list)
76 
77 
78 
80  def List_Gates(self):
81 
82  # call the C wrapper function
83  super(qgd_N_Qubit_Decomposition_custom, self).List_Gates()
84 
85 
86 
89  def get_Circuit( self ):
90 
91  # call the C wrapper function
92  return super().get_Circuit()
93 
94 
97  def get_Qiskit_Circuit( self ):
98 
99  from squander import Qiskit_IO
100 
101  squander_circuit = self.get_Circuit()
102  parameters = self.get_Optimized_Parameters()
103 
104  return Qiskit_IO.get_Qiskit_Circuit( squander_circuit, parameters )
105 
106 
110 
111  return super().get_Optimized_Parameters()
112 
113 
116  def Optimization_Problem( self, parameters=None ):
117 
118  if parameters is None:
119  print( "Optimization_Problem: array of input parameters is None")
120  return None
121 
122  # evaluate the cost function and gradients
123  cost_function = super().Optimization_Problem(parameters)
124 
125 
126  return cost_function
127 
128 
129 
130 
133  def get_Cirq_Circuit( self ):
134  #TODO: implement like for QISKIT
135  import cirq
136 
137 
138  # creating Cirq quantum circuit
139  circuit = cirq.Circuit()
140 
141  # creating qubit register
142  q = cirq.LineQubit.range(self.qbit_num)
143 
144  # retrive the list of decomposing gate structure
145  gates = self.get_Gates()
146 
147  # constructing quantum circuit
148  for idx in range(len(gates)-1, -1, -1):
149 
150  gate = gates[idx]
151 
152  if gate.get("type") == "CNOT":
153  # adding CNOT gate to the quantum circuit
154  circuit.append(cirq.CNOT(q[self.qbit_num-1-gate.get("control_qbit")], q[self.qbit_num-1-gate.get("target_qbit")]))
155 
156  if gate.get("type") == "CRY":
157  # adding CRY gate to the quantum circuit
158  print("CRY gate needs to be implemented")
159 
160  elif gate.get("type") == "CZ":
161  # adding CZ gate to the quantum circuit
162  circuit.append(cirq.CZ(q[self.qbit_num-1-gate.get("control_qbit")], q[self.qbit_num-1-gate.get("target_qbit")]))
163 
164  elif gate.get("type") == "CH":
165  # adding CZ gate to the quantum circuit
166  circuit.append(cirq.CH(q[self.qbit_num-1-gate.get("control_qbit")], q[self.qbit_num-1-gate.get("target_qbit")]))
167 
168  elif gate.get("type") == "SYC":
169  # Sycamore gate
170  circuit.append(cirq.google.SYC(q[self.qbit_num-1-gate.get("control_qbit")], q[self.qbit_num-1-gate.get("target_qbit")]))
171 
172  elif gate.get("type") == "U3":
173  print("Unsupported gate in the Cirq export: U3 gate")
174  return None;
175 
176  elif gate.get("type") == "RX":
177  # RX gate
178  circuit.append(cirq.rx(gate.get("Theta")).on(q[self.qbit_num-1-gate.get("target_qbit")]))
179 
180  elif gate.get("type") == "RY":
181  # RY gate
182  circuit.append(cirq.ry(gate.get("Theta")).on(q[self.qbit_num-1-gate.get("target_qbit")]))
183 
184  elif gate.get("type") == "RZ":
185  # RZ gate
186  circuit.append(cirq.rz(gate.get("Phi")).on(q[self.qbit_num-1-gate.get("target_qbit")]))
187 
188  elif gate.get("type") == "H":
189  # Hadamard gate
190  circuit.append(cirq.h(q[self.qbit_num-1-gate.get("target_qbit")]))
191 
192  elif gate.get("type") == "X":
193  # X gate
194  circuit.append(cirq.x(q[self.qbit_num-1-gate.get("target_qbit")]))
195 
196  elif gate.get("type") == "Y":
197  # Y gate
198  circuit.append(cirq.y(q[self.qbit_num-1-gate.get("target_qbit")]))
199 
200  elif gate.get("type") == "Z":
201  # Z gate
202  circuit.append(cirq.z(q[self.qbit_num-1-gate.get("target_qbit")]))
203 
204 
205  elif gate.get("type") == "SX":
206  # RZ gate
207  circuit.append(cirq.sx(q[self.qbit_num-1-gate.get("target_qbit")]))
208 
209 
210  return circuit
211 
212 
213 
216  def import_Qiskit_Circuit( self, qc_in ):
217 
218  from squander import Qiskit_IO
219 
220 
221  Circuit_Squander, circuit_parameters = Qiskit_IO.convert_Qiskit_to_Squander( qc_in )
222 
223  # setting gate structure and optimized initial parameters
224  self.set_Gate_Structure( Circuit_Squander )
225  self.set_Optimized_Parameters( circuit_parameters )
226 
227 
228 
229 
233  def set_Optimizer( self, optimizer="BFGS" ):
234 
235  # Set the optimizer
236  super(qgd_N_Qubit_Decomposition_custom, self).set_Optimizer(optimizer)
237 
238 
239 
240 
243 
244  # Set the optimizer
245  super(qgd_N_Qubit_Decomposition_custom, self).Prepare_Gates_To_Export()
246 
247 
248 
251  def set_Gate_Structure( self, Gate_structure ):
252 
253  from squander.gates.qgd_Circuit import qgd_Circuit
254 
255  if not isinstance(Gate_structure, qgd_Circuit) :
256  raise Exception("Input parameter Gate_structure should be a an instance of Gates_Block")
257 
258 
259  super(qgd_N_Qubit_Decomposition_custom, self).set_Gate_Structure( Gate_structure )
260 
261 
262 
263 
266  def set_Cost_Function_Variant( self, costfnc=0 ):
267 
268  # Set the optimizer
269  super(qgd_N_Qubit_Decomposition_custom, self).set_Cost_Function_Variant(costfnc=costfnc)
270 
def Optimization_Problem(self, parameters=None)
Call to evaluate the cost function.
def set_Cost_Function_Variant(self, costfnc=0)
Call to set the optimizer used in the gate synthesis process.
def Prepare_Gates_To_Export(self)
Call to prepare the circuit to be exported into Qiskit format.
def List_Gates(self)
Call to print the gates decomposing the initial unitary.
def Reorder_Qubits(self, qbit_list)
Call to reorder the qubits in the matrix of the gate.
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 get_Circuit(self)
Call to retrieve the incorporated quantum circuit (Squander format)
def set_Gate_Structure(self, Gate_structure)
Call to set custom gate structure to used in the decomposition.
def __init__(self, Umtx, initial_guess="RANDOM", config={}, accelerator_num=0)
Constructor of the class.
def get_Cirq_Circuit(self)
Export the unitary decomposition into Qiskit format.
def import_Qiskit_Circuit(self, qc_in)
Call to import initial quantum circuit in QISKIT format to be further comporessed.
A QGD Python interface class for the decomposition of N-qubit unitaries into U3 and CNOT gates...
def Start_Decomposition(self)
Wrapper function to call the start_decomposition method of C++ class N_Qubit_Decomposition.
def set_Optimizer(self, optimizer="BFGS")
Call to set the optimizer used in the gate synthesis process.