Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
QC_sim_benchmark.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 from squander import Circuit
27 
28 from squander.partitioning.partition import PartitionCircuit
29 
30 import numpy as np
31 import random
32 import scipy.linalg
33 import time
34 
35 
36 np.set_printoptions(linewidth=200)
37 
38 
39 # number of qubits
40 qbit_num_min = 4
41 qbit_num_max = 24
42 
43 # number of levels
44 levels = 4
45 
46 random_initial_state = True
47 
48 
50 
51 execution_times_squander = {}
52 transformed_states_squander = {}
53 parameters_squander = {}
54 initial_state_squander = {}
55 
56 for qbit_num in range(qbit_num_min, qbit_num_max+1, 1):
57 
58  # matrix size of the unitary
59  matrix_size = 1 << qbit_num #pow(2, qbit_num )
60 
61  if (random_initial_state ) :
62  initial_state_real = np.random.uniform(-1.0,1.0, (matrix_size,) )
63  initial_state_imag = np.random.uniform(-1.0,1.0, (matrix_size,) )
64  initial_state = initial_state_real + initial_state_imag*1j
65  initial_state = initial_state/np.linalg.norm(initial_state)
66  else:
67  initial_state = np.zeros( (matrix_size), dtype=np.complex128 )
68  initial_state[0] = 1.0 + 0j
69 
70  initial_state_squander[ qbit_num ] = initial_state.copy()
71 
72  # prepare circuit
73 
74  circuit_squander = Circuit( qbit_num )
75 
76  gates_num = 0
77  for level in range(levels):
78 
79  # preparing circuit
80  for control_qbit in range(qbit_num-1):
81  for target_qbit in range(control_qbit+1, qbit_num):
82 
83  circuit_squander.add_U3(target_qbit)
84  circuit_squander.add_U3(control_qbit)
85  #circuit_squander.add_CNOT( target_qbit=target_qbit, control_qbit=control_qbit )
86  circuit_squander.add_CRY( target_qbit=target_qbit, control_qbit=control_qbit )
87  gates_num = gates_num + 3
88 
89  for target_qbit in range(qbit_num):
90  circuit_squander.add_U3(target_qbit)
91  gates_num = gates_num + 1
92  break
93 
94 
95 
96  num_of_parameters = circuit_squander.get_Parameter_Num()
97  #print("The number of free parameters at qubit_num= ", qbit_num, ": ", num_of_parameters )
98 
99 
100  parameters = np.random.rand(num_of_parameters)*2*np.pi
101 
102  partitioned_circuit_squander, parameters_reordered, L = PartitionCircuit(circuit_squander, parameters, 5, "ilp-fusion")
103  #gate_dict = {i: gate for i, gate in enumerate(circuit_squander.get_Gates())}
104  #from squander.partitioning.tools import get_qubits, total_float_ops
105  #gate_to_qubit = { i: get_qubits(g) for i, g in gate_dict.items() }
106  #print(total_float_ops(partitioned_circuit_squander.get_Qbit_Num(), 4, gate_to_qubit, None, L))
107  partitioned_circuit_squander.set_min_fusion(14)
108 
109  t0 = time.time()
110  partitioned_circuit_squander.apply_to( parameters_reordered, initial_state )
111  t_SQUANDER = time.time() - t0
112  print( "Time elapsed SQUANDER: ", t_SQUANDER, " seconds at qbit_num = ", qbit_num, ' number of gates: ', gates_num )
113 
114  execution_times_squander[ qbit_num ] = t_SQUANDER
115  transformed_states_squander[ qbit_num ] = np.reshape(initial_state, (initial_state.size,) )
116  parameters_squander[ qbit_num ] = parameters
117 
118 
119 print("SQUANDER execution times [s]:")
120 print( execution_times_squander )
121 
122 
123 
125 
126 execution_times_qiskit = {}
127 transformed_states_qiskit = {}
128 
129 
130 import qiskit
131 qiskit_version = qiskit.version.get_version_info()
132 
133 from qiskit import QuantumCircuit
134 import qiskit_aer as Aer
135 
136 if qiskit_version[0] == '1' or qiskit_version[0] == '2':
137  from qiskit import transpile
138 elif qiskit_version[0] == '0':
139  from qiskit import execute
140 
141 
142 
143 
144 for qbit_num in range(qbit_num_min, qbit_num_max+1, 1):
145 
146  # matrix size of the unitary
147  matrix_size = 1 << qbit_num #pow(2, qbit_num )
148 
149  initial_state = initial_state_squander[ qbit_num ]
150 
151  parameters = parameters_squander[ qbit_num ]
152  parameter_idx = 0
153 
154  # prepare circuit
155 
156  # creating Qiskit quantum circuit
157  circuit_qiskit = QuantumCircuit(qbit_num)
158 
159  if random_initial_state:
160  circuit_qiskit.initialize( initial_state )
161 
162  for level in range(levels):
163 
164  # preparing circuit
165  for control_qbit in range(qbit_num-1):
166  for target_qbit in range(control_qbit+1, qbit_num):
167 
168  circuit_qiskit.u(parameters[parameter_idx]*2, parameters[parameter_idx+1], parameters[parameter_idx+2], target_qbit )
169  parameter_idx = parameter_idx+3
170  circuit_qiskit.u(parameters[parameter_idx]*2, parameters[parameter_idx+1], parameters[parameter_idx+2], control_qbit )
171  parameter_idx = parameter_idx+3
172  #circuit_qiskit.cx( control_qbit, target_qbit )
173  circuit_qiskit.cry( parameters[parameter_idx]*2, control_qbit, target_qbit )
174  parameter_idx = parameter_idx+1
175 
176  for target_qbit in range(qbit_num):
177  circuit_qiskit.u(parameters[parameter_idx]*2, parameters[parameter_idx+1], parameters[parameter_idx+2], target_qbit )
178  parameter_idx = parameter_idx+3
179  break
180 
181 
182 
183 
184 
185  t0 = time.time()
186 
187  # Execute and get the state vector
188  if qiskit_version[0] == '1' or qiskit_version[0] == '2':
189 
190  circuit_qiskit.save_statevector()
191 
192  backend = Aer.AerSimulator(method='statevector')
193  compiled_circuit = transpile(circuit_qiskit, backend)
194  result = backend.run(compiled_circuit).result()
195 
196  transformed_state = result.get_statevector(compiled_circuit)
197 
198 
199  elif qiskit_version[0] == '0':
200 
201  # Select the StatevectorSimulator from the Aer provider
202  simulator = Aer.get_backend('statevector_simulator')
203 
204  backend = Aer.get_backend('aer_simulator')
205  result = execute(circuit_qiskit, simulator).result()
206 
207  transformed_state = result.get_statevector(circuit_qiskit)
208 
209 
210 
211  t_qiskit = time.time() - t0
212  #print( "Time elapsed QISKIT: ", t_qiskit, " at qbit_num = ", qbit_num )
213 
214  execution_times_qiskit[ qbit_num ] = t_qiskit
215  transformed_states_qiskit[ qbit_num ] = np.array(transformed_state)
216 
217 print("QISKIT execution times [s]:")
218 print( execution_times_qiskit )
219 
220 
221 from qulacs import Observable, QuantumCircuit, QuantumState
222 import qulacs
223 
224 execution_times_qulacs = {}
225 transformed_states_qulacs = {}
226 
227 
228 for qbit_num in range(qbit_num_min, qbit_num_max+1, 1):
229 
230  # matrix size of the unitary
231  matrix_size = 1 << qbit_num #pow(2, qbit_num )
232 
233  initial_state = initial_state_squander[ qbit_num ]
234 
235  parameters = parameters_squander[ qbit_num ]
236  parameter_idx = 0
237 
238  # prepare circuit
239 
240  # creating qulacs quantum circuit
241  state = QuantumState(qbit_num)
242  state.load( initial_state )
243 
244  circuit_qulacs = QuantumCircuit(qbit_num)
245 
246  for level in range(levels):
247 
248  # preparing circuit
249  for control_qbit in range(qbit_num-1):
250  for target_qbit in range(control_qbit+1, qbit_num):
251 
252  circuit_qulacs.add_U3_gate(target_qbit, parameters[parameter_idx]*2, parameters[parameter_idx+1], parameters[parameter_idx+2] )
253  parameter_idx = parameter_idx+3
254  circuit_qulacs.add_U3_gate( control_qbit, parameters[parameter_idx]*2, parameters[parameter_idx+1], parameters[parameter_idx+2] )
255  parameter_idx = parameter_idx+3
256 
257  #circuit_qulacs.add_CNOT_gate( control_qbit, target_qbit )
258 
259  RY_gate = qulacs.gate.RotY( target_qbit, parameters[parameter_idx]*2 )
260  RY_gate = qulacs.gate.to_matrix_gate( RY_gate )
261  RY_gate.add_control_qubit( control_qbit, 1)
262  circuit_qulacs.add_gate( RY_gate )
263  #circuit_qulacs.add_RotY_gate( target_qbit, parameters[parameter_idx]*2 )
264  parameter_idx = parameter_idx+1
265 
266 
267  for target_qbit in range(qbit_num):
268  circuit_qulacs.add_U3_gate( target_qbit, parameters[parameter_idx]*2, parameters[parameter_idx+1], parameters[parameter_idx+2] )
269  parameter_idx = parameter_idx+3
270  break
271 
272 
273  t0 = time.time()
274  # Execute and get the state vector
275  circuit_qulacs.update_quantum_state( state )
276  transformed_state = state.get_vector()
277  t_qulacs = time.time() - t0
278  #print( "Time elapsed qulacs: ", t_qulacs, " at qbit_num = ", qbit_num )
279 
280  execution_times_qulacs[ qbit_num ] = t_qulacs
281  transformed_states_qulacs[ qbit_num ] = np.array(transformed_state)
282 
283 print("Qulacs execution times [s]:")
284 print( execution_times_qulacs )
285 # check errors
286 
287 print(' ')
288 print("Difference between the transformed state vectors:")
289 # SQUANDER-QISKIT-Qulacs comparision
290 keys = transformed_states_qiskit.keys()
291 for qbit_num in keys:
292  state_squander = transformed_states_squander[ qbit_num ]
293  state_qiskit = transformed_states_qiskit[ qbit_num ]
294  state_qulacs = transformed_states_qulacs[ qbit_num ]
295 
296  print( "Squander vs QISKIT: ", np.linalg.norm( state_squander-state_qiskit ) )
297  print( "Squander vs Qulacs: ", np.linalg.norm( state_squander-state_qulacs ) )
298 
def PartitionCircuit
Definition: partition.py:51