Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
N_Qubit_Decomposition_Cost_Function.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 @author: Peter Rakyta, Ph.D.
18 */
24 //#include <tbb/parallel_for.h>
25 
26 
27 
34 double get_cost_function(Matrix matrix, int trace_offset) {
35 
36  int matrix_size = matrix.cols ;
37 /*
38  tbb::combinable<double> priv_partial_cost_functions{[](){return 0;}};
39  tbb::parallel_for( tbb::blocked_range<int>(0, matrix_size, 1), functor_cost_fnc( matrix, &priv_partial_cost_functions ));
40 */
41 /*
42  //sequential version
43  functor_cost_fnc tmp = functor_cost_fnc( matrix, matrix_size, partial_cost_functions, matrix_size );
44  #pragma omp parallel for
45  for (int idx=0; idx<matrix_size; idx++) {
46  tmp(idx);
47  }
48 */
49 /*
50  // calculate the final cost function
51  double cost_function = 0;
52  priv_partial_cost_functions.combine_each([&cost_function](double a) {
53  cost_function = cost_function + a;
54  });
55 */
56 
57 /*
58 #ifdef USE_AVX
59 
60 
61  __m128d trace_128 = _mm_setr_pd(0.0, 0.0);
62  double* matrix_data = (double*)matrix.get_data();
63  int offset = 2*(matrix.stride+1);
64 
65  for (int idx=0; idx<matrix_size; idx++) {
66 
67  // get the diagonal element
68  __m128d element_128 = _mm_load_pd(matrix_data);
69 
70  // add the diagonal elements to the trace
71  trace_128 = _mm_add_pd(trace_128, element_128);
72 
73  matrix_data = matrix_data + offset;
74  }
75 
76 
77  trace_128 = _mm_mul_pd(trace_128, trace_128);
78  double cost_function = std::sqrt(1.0 - (trace_128[0] + trace_128[1])/(matrix_size*matrix_size));
79 
80 #else
81 
82  QGD_Complex16 trace;
83  memset( &trace, 0.0, 2*sizeof(double) );
84  //trace.real = 0.0;
85  //trace.imag = 0.0;
86 
87  for (int idx=0; idx<matrix_size; idx++) {
88 
89  trace.real += matrix[idx*matrix.stride + idx].real;
90  trace.imag += matrix[idx*matrix.stride + idx].imag;
91  }
92 
93  double cost_function = std::sqrt(1.0 - (trace.real*trace.real + trace.imag*trace.imag)/(matrix_size*matrix_size));
94 #endif
95 */
96 
97 
98  double trace_real = 0.0;
99 
100  if ( trace_offset == 0 ) {
101 
102  for (int idx=0; idx<matrix_size; idx++) {
103 
104  trace_real += matrix[idx*matrix.stride + idx].real;
105 
106  }
107  }
108  else {
109 
110  for (int idx=0; idx<matrix_size; idx++) {
111 
112  trace_real += matrix[(idx+trace_offset)*matrix.stride + idx].real;
113 
114  }
115 
116  }
117 
118  //double cost_function = std::sqrt(1.0 - trace_real/matrix_size);
119  double cost_function = (1.0 - trace_real/matrix_size);
120 
121  return cost_function;
122 
123 }
124 
125 
126 
134 
135  Matrix_real ret(1,2);
136 
137  // calculate the cost function
138  ret[0] = get_cost_function( matrix, trace_offset );
139 
140 
141 
142  // calculate teh first correction
143 
144  int matrix_size = matrix.cols;
145 
146  double trace_real = 0.0;
147 
148  if ( trace_offset == 0 ) {
149  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
150 
151  int qbit_error_mask = 1 << qbit_idx;
152 
153  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
154 
155  // determine the row index pair with one bit error at the given qbit_idx
156  int row_idx = col_idx ^ qbit_error_mask;
157 
158  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
159  }
160  }
161 
162  }
163  else {
164 
165 
166  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
167 
168  int qbit_error_mask = 1 << qbit_idx;
169 
170  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
171 
172  // determine the row index pair with one bit error at the given qbit_idx
173  int row_idx = (col_idx + trace_offset) ^ qbit_error_mask;
174 // std::cout << matrix[row_idx*matrix.stride + col_idx].real << " " << row_idx << " " << col_idx << std::endl;
175  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
176  }
177  }
178 
179 
180  }
181 
182  //double cost_function = std::sqrt(1.0 - trace_real/matrix_size);
183  double cost_function = trace_real/matrix_size;
184 
185 //std::cout << cost_function << std::endl;
186 //exit(1);
187 
188  ret[1] = cost_function;
189 
190  return ret;
191 
192 
193 
194 }
195 
196 
197 
198 
206 
207 
208  Matrix_real ret(1,3);
209 
210  // calculate the cost function
211  ret[0] = get_cost_function( matrix, trace_offset );
212 
213 
214 
215  // calculate the first correction
216 
217  int matrix_size = matrix.cols;
218 
219  double trace_real = 0.0;
220 
221  if ( trace_offset == 0 ) {
222  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
223 
224  int qbit_error_mask = 1 << qbit_idx;
225 
226  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
227 
228  // determine the row index pair with one bit error at the given qbit_idx
229  int row_idx = col_idx ^ qbit_error_mask;
230 
231  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
232  }
233  }
234 
235  }
236  else {
237 
238 
239  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
240 
241  int qbit_error_mask = 1 << qbit_idx;
242 
243  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
244 
245  // determine the row index pair with one bit error at the given qbit_idx
246  int row_idx = (col_idx+trace_offset) ^ qbit_error_mask;
247 
248  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
249  }
250  }
251 
252 
253  }
254 
255  double cost_function = trace_real/matrix_size;
256 
257  ret[1] = cost_function;
258 
259 
260 
261 
262  // calculate the second correction
263 
264  trace_real = 0.0;
265 
266  if ( trace_offset == 0 ) {
267  for (int qbit_idx=0; qbit_idx<qbit_num-1; qbit_idx++) {
268  for (int qbit_idx2=qbit_idx+1; qbit_idx2<qbit_num; qbit_idx2++) {
269 
270  int qbit_error_mask = (1 << qbit_idx) + (1 << qbit_idx2);
271 
272  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
273 
274  // determine the row index pair with one bit error at the given qbit_idx
275  int row_idx = col_idx ^ qbit_error_mask;
276 
277  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
278  }
279 
280  }
281  }
282  }
283  else {
284 
285  for (int qbit_idx=0; qbit_idx<qbit_num-1; qbit_idx++) {
286  for (int qbit_idx2=qbit_idx+1; qbit_idx2<qbit_num; qbit_idx2++) {
287 
288  int qbit_error_mask = (1 << qbit_idx) + (1 << qbit_idx2);
289 
290  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
291 
292  // determine the row index pair with one bit error at the given qbit_idx
293  int row_idx = (col_idx+trace_offset) ^ qbit_error_mask;
294 
295  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
296  }
297 
298  }
299  }
300 
301 
302  }
303 
304  double cost_function2 = trace_real/matrix_size;
305 
306  ret[2] = cost_function2;
307 
308  return ret;
309 
310 
311 
312 
313 
314 }
315 
317 {
318  double ret = 0.0;
319  for (int rowidx = 0; rowidx < matrix.rows; rowidx++) {
320  int baseidx = rowidx*matrix.stride;
321  for (int colidx = 0; colidx < matrix.cols; colidx++) {
322  if (rowidx == colidx) {
323  ret += (matrix[baseidx+colidx].real - 1.0) * (matrix[baseidx+colidx].real - 1.0) + matrix[baseidx+colidx].imag * matrix[baseidx+colidx].imag;
324  } else {
325  ret += matrix[baseidx+colidx].real * matrix[baseidx+colidx].real + matrix[baseidx+colidx].imag * matrix[baseidx+colidx].imag;
326  }
327  }
328  }
329  return ret;
330 }
331 
338 
339  int matrix_size = matrix.cols;
340  double trace_real=0.0;
341  double trace_imag=0.0;
342  QGD_Complex16 ret;
343 
344  for (int idx=0; idx<matrix_size; idx++) {
345 
346  trace_real += matrix[idx*matrix.stride + idx].real;
347  trace_imag += matrix[idx*matrix.stride + idx].imag;
348 
349  }
350  ret.real = trace_real;
351  ret.imag = trace_imag;
352 
353  return ret;
354 }
355 
363 
364  double d = 1.0/matrix.cols;
365  double cost_function = 0.0;
366  QGD_Complex16 ret = get_trace(matrix);
367  cost_function = 1.0-d*d*(ret.real*ret.real+ret.imag*ret.imag);
368 
369  return cost_function;
370 }
371 
372 double get_infidelity(Matrix& matrix){
373 
374  double d = matrix.cols;
375  double cost_function = 0.0;
376  QGD_Complex16 ret = get_trace(matrix);
377  cost_function = 1.0-((ret.real*ret.real+ret.imag*ret.imag)/d+1)/(d+1);
378 
379  return cost_function;
380 }
381 
389 
390  Matrix ret(1,2);
391 
392  QGD_Complex16 trace_tmp = get_trace(matrix);
393 
394  int matrix_size = matrix.cols;
395 
396  double trace_real = 0.0;
397  double trace_imag = 0.0;
398 
399  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
400 
401  int qbit_error_mask = 1 << qbit_idx;
402 
403  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
404 
405  // determine the row index pair with one bit error at the given qbit_idx
406  int row_idx = col_idx ^ qbit_error_mask;
407 
408  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
409  trace_imag += matrix[row_idx*matrix.stride + col_idx].imag;
410  }
411  }
412 
413  ret[0].real = trace_tmp.real;
414  ret[0].imag = trace_tmp.imag;
415  ret[1].real = trace_real;
416  ret[1].imag = trace_imag;
417 
418  return ret;
419 }
420 
421 
429 
430  Matrix ret(1,3);
431 
432  QGD_Complex16 trace_tmp = get_trace(matrix);
433 
434  int matrix_size = matrix.cols;
435 
436  double trace_real = 0.0;
437  double trace_imag = 0.0;
438 
439  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
440 
441  int qbit_error_mask = 1 << qbit_idx;
442 
443  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
444 
445  // determine the row index pair with one bit error at the given qbit_idx
446  int row_idx = col_idx ^ qbit_error_mask;
447 
448  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
449  trace_imag += matrix[row_idx*matrix.stride + col_idx].imag;
450  }
451  }
452 
453 
454  ret[1].real = trace_real;
455  ret[1].imag = trace_imag;
456 
457 
458 
459  // calculate the second correction
460 
461  trace_real = 0.0;
462  trace_imag = 0.0;
463 
464  for (int qbit_idx=0; qbit_idx<qbit_num-1; qbit_idx++) {
465  for (int qbit_idx2=qbit_idx+1; qbit_idx2<qbit_num; qbit_idx2++) {
466 
467  int qbit_error_mask = (1 << qbit_idx) + (1 << qbit_idx2);
468 
469  for (int col_idx=0; col_idx<matrix_size; col_idx++) {
470 
471  // determine the row index pair with one bit error at the given qbit_idx
472  int row_idx = col_idx ^ qbit_error_mask;
473 
474  trace_real += matrix[row_idx*matrix.stride + col_idx].real;
475  trace_imag += matrix[row_idx*matrix.stride + col_idx].imag;
476  }
477 
478  }
479  }
480 
481 
482  ret[2].real = trace_real;
483  ret[2].imag = trace_imag;
484  ret[0].real = trace_tmp.real;
485  ret[0].imag = trace_tmp.imag;
486 
487  return ret;
488 }
489 
498 functor_cost_fnc::functor_cost_fnc( Matrix matrix_in, tbb::combinable<double>* partial_cost_functions_in ) {
499 
500  matrix = matrix_in;
501  data = matrix.get_data();
502  partial_cost_functions = partial_cost_functions_in;
503 }
504 
509 void functor_cost_fnc::operator()( tbb::blocked_range<int> r ) const {
510 
511  int matrix_size = matrix.rows;
512  double &cost_function_priv = partial_cost_functions->local();
513 
514  for ( int row_idx = r.begin(); row_idx != r.end(); row_idx++) {
515 
516  if ( row_idx > matrix_size ) {
517  std::string err("Error: row idx should be less than the number of roes in the matrix.");
518  throw err;
519  }
520 
521  // getting the corner element
522  QGD_Complex16 corner_element = data[0];
523 
524 
525  // Calculate the |x|^2 value of the elements of the matrix and summing them up to calculate the partial cost function
526  double partial_cost_function = 0;
527  int idx_offset = row_idx*matrix_size;
528  int idx_max = idx_offset + row_idx;
529  for ( int idx=idx_offset; idx<idx_max; idx++ ) {
530  partial_cost_function = partial_cost_function + data[idx].real*data[idx].real + data[idx].imag*data[idx].imag;
531  }
532 
533  int diag_element_idx = row_idx*matrix_size + row_idx;
534  double diag_real = data[diag_element_idx].real - corner_element.real;
535  double diag_imag = data[diag_element_idx].imag - corner_element.imag;
536  partial_cost_function = partial_cost_function + diag_real*diag_real + diag_imag*diag_imag;
537 
538 
539  idx_offset = idx_max + 1;
540  idx_max = row_idx*matrix_size + matrix_size;
541  for ( int idx=idx_offset; idx<idx_max; idx++ ) {
542  partial_cost_function = partial_cost_function + data[idx].real*data[idx].real + data[idx].imag*data[idx].imag;
543  }
544 
545  // storing the calculated partial cost function
546  cost_function_priv = cost_function_priv + partial_cost_function;
547 
548  }
549 }
550 
551 
552 
553 
554 
555 
556 
557 
double get_hilbert_schmidt_test(Matrix &matrix)
Call co calculate the cost function of the optimization process according to https://arxiv.org/pdf/2210.09191.pdf.
QGD_Complex16 * data
Pointer to the data stored in the matrix.
Matrix_real get_cost_function_with_correction(Matrix matrix, int qbit_num, int trace_offset)
Call co calculate the cost function of the optimization process, and the first correction to the cost...
int stride
The column stride of the array. (The array elements in one row are a_0, a_1, ... a_{cols-1}, 0, 0, 0, 0. The number of zeros is stride-cols)
Definition: matrix_base.hpp:46
double get_cost_function(Matrix matrix, int trace_offset)
Call co calculate the cost function during the final optimization process.
double get_infidelity(Matrix &matrix)
Call to calculate infidelity.
scalar * get_data() const
Call to get the pointer to the stored data.
Matrix_real get_cost_function_with_correction2(Matrix matrix, int qbit_num, int trace_offset)
Call co calculate the cost function of the optimization process, and the first correction to the cost...
int rows
The number of rows.
Definition: matrix_base.hpp:42
int cols
The number of columns.
Definition: matrix_base.hpp:44
matrix_size
[load Umtx]
Definition: example.py:58
Matrix matrix
Array stroing the matrix.
functor_cost_fnc(Matrix matrix_in, tbb::combinable< double > *partial_cost_functions_in)
Constructor of the class.
void operator()(tbb::blocked_range< int > r) const
Operator to calculate the partial cost function derived from the row of the matrix labeled by row_idx...
Matrix get_trace_with_correction2(Matrix &matrix, int qbit_num)
Call co calculate the Hilbert Schmidt testof the optimization process, and the first correction to th...
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
Class to store data of complex arrays and its properties.
Definition: matrix.h:38
double get_cost_function_sum_of_squares(Matrix &matrix)
Header file for the paralleized calculation of the cost function of the final optimization problem (s...
double real
the real part of a complex number
Definition: QGDTypes.h:40
Matrix get_trace_with_correction(Matrix &matrix, int qbit_num)
Call co calculate the Hilbert Schmidt testof the optimization process, and the first correction to th...
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:39
tbb::combinable< double > * partial_cost_functions
array storing the partial cost functions
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
QGD_Complex16 get_trace(Matrix &matrix)
Call to calculate the real and imaginary parts of the trace.