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