Quantum Gate Decomposer  v1.3
Powerful decomposition of almost any unitary into U3 and CNOT gates
Random_Unitary.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright (C) 2020 Peter Rakyta, Ph.D.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see http://www.gnu.org/licenses/.
17 
18 @author: Peter Rakyta, Ph.D.
19 */
25 #include "qgd/Random_Unitary.h"
26 
27 
28 
35 Matrix
36 few_CNOT_unitary( int qbit_num, int cnot_num) {
37 
38  // the current number of CNOT gates
39  int cnot_num_curr = 0;
40 
41  // the size of the matrix
43 
44  // The unitary describing each qubits in their initial state
46 
47  // constructing the unitary
48  while (true) {
49  int cnot_or_u3 = rand() % 5 + 1;
50 
51  CNOT* cnot_op = NULL;
52  U3* u3_op = NULL;
53 
54  Matrix gate_matrix;
55 
56  if (cnot_or_u3 <= 4) {
57  // creating random parameters for the U3 operation
58  double parameters[3];
59 
60  parameters[0] = double(rand())/RAND_MAX*4*M_PI;
61  parameters[1] = double(rand())/RAND_MAX*2*M_PI;
62  parameters[2] = double(rand())/RAND_MAX*2*M_PI;
63 
64 
65  // randomly choose the target qbit
66  int target_qbit = rand() % qbit_num;
67 
68  // creating the U3 gate
69  u3_op = new U3(qbit_num, target_qbit, true, true, true);
70 
71  // get the matrix of the operation
72  gate_matrix = u3_op->get_matrix(parameters);
73  }
74  else if ( cnot_or_u3 == 5 ) {
75  // randomly choose the target qbit
76  int target_qbit = rand() % qbit_num;
77 
78  // randomly choose the control qbit
79  int control_qbit = rand() % qbit_num;
80 
81  if (target_qbit == control_qbit) {
82  gate_matrix = create_identity( matrix_size );
83  }
84  else {
85 
86  // creating the CNOT gate
87  cnot_op = new CNOT(qbit_num, control_qbit, target_qbit);
88 
89  // get the matrix of the operation
90  gate_matrix = cnot_op->get_matrix();
91 
92  cnot_num_curr = cnot_num_curr + 1;
93  }
94  }
95  else {
96  gate_matrix = create_identity( matrix_size );
97  }
98 
99 
100  // get the current unitary
101  Matrix mtx_tmp = dot(gate_matrix, mtx);
102  mtx = mtx_tmp;
103 
104  delete u3_op;
105  u3_op = NULL;
106 
107  delete cnot_op;
108  cnot_op = NULL;
109 
110  // exit the loop if the maximal number of CNOT gates reached
111  if (cnot_num_curr >= cnot_num) {
112  return mtx;
113  }
114 
115  }
116 
117 }
118 
119 
126 
127  if (dim_in < 2) {
128  throw "wrong dimension";
129  }
130 
131  // number of qubits
132  dim = dim_in;
133 
134 }
135 
136 
141 Matrix
143 
144  // create array of random parameters to construct random unitary
145  double* vartheta = (double*) qgd_calloc( int(dim*(dim-1)/2),sizeof(double), 64);
146  double* varphi = (double*) qgd_calloc( int(dim*(dim-1)/2),sizeof(double), 64);
147  double* varkappa = (double*) qgd_calloc( (dim-1),sizeof(double), 64);
148 
149  // initialize random seed:
150  srand (time(NULL));
151 
152  for (int idx=0; idx<dim*(dim-1)/2; idx++) {
153  vartheta[idx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;
154  }
155 
156 
157  for (int idx=0; idx<dim*(dim-1)/2; idx++) {
158  varphi[idx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;
159  }
160 
161 
162  for (int idx=0; idx<(dim-1); idx++) {
163  varkappa[idx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;
164  }
165 
166  Matrix Umtx = Construct_Unitary_Matrix( vartheta, varphi, varkappa );
167 
168  qgd_free(vartheta);
169  qgd_free(varphi);
170  qgd_free(varkappa);
171  vartheta = NULL;
172  varphi = NULL;
173  varkappa = NULL;
174 
175  return Umtx;
176 
177 }
178 
179 
187 Matrix
188 Random_Unitary::Construct_Unitary_Matrix( double* vartheta, double* varphi, double* varkappa ) {
189 
190 
191  Matrix ret = create_identity(dim);
192 
193  for (int varalpha=1; varalpha<dim; varalpha++) { // = 2:obj.dim
194  for (int varbeta = 0; varbeta<varalpha; varbeta++) {// 1:varalpha-1
195 
196  double theta_loc = vartheta[ convert_indexes(varalpha, varbeta) ];
197  double phi_loc = varphi[ convert_indexes(varalpha, varbeta) ];
198 
199 
200  // Eq (26)
201  QGD_Complex16 a;
202  a.real = cos( theta_loc )*cos(phi_loc);
203  a.imag = cos( theta_loc )*sin(phi_loc);
204 
205  // Eq (28) and (26)
206  double varepsilon = varkappa[varalpha-1]*kronecker( varalpha-1, varbeta);
207  QGD_Complex16 b;
208  b.real = sin( theta_loc )*cos(varepsilon);
209  b.imag = sin( theta_loc )*sin(varepsilon);
210 
211  a.real = -a.real;
212  b.imag = -b.imag;
213  Matrix Omega_loc = Omega( varalpha, varbeta, a, b );
214  Matrix ret_tmp = dot( ret, Omega_loc); // ret * Omega_loc
215 
216  ret = ret_tmp;
217  }
218  }
219 
220 
221  QGD_Complex16 gamma_loc;
222  gamma_loc.real = gamma();
223  gamma_loc.imag = 0;
224 
225  for ( int idx=0; idx<dim*dim; idx++) {
226  ret[idx] = mult(ret[idx], gamma_loc);
227  }
228 
229  return ret;
230 
231 
232 
233 }
234 
235 
242 int
243 Random_Unitary::convert_indexes( int varalpha, int varbeta ) {
244  int ret = varbeta + (varalpha-1)*(varalpha-2)/2;
245  return ret;
246 }
247 
248 
255  return Construct_Unitary_Matrix( parameters, parameters+int(dim*(dim-1)/2), parameters+int(dim*(dim-1)));
256 }
257 
258 
267 Matrix
268 Random_Unitary::Omega(int varalpha, int varbeta, QGD_Complex16 x, QGD_Complex16 y ) {
269 
270  Matrix ret = I_alpha_beta( varalpha, varbeta );
271 
272 
273  Matrix Mloc;
274 
275  if (varalpha + varbeta != (3 + kronecker( dim, 2 )) ) {
276  Mloc = M( varalpha, varbeta, x, y );
277 
278  }
279  else {
280  y.imag = -y.imag;
281  Mloc = M( varalpha, varbeta, x, mult(gamma(), y) );
282  }
283 
284 
285  //#pragma omp parallel for
286  for ( int idx=0; idx<dim*dim; idx++ ) {
287  ret[idx].real = ret[idx].real + Mloc[idx].real;
288  ret[idx].imag = ret[idx].imag + Mloc[idx].imag;
289  }
290 
291  return ret;
292 
293 
294 }
295 
296 
305 Matrix
306 Random_Unitary::M( int varalpha, int varbeta, QGD_Complex16 s, QGD_Complex16 t ) {
307 
308  Matrix Qloc = Q( s, t);
309 
310  Matrix ret1 = E_alpha_beta( varbeta, varbeta );
311  Matrix ret2 = E_alpha_beta( varbeta, varalpha );
312  Matrix ret3 = E_alpha_beta( varalpha, varbeta );
313  Matrix ret4 = E_alpha_beta( varalpha, varalpha );
314 
315 
316  mult(Qloc[0], ret1);
317  mult(Qloc[1], ret2);
318  mult(Qloc[2], ret3);
319  mult(Qloc[3], ret4);
320 
321  Matrix ret = Matrix(dim, dim);
322 
323  for ( int idx=0; idx<dim*dim; idx++ ) {
324  ret[idx].real = ret1[idx].real + ret2[idx].real + ret3[idx].real + ret4[idx].real;
325  ret[idx].imag = ret1[idx].imag + ret2[idx].imag + ret3[idx].imag + ret4[idx].imag;
326  }
327 
328  return ret;
329 
330 }
331 
332 
339 Matrix
341 
342  Matrix ret = Matrix(2, 2);
343  ret[0] = u2;
344  ret[1] = u1;
345  ret[2].real = -u1.real;
346  ret[2].imag = u1.imag;
347  ret[3].real = u2.real;
348  ret[3].imag = -u2.imag;
349 
350  return ret;
351 
352 }
353 
354 
361 Matrix
362 Random_Unitary::E_alpha_beta( int varalpha, int varbeta ) {
363 
364  Matrix ret = Matrix(dim, dim);
365  memset( ret.get_data(), 0, dim*dim*sizeof(QGD_Complex16));
366  ret[varalpha*dim+varbeta].real = 1;
367 
368  return ret;
369 
370 }
371 
378 Matrix
379 Random_Unitary::I_alpha_beta( int varalpha, int varbeta ) {
380 
381 
382  Matrix ret = create_identity(dim);
383 
384  ret[varalpha*dim+varalpha].real = 0;
385  ret[varbeta*dim+varbeta].real = 0;
386 
387  return ret;
388 
389 }
390 
391 
396 double
398 
399  double ret = pow(-1, 0.25*(2*dim-1+pow(-1,dim)));//(-1)^(0.25*(2*dim-1+(-1)^dim));
400  return ret;
401 
402 }
403 
410 double
411 Random_Unitary::kronecker( int a, int b ) {
412 
413  if (a == b) {
414  return 1;
415  }
416  else {
417  return 0;
418  }
419 
420 }
421 
422 
423 
424 
425 
426 
Matrix dot(Matrix &A, Matrix &B)
Call to calculate the product of two complex matrices by calling method zgemm3m from the CBLAS librar...
Definition: dot.cpp:20
A class representing a U3 operation.
Definition: U3.h:35
Matrix few_CNOT_unitary(int qbit_num, int cnot_num)
Call to create a random unitary constructed by CNOT operation between randomly chosen qubits and by r...
void * qgd_calloc(size_t element_num, size_t size, size_t alignment)
custom defined memory allocation function.
Definition: common.cpp:38
Header file for a class and methods to cerate random unitary matrices.
void qgd_free(void *ptr)
custom defined memory release function.
Definition: common.cpp:66
scalar * get_data()
Call to get the pointer to the stored data.
Definition: matrix_base.h:221
int convert_indexes(int varalpha, int varbeta)
Calculates an index from paramaters varalpha and varbeta.
QGD_Complex16 mult(QGD_Complex16 a, QGD_Complex16 b)
Call to calculate the product of two complex scalars.
Definition: common.cpp:367
double kronecker(int a, int b)
Kronecker delta.
int dim
The number of rows in the created unitary.
double gamma()
Implements Eq (11) of arXiv:1303:5904v1.
matrix_size
Definition: example.py:41
Matrix E_alpha_beta(int varalpha, int varbeta)
Implements matrix I below Eq (7) of arXiv:1303:5904v1.
Umtx
The unitary to be decomposed.
Definition: example.py:44
Matrix M(int varalpha, int varbeta, QGD_Complex16 s, QGD_Complex16 t)
Implements Eq (8) of arXiv:1303:5904v1.
Structure type representing complex numbers in the QGD package.
Definition: QGDTypes.h:39
A class representing a CNOT operation.
Definition: CNOT.h:36
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:81
Class to store data of complex arrays and its properties.
Definition: matrix.h:12
int qbit_num
Definition: example.py:38
Random_Unitary(int dim_in)
Constructor of the class.
Matrix Q(QGD_Complex16 u1, QGD_Complex16 u2)
Implements Eq (9) of arXiv:1303:5904v1.
Matrix I_alpha_beta(int varalpha, int varbeta)
Implements matrix I below Eq (7) of arXiv:1303:5904v1.
Matrix create_identity(int matrix_size)
Call to create an identity matrix.
Definition: common.cpp:128
Matrix get_matrix(const double *parameters)
Call to retrieve the operation matrix.
Definition: U3.cpp:138
double real
the real part of a complex number
Definition: QGDTypes.h:41
Matrix Omega(int varalpha, int varbeta, QGD_Complex16 x, QGD_Complex16 y)
Eq (6) of arXiv:1303:5904v1.
Matrix get_matrix()
Call to retrieve the operation matrix.
Definition: CNOT.cpp:79
Matrix Construct_Unitary_Matrix()
Call to create a random unitary.
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:43