Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Optimization_Interface.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 */
23 #include "Optimization_Interface.h"
25 #include "Adam.h"
26 #include "grad_descend.h"
27 #include "BFGS_Powell.h"
28 #include "Bayes_Opt.h"
29 
30 #include "RL_experience.h"
31 
32 #include <fstream>
33 
34 
35 
36 
37 
38 extern "C" int LAPACKE_dgesv( int matrix_layout, int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ldb);
39 
40 
42 
43 
49 
50  // logical value. Set true if finding the minimum number of gate layers is required (default), or false when the maximal number of two-qubit gates is used (ideal for general unitaries).
51  optimize_layer_num = false;
52 
53  // A string describing the type of the class
55 
56  // The global minimum of the optimization problem
58 
59  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
60  adaptive_eta = true;
61 
62  // parameter to contron the radius of parameter randomization around the curren tminimum
63  radius = 1.0;
64  randomization_rate = 0.3;
65 
66  // The chosen variant of the cost function
68 
69  number_of_iters = 0;
70 
71 
72 
73  // variables to calculate the cost function with first and second corrections
74  prev_cost_fnv_val = 1.0;
75  correction1_scale = 1/1.7;
76  correction2_scale = 1/2.0;
77 
78 
79  // number of utilized accelerators
80  accelerator_num = 0;
81 
82  // set the trace offset
83  trace_offset = 0;
84 
85  // unique id indentifying the instance of the class
86  std::uniform_int_distribution<> distrib_int(0, INT_MAX);
87  int id = distrib_int(gen);
88 
89 
90  // Time spent on circuit simulation/cost function evaluation
92  // time spent on optimization
93  CPU_time = 0.0;
94 
95 }
96 
105 Optimization_Interface::Optimization_Interface( Matrix Umtx_in, int qbit_num_in, bool optimize_layer_num_in, std::map<std::string, Config_Element>& config, guess_type initial_guess_in= CLOSE_TO_ZERO, int accelerator_num_in ) : Decomposition_Base(Umtx_in, qbit_num_in, config, initial_guess_in) {
106 
107  // logical value. Set true if finding the minimum number of gate layers is required (default), or false when the maximal number of two-qubit gates is used (ideal for general unitaries).
108  optimize_layer_num = optimize_layer_num_in;
109 
110  // A string describing the type of the class
112 
113  // The global minimum of the optimization problem
115 
116  number_of_iters = 0;
117 
118 
119  // number of iteratrion loops in the optimization
120  iteration_loops[2] = 3;
121 
122  // filling in numbers that were not given in the input
123  for ( std::map<int,int>::iterator it = max_layer_num_def.begin(); it!=max_layer_num_def.end(); it++) {
124  if ( max_layer_num.count( it->first ) == 0 ) {
125  max_layer_num.insert( std::pair<int, int>(it->first, it->second) );
126  }
127  }
128 
129  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
130  adaptive_eta = true;
131 
132  // parameter to contron the radius of parameter randomization around the curren tminimum
133  radius = 1.0;
134  randomization_rate = 0.3;
135 
136  // The chosen variant of the cost function
138 
139 
140  // variables to calculate the cost function with first and second corrections
141  prev_cost_fnv_val = 1.0;
142  correction1_scale = 1/1.7;
143  correction2_scale = 1/2.0;
144 
145 
146  // set the trace offset
147  trace_offset = 0;
148 
149  // unique id indentifying the instance of the class
150  std::uniform_int_distribution<> distrib_int(0, INT_MAX);
151  id = distrib_int(gen);
152 
153 
154 
155  // Time spent on circuit simulation/cost function evaluation
157  // time spent on optimization
158  CPU_time = 0.0;
159 
160 #if defined __DFE__
161  // number of utilized accelerators
162  accelerator_num = accelerator_num_in;
163 #elif defined __GROQ__
164  // number of utilized accelerators
165  accelerator_num = accelerator_num_in;
166 #else
167  accelerator_num = 0;
168 #endif
169 
170 }
171 
172 
173 
178 
179 
180 #ifdef __DFE__
181  if ( Umtx.cols == Umtx.rows && qbit_num >= 2 && get_accelerator_num() > 0 ) {
182  unload_dfe_lib();//releive_DFE();
183  }
184 #endif
185 
186 
187 
188 }
189 
190 
196 
198 
199 }
200 
201 
202 
209 
210  FILE* pFile;
211  std::string filename("costfuncs_and_entropy.txt");
212 
213  if (project_name != "") {
214  filename = project_name + "_" + filename;
215  }
216 
217  const char* c_filename = filename.c_str();
218  pFile = fopen(c_filename, "a");
219 
220  if (pFile==NULL) {
221  fputs ("File error",stderr);
222  std::string error("Cannot open file.");
223  throw error;
224  }
225 
226  Matrix input_state(Power_of_2(qbit_num),1);
227 
228  std::uniform_int_distribution<> distrib(0, qbit_num-2);
229 
230  memset(input_state.get_data(), 0.0, (input_state.size()*2)*sizeof(double) );
231  input_state[0].real = 1.0;
232 
233  matrix_base<int> qbit_sublist(1,2);
234  qbit_sublist[0] = 0;//distrib(gen);
235  qbit_sublist[1] = 1;//qbit_sublist[0]+1;
236 
237  double renyi_entropy = get_second_Renyi_entropy(parameters, input_state, qbit_sublist);
238 
239  fprintf(pFile,"%i\t%f\t%f\n", (int)number_of_iters, current_minimum, renyi_entropy);
240  fclose(pFile);
241 
242  return;
243 }
244 
245 
253 
254 
255  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
256 
257  instance->export_current_cost_fnc( current_minimum, parameters );
258 
259 }
260 
261 
265 void
267 
268 
269  // creating block of gates
270  Gates_block* block = new Gates_block( qbit_num );
271 
272  // adding U1 gates for final phase corrections
273  for (int qbit=0; qbit<qbit_num; qbit++) {
274  block->add_u1(qbit);
275  }
276 
277  // adding the opeartion block to the gates
278  add_gate( block );
279 
280 }
281 
282 
283 
284 
290 void
292 
293  // (U-U_{approx}) (U-U_{approx})^\dagger = 2*I - U*U_{approx}^\dagger - U_{approx}*U^\dagger
294  // U*U_{approx}^\dagger = decomposed_matrix_copy
295 
296  /*Matrix A(matrix_size, matrix_size);
297  QGD_Complex16* A_data = A.get_data();
298  QGD_Complex16* decomposed_data = decomposed_matrix.get_data();
299  QGD_Complex16 phase;
300  phase.real = decomposed_matrix[0].real/(std::sqrt(decomposed_matrix[0].real*decomposed_matrix[0].real + decomposed_matrix[0].imag*decomposed_matrix[0].imag));
301  phase.imag = -decomposed_matrix[0].imag/(std::sqrt(decomposed_matrix[0].real*decomposed_matrix[0].real + decomposed_matrix[0].imag*decomposed_matrix[0].imag));
302 
303  for (int idx=0; idx<matrix_size; idx++ ) {
304  for (int jdx=0; jdx<matrix_size; jdx++ ) {
305 
306  if (idx==jdx) {
307  QGD_Complex16 mtx_val = mult(phase, decomposed_data[idx*matrix_size+jdx]);
308  A_data[idx*matrix_size+jdx].real = 2.0 - 2*mtx_val.real;
309  A_data[idx*matrix_size+jdx].imag = 0;
310  }
311  else {
312  QGD_Complex16 mtx_val_ij = mult(phase, decomposed_data[idx*matrix_size+jdx]);
313  QGD_Complex16 mtx_val_ji = mult(phase, decomposed_data[jdx*matrix_size+idx]);
314  A_data[idx*matrix_size+jdx].real = - mtx_val_ij.real - mtx_val_ji.real;
315  A_data[idx*matrix_size+jdx].imag = - mtx_val_ij.imag + mtx_val_ji.imag;
316  }
317 
318  }
319  }
320 
321 
322  Matrix alpha(matrix_size, 1);
323  Matrix beta(matrix_size, 1);
324  Matrix B = create_identity(matrix_size);
325 
326  // solve the generalized eigenvalue problem of I- 1/2
327  LAPACKE_zggev( CblasRowMajor, 'N', 'N',
328  matrix_size, A.get_data(), matrix_size, B.get_data(),
329  matrix_size, alpha.get_data(),
330  beta.get_data(), NULL, matrix_size, NULL,
331  matrix_size );
332 
333  // determine the largest eigenvalue
334  double eigval_max = 0;
335  for (int idx=0; idx<matrix_size; idx++) {
336  double eigval_abs = std::sqrt((alpha[idx].real*alpha[idx].real + alpha[idx].imag*alpha[idx].imag) / (beta[idx].real*beta[idx].real + beta[idx].imag*beta[idx].imag));
337  if ( eigval_max < eigval_abs ) eigval_max = eigval_abs;
338  }
339 
340  // the norm is the square root of the largest einegvalue.*/
341  if ( cost_fnc == FROBENIUS_NORM ) {
342  decomposition_error = get_cost_function(decomposed_matrix);
343  }
344  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
345  Matrix_real&& ret = get_cost_function_with_correction(decomposed_matrix, qbit_num);
346  decomposition_error = ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale;
347  }
348  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
349  Matrix_real&& ret = get_cost_function_with_correction2(decomposed_matrix, qbit_num);
350  decomposition_error = ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale);
351  }
352  else if ( cost_fnc == HILBERT_SCHMIDT_TEST){
353  decomposition_error = get_hilbert_schmidt_test(decomposed_matrix);
354  }
356  Matrix&& ret = get_trace_with_correction(decomposed_matrix, qbit_num);
357  double d = 1.0/decomposed_matrix.cols;
358  decomposition_error = 1 - d*d*(ret[0].real*ret[0].real+ret[0].imag*ret[0].imag+std::sqrt(prev_cost_fnv_val)*correction1_scale*(ret[1].real*ret[1].real+ret[1].imag*ret[1].imag));
359  }
361  Matrix&& ret = get_trace_with_correction2(decomposed_matrix, qbit_num);
362  double d = 1.0/decomposed_matrix.cols;
363  decomposition_error = 1 - d*d*(ret[0].real*ret[0].real+ret[0].imag*ret[0].imag+std::sqrt(prev_cost_fnv_val)*(correction1_scale*(ret[1].real*ret[1].real+ret[1].imag*ret[1].imag)+correction2_scale*(ret[2].real*ret[2].real+ret[2].imag*ret[2].imag)));
364  }
365  else if ( cost_fnc == SUM_OF_SQUARES) {
367  }
368  else if (cost_fnc == INFIDELITY){
369  decomposition_error = get_infidelity(decomposed_matrix);
370  }
371  else {
372  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
373  throw err;
374  }
375 
376 }
377 
378 
379 
384 
385  //The stringstream input to store the output messages.
386  std::stringstream sstream;
387  sstream << "***************************************************************" << std::endl;
388  sstream << "Final fine tuning of the parameters in the " << qbit_num << "-qubit decomposition" << std::endl;
389  sstream << "***************************************************************" << std::endl;
390  print(sstream, 1);
391 
392 
393 
394 
395  //# setting the global minimum
397 
398  if ( optimized_parameters_mtx.size() == 0 ) {
400  }
401  else {
403  if ( check_optimization_solution() ) return;
404 
406  }
407 }
408 
409 
410 
411 
418 
419 
420  switch ( alg ) {
421  case ADAM:
422  solve_layer_optimization_problem_ADAM( num_of_parameters, solution_guess);
423  return;
424  case ADAM_BATCHED:
425  solve_layer_optimization_problem_ADAM_BATCHED( num_of_parameters, solution_guess);
426  return;
427  case GRAD_DESCEND:
428  solve_layer_optimization_problem_GRAD_DESCEND( num_of_parameters, solution_guess);
429  return;
430  case AGENTS:
431  solve_layer_optimization_problem_AGENTS( num_of_parameters, solution_guess);
432  return;
433  case COSINE:
434  solve_layer_optimization_problem_COSINE( num_of_parameters, solution_guess);
435  return;
437  solve_layer_optimization_problem_GRAD_DESCEND_PARAMETER_SHIFT_RULE( num_of_parameters, solution_guess);
438  return;
439  case AGENTS_COMBINED:
440  solve_layer_optimization_problem_AGENTS_COMBINED( num_of_parameters, solution_guess);
441  return;
442  case BFGS:
443  solve_layer_optimization_problem_BFGS( num_of_parameters, solution_guess);
444  return;
445  case BAYES_OPT:
446  solve_layer_optimization_problem_BAYES_OPT( num_of_parameters, solution_guess);
447  return;
448  case BAYES_AGENTS:
449  solve_layer_optimization_problem_BAYES_AGENTS( num_of_parameters, solution_guess);
450  return;
451  case BFGS2:
452  solve_layer_optimization_problem_BFGS2( num_of_parameters, solution_guess);
453  return;
454  default:
455  std::string error("Optimization_Interface::solve_layer_optimization_problem: unimplemented optimization algorithm");
456  throw error;
457  }
458 
459 
460 }
461 
469 
470  // random generator of real numbers
471  std::uniform_real_distribution<> distrib_prob(0.0, 1.0);
472  std::uniform_real_distribution<> distrib_real(-2*M_PI, 2*M_PI);
473 
474 
475  double radius_loc;
476  if ( config.count("Randomized_Radius") > 0 ) {
477  config["Randomized_Radius"].get_property( radius_loc );
478  }
479  else {
480  radius_loc = radius;
481  }
482 
483  const int num_of_parameters = input.size();
484 
485  int changed_parameters = 0;
486  for ( int jdx=0; jdx<num_of_parameters; jdx++) {
487  if ( distrib_prob(gen) <= randomization_rate ) {
488  output[jdx] = (cost_fnc!=VQE) ? input[jdx] + distrib_real(gen)*std::sqrt(f0)*radius_loc : input[jdx] + distrib_real(gen)*radius_loc;
489  changed_parameters++;
490  }
491  else {
492  output[jdx] = input[jdx];
493  }
494  }
495 
496 #ifdef __MPI__
497  //MPI_Bcast( (void*)output.get_data(), num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
498 #endif
499 
500 
501 }
502 
503 
510 
511  // get the transformed matrix with the gates in the list
512  Matrix_real parameters_mtx(parameters, 1, parameter_num );
513 
514 
515  return optimization_problem( parameters_mtx );
516 
517 
518 }
519 
520 
527 
528  // get the transformed matrix with the gates in the list
529  if ( parameters.size() != parameter_num ) {
530  std::stringstream sstream;
531  sstream << "Optimization_Interface::optimization_problem: Number of free paramaters should be " << parameter_num << ", but got " << parameters.size() << std::endl;
532  print(sstream, 0);
533  std::string err("Optimization_Interface::optimization_problem: Wrong number of parameters.");
534  throw err;
535  }
536 
537  Matrix matrix_new = Umtx.copy();
538  apply_to( parameters, matrix_new );
539 
540  if ( cost_fnc == FROBENIUS_NORM ) {
541  return get_cost_function(matrix_new, trace_offset);
542  }
543  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
545  return ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale;
546  }
547  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
549  return ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale);
550  }
551  else if ( cost_fnc == HILBERT_SCHMIDT_TEST){
552  return get_hilbert_schmidt_test(matrix_new);
553  }
555  Matrix&& ret = get_trace_with_correction(matrix_new, qbit_num);
556  double d = 1.0/matrix_new.cols;
557  return 1 - d*d*(ret[0].real*ret[0].real+ret[0].imag*ret[0].imag+std::sqrt(prev_cost_fnv_val)*correction1_scale*(ret[1].real*ret[1].real+ret[1].imag*ret[1].imag));
558  }
560  Matrix&& ret = get_trace_with_correction2(matrix_new, qbit_num);
561  double d = 1.0/matrix_new.cols;
562  return 1 - d*d*(ret[0].real*ret[0].real+ret[0].imag*ret[0].imag+std::sqrt(prev_cost_fnv_val)*(correction1_scale*(ret[1].real*ret[1].real+ret[1].imag*ret[1].imag)+correction2_scale*(ret[2].real*ret[2].real+ret[2].imag*ret[2].imag)));
563  }
564  else if ( cost_fnc == SUM_OF_SQUARES) {
565  return get_cost_function_sum_of_squares(matrix_new);
566  }
567  else if (cost_fnc == INFIDELITY){
568  return get_infidelity(matrix_new);
569  }
570  else {
571  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
572  throw err;
573  }
574 
575 }
576 
577 #ifdef __DFE__
578 
584 Optimization_Interface::optimization_problem_batched_DFE( std::vector<Matrix_real>& parameters_vec) {
585 
586 
587  Matrix_real cost_fnc_mtx(parameters_vec.size(), 1);
588 
589  int gatesNum, gateSetNum, redundantGateSets;
590  DFEgate_kernel_type* DFEgates = convert_to_batched_DFE_gates( parameters_vec, gatesNum, gateSetNum, redundantGateSets );
591 
592  Matrix_real trace_DFE_mtx(gateSetNum, 3);
593 
594 
595 
596  number_of_iters = number_of_iters + parameters_vec.size();
597 
598 
599 
600 #ifdef __MPI__
601  // the number of decomposing layers are divided between the MPI processes
602 
603  int mpi_gateSetNum = gateSetNum / world_size;
604  int mpi_starting_gateSetIdx = gateSetNum/world_size * current_rank;
605 
606  Matrix_real mpi_trace_DFE_mtx(mpi_gateSetNum, 3);
607 
608 
609  number_of_iters = number_of_iters + mpi_gateSetNum;
610 
611 
612  lock_lib();
613  calcqgdKernelDFE( Umtx.rows, Umtx.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, trace_offset, mpi_trace_DFE_mtx.get_data() );
614  unlock_lib();
615 
616  int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
617  MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
618 
619 #else
620  lock_lib();
621  calcqgdKernelDFE( Umtx.rows, Umtx.cols, DFEgates, gatesNum, gateSetNum, trace_offset, trace_DFE_mtx.get_data() );
622  unlock_lib();
623 
624 #endif // __MPI__
625 
626 
627  // calculate the cost function
628  if ( cost_fnc == FROBENIUS_NORM ) {
629  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
630  cost_fnc_mtx[idx] = 1-trace_DFE_mtx[idx*3]/Umtx.cols;
631  }
632  }
633  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
634  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
635  cost_fnc_mtx[idx] = 1-(trace_DFE_mtx[idx*3] + std::sqrt(prev_cost_fnv_val)*trace_DFE_mtx[idx*3+1]*correction1_scale)/Umtx.cols;
636  }
637  }
638  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
639  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
640  cost_fnc_mtx[idx] = 1-(trace_DFE_mtx[idx*3] + std::sqrt(prev_cost_fnv_val)*(trace_DFE_mtx[idx*3+1]*correction1_scale + trace_DFE_mtx[idx*3+2]*correction2_scale))/Umtx.cols;
641  }
642  }
643  else {
644  std::string err("Optimization_Interface::optimization_problem_batched: Cost function variant not implmented for DFE.");
645  throw err;
646  }
647 
648 
649 
650 
651 
652  delete[] DFEgates;
653 
654  return cost_fnc_mtx;
655 
656 }
657 #endif
658 
659 
660 
661 #ifdef __GROQ__
662 
663 
670 double
671 Optimization_Interface::optimization_problem_Groq( Matrix_real& parameters, int chosen_device) {
672 
673  throw std::string("Optimization_Interface::optimization_problem_Groq should be implemented in derrived classes.");
674 
675  return 0.0;
676 
677 }
678 
679 
680 
687 Optimization_Interface::optimization_problem_batched_Groq( std::vector<Matrix_real>& parameters_vec) {
688 
689  int task_num = parameters_vec.size();
690 
691  Matrix_real cost_fnc_mtx(task_num, 1);
692 
693  if ( get_initialize_id() != id ) {
694  init_groq_sv_lib(accelerator_num, id);
695  }
696 
697  if ( accelerator_num == 1 ) {
698 
699  int chosen_device = 0;
700 
701  for( int idx=0; idx<task_num; idx++ ) {
702  cost_fnc_mtx[idx] = optimization_problem_Groq( parameters_vec[idx], chosen_device );
703  }
704 
705  }
706  else if ( accelerator_num == 2 ) {
707 
708  for( int idx=0; idx<task_num; idx=idx+2 ) {
709  tbb::parallel_invoke(
710  [&]() { cost_fnc_mtx[idx] = optimization_problem_Groq( parameters_vec[idx], 0 ); },
711  [&]() { if ( (idx+1) < task_num ) cost_fnc_mtx[idx+1] = optimization_problem_Groq( parameters_vec[idx+1], 1 ); }
712  );
713  }
714 
715  }
716  else {
717  throw std::string("Unsupported number of Groq accelerators.");
718  }
719 
720  return cost_fnc_mtx;
721 
722 
723 }
724 #endif
725 
732 Optimization_Interface::optimization_problem_batched( std::vector<Matrix_real>& parameters_vec) {
733 
734 
735 
736 
737 
738 #if defined __DFE__
739  if ( Umtx.cols == Umtx.rows && get_accelerator_num() > 0 ) {
740  return optimization_problem_batched_DFE( parameters_vec );
741  }
742 #elif defined __GROQ__
743  if ( Umtx.cols == 1 && get_accelerator_num() > 0 ) {
744  return optimization_problem_batched_Groq( parameters_vec );
745  }
746 #endif
747 
748 
749  Matrix_real cost_fnc_mtx(parameters_vec.size(), 1);
750  int parallel = get_parallel_configuration();
751 
752 #ifdef __MPI__
753 
754 
755  // the number of decomposing layers are divided between the MPI processes
756 
757  int batch_element_num = parameters_vec.size();
758  int mpi_batch_element_num = batch_element_num / world_size;
759  int mpi_batch_element_remainder = batch_element_num % world_size;
760 
761  if ( mpi_batch_element_remainder > 0 ) {
762  std::string err("Optimization_Interface::optimization_problem_batched: The size of the batch should be divisible with the number of processes.");
763  throw err;
764  }
765 
766  int mpi_starting_batchIdx = mpi_batch_element_num * current_rank;
767 
768 
769  Matrix_real cost_fnc_mtx_loc(mpi_batch_element_num, 1);
770 
771  int work_batch = 1;
772  if( parallel==0) {
773  work_batch = mpi_batch_element_num;
774  }
775 
776  tbb::parallel_for( tbb::blocked_range<int>(0, (int)mpi_batch_element_num, work_batch), [&](tbb::blocked_range<int> r) {
777  for (int idx=r.begin(); idx<r.end(); ++idx) {
778  cost_fnc_mtx_loc[idx] = optimization_problem( parameters_vec[idx + mpi_starting_batchIdx] );
779  }
780  });
781 
782  //number_of_iters = number_of_iters + mpi_batch_element_num;
783 
784 
785 
786  int bytes = cost_fnc_mtx_loc.size()*sizeof(double);
787  MPI_Allgather(cost_fnc_mtx_loc.get_data(), bytes, MPI_BYTE, cost_fnc_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
788 
789 
790 #else
791 
792  int work_batch = 1;
793  if( parallel==0) {
794  work_batch = parameters_vec.size();
795  }
796 
797  tbb::parallel_for( tbb::blocked_range<int>(0, (int)parameters_vec.size(), work_batch), [&](tbb::blocked_range<int> r) {
798  for (int idx=r.begin(); idx<r.end(); ++idx) {
799  cost_fnc_mtx[idx] = optimization_problem( parameters_vec[idx] );
800  }
801  });
802 
803 
804 #endif // __MPI__
805 
806 
807  return cost_fnc_mtx;
808 
809 }
810 
811 
812 
813 
814 
822 double Optimization_Interface::optimization_problem( Matrix_real parameters, void* void_instance, Matrix ret_temp) {
823 
824  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
825  std::vector<Gate*> gates_loc = instance->get_gates();
826 
827  {
828  tbb::spin_mutex::scoped_lock my_lock{my_mutex_optimization_interface};
829 
830  number_of_iters++;
831 
832  }
833 
834  // get the transformed matrix with the gates in the list
835  Matrix Umtx_loc = instance->get_Umtx();
836  Matrix matrix_new = Umtx_loc.copy();
837  instance->apply_to( parameters, matrix_new );
838 
840 
841  if ( cost_fnc == FROBENIUS_NORM ) {
842  return get_cost_function(matrix_new, instance->get_trace_offset());
843  }
844  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
845  double correction1_scale = instance->get_correction1_scale();
846  Matrix_real&& ret = get_cost_function_with_correction(matrix_new, instance->get_qbit_num(), instance->get_trace_offset());
847  return ret[0] - 0*std::sqrt(instance->get_previous_cost_function_value())*ret[1]*correction1_scale;
848  }
849  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
850  double correction1_scale = instance->get_correction1_scale();
851  double correction2_scale = instance->get_correction2_scale();
852  Matrix_real&& ret = get_cost_function_with_correction2(matrix_new, instance->get_qbit_num(), instance->get_trace_offset());
853  return ret[0] - std::sqrt(instance->get_previous_cost_function_value())*(ret[1]*correction1_scale + ret[2]*correction2_scale);
854  }
855  else if ( cost_fnc == HILBERT_SCHMIDT_TEST){
856  QGD_Complex16 trace_temp = get_trace(matrix_new);
857  ret_temp[0].real = trace_temp.real;
858  ret_temp[0].imag = trace_temp.imag;
859  double d = 1.0/matrix_new.cols;
860  return 1.0-d*d*trace_temp.real*trace_temp.real-d*d*trace_temp.imag*trace_temp.imag;
861  }
862  else if ( cost_fnc == HILBERT_SCHMIDT_TEST_CORRECTION1 ){
863  double correction1_scale = instance->get_correction1_scale();
864  Matrix&& ret = get_trace_with_correction(matrix_new, instance->get_qbit_num());
865  double d = 1.0/matrix_new.cols;
866  for (int idx=0; idx<3; idx++){ret_temp[idx].real=ret[idx].real;ret_temp[idx].imag=ret[idx].imag;}
867  return 1.0 - d*d*(ret[0].real*ret[0].real+ret[0].imag*ret[0].imag+std::sqrt(instance->get_previous_cost_function_value())*correction1_scale*(ret[1].real*ret[1].real+ret[1].imag*ret[1].imag));
868  }
869  else if ( cost_fnc == HILBERT_SCHMIDT_TEST_CORRECTION2 ){
870  double correction1_scale = instance->get_correction1_scale();
871  double correction2_scale = instance->get_correction2_scale();
872  Matrix&& ret = get_trace_with_correction2(matrix_new, instance->get_qbit_num());
873  double d = 1.0/matrix_new.cols;
874  for (int idx=0; idx<4; idx++){ret_temp[idx].real=ret[idx].real;ret_temp[idx].imag=ret[idx].imag;}
875  return 1.0 - d*d*(ret[0].real*ret[0].real+ret[0].imag*ret[0].imag+std::sqrt(instance->get_previous_cost_function_value())*(correction1_scale*(ret[1].real*ret[1].real+ret[1].imag*ret[1].imag)+correction2_scale*(ret[2].real*ret[2].real+ret[2].imag*ret[2].imag)));
876  }
877  else if ( cost_fnc == SUM_OF_SQUARES) {
878  return get_cost_function_sum_of_squares(matrix_new);
879  }
880  else if (cost_fnc == INFIDELITY){
881  QGD_Complex16 trace_temp = get_trace(matrix_new);
882  ret_temp[0].real = trace_temp.real;
883  ret_temp[0].imag = trace_temp.imag;
884  double d = matrix_new.cols;
885  return 1.0-((trace_temp.real*trace_temp.real+trace_temp.imag*trace_temp.imag)/d+1)/(d+1);
886  }
887  else {
888  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
889  throw err;
890  }
891 
892 
893 }
894 
895 
896 
904 
905  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
906  Matrix ret(1,3);
907  double cost_func = instance->optimization_problem(parameters, void_instance, ret);
908  return cost_func;
909 }
910 
911 
912 
913 
920 double Optimization_Interface::optimization_problem( Matrix_real parameters, void* void_instance){
921  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
922  return instance->optimization_problem_non_static(parameters, void_instance);
923 }
924 
925 
926 
927 
928 
929 
930 
937 void Optimization_Interface::optimization_problem_grad( Matrix_real parameters, void* void_instance, Matrix_real& grad ) {
938 
939  // The function value at x0
940  double f0;
941 
942  // calculate the approximate gradient
943  optimization_problem_combined( parameters, void_instance, &f0, grad);
944 
945 }
946 
947 
948 
949 
950 
958 void Optimization_Interface::optimization_problem_combined_non_static( Matrix_real parameters, void* void_instance, double* f0, Matrix_real& grad ) {
959 
960  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
961 
962  int parallel = instance->get_parallel_configuration();
963 
964  // the number of free parameters
965  int parameter_num_loc = instance->get_parameter_num();
966 
967  // the variant of the cost function
969 
970  // value of the cost function from the previous iteration to weigth the correction to the trace
972  double correction1_scale = instance->get_correction1_scale();
973  double correction2_scale = instance->get_correction2_scale();
974 
975  int qbit_num = instance->get_qbit_num();
976  int trace_offset_loc = instance->get_trace_offset();
977 
978 #ifdef __DFE__
979 
981 //std::cout << "number of qubits: " << instance->qbit_num << std::endl;
982 //tbb::tick_count t0_DFE = tbb::tick_count::now();/////////////////////////////////
983 if ( Umtx.cols == Umtx.rows && instance->qbit_num >= 5 && instance->get_accelerator_num() > 0 ) {
984 
985  int gatesNum, redundantGateSets, gateSetNum;
986  DFEgate_kernel_type* DFEgates = instance->convert_to_DFE_gates_with_derivates( parameters, gatesNum, gateSetNum, redundantGateSets );
987 
988  Matrix&& Umtx_loc = instance->get_Umtx();
989  Matrix_real trace_DFE_mtx(gateSetNum, 3);
990 
991 
992 #ifdef __MPI__
993  // the number of decomposing layers are divided between the MPI processes
994 
995  int mpi_gateSetNum = gateSetNum / instance->world_size;
996  int mpi_starting_gateSetIdx = gateSetNum/instance->world_size * instance->current_rank;
997 
998  Matrix_real mpi_trace_DFE_mtx(mpi_gateSetNum, 3);
999 
1000  instance->number_of_iters = instance->number_of_iters + mpi_gateSetNum;
1001 
1002  lock_lib();
1003  calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, trace_offset_loc, mpi_trace_DFE_mtx.get_data() );
1004  unlock_lib();
1005 
1006  int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
1007  MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
1008 
1009 #else
1010 
1011  instance->number_of_iters = instance->number_of_iters + gateSetNum;
1012 
1013  lock_lib();
1014  calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates, gatesNum, gateSetNum, trace_offset_loc, trace_DFE_mtx.get_data() );
1015  unlock_lib();
1016 
1017 #endif
1018 
1019  std::stringstream sstream;
1020  sstream << *f0 << " " << 1.0 - trace_DFE_mtx[0]/Umtx_loc.cols << " " << trace_DFE_mtx[1]/Umtx_loc.cols << " " << trace_DFE_mtx[2]/Umtx_loc.cols << std::endl;
1021  instance->print(sstream, 5);
1022 
1023 
1024  if ( cost_fnc == FROBENIUS_NORM ) {
1025  *f0 = 1-trace_DFE_mtx[0]/Umtx_loc.cols;
1026  }
1027  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1028  *f0 = 1 - (trace_DFE_mtx[0] + std::sqrt(prev_cost_fnv_val)*trace_DFE_mtx[1]*correction1_scale)/Umtx_loc.cols;
1029  }
1030  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1031  *f0 = 1 - (trace_DFE_mtx[0] + std::sqrt(prev_cost_fnv_val)*(trace_DFE_mtx[1]*correction1_scale + trace_DFE_mtx[2]*correction2_scale))/Umtx_loc.cols;
1032  }
1033  else {
1034  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1035  throw err;
1036  }
1037 
1038  //double f0_DFE = *f0;
1039 
1040  //Matrix_real grad_components_DFE_mtx(1, parameter_num_loc);
1041  for (int idx=0; idx<parameter_num_loc; idx++) {
1042 
1043  if ( cost_fnc == FROBENIUS_NORM ) {
1044  grad[idx] = -trace_DFE_mtx[3*(idx+1)]/Umtx_loc.cols;
1045  }
1046  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1047  grad[idx] = -(trace_DFE_mtx[3*(idx+1)] + std::sqrt(prev_cost_fnv_val)*trace_DFE_mtx[3*(idx+1)+1]*correction1_scale)/Umtx_loc.cols;
1048  }
1049  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1050  grad[idx] = -(trace_DFE_mtx[3*(idx+1)] + std::sqrt(prev_cost_fnv_val)*(trace_DFE_mtx[3*(idx+1)+1]*correction1_scale + trace_DFE_mtx[3*(idx+1)+2]*correction2_scale))/Umtx_loc.cols;
1051  }
1052  else {
1053  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1054  throw err;
1055  }
1056 
1057  //grad_components_DFE_mtx[idx] = gsl_vector_get( grad, idx );
1058 
1059 
1060  }
1061 
1062  delete[] DFEgates;
1063 
1064 //tbb::tick_count t1_DFE = tbb::tick_count::now();/////////////////////////////////
1065 //std::cout << "uploaded data to DFE: " << (int)(gatesNum*gateSetNum*sizeof(DFEgate_kernel_type)) << " bytes" << std::endl;
1066 //std::cout << "time elapsed DFE: " << (t1_DFE-t0_DFE).seconds() << ", expected time: " << (((double)Umtx_loc.rows*(double)Umtx_loc.cols*gatesNum*gateSetNum/get_chained_gates_num()/4 + 4578*3*get_chained_gates_num()))/350000000 + 0.001<< std::endl;
1067 
1069 }
1070 else {
1071 
1072 #endif
1073 
1074 #ifdef __DFE__
1075 tbb::tick_count t0_CPU = tbb::tick_count::now();
1076 #endif
1077 
1078  Matrix_real cost_function_terms;
1079 
1080  // vector containing gradients of the transformed matrix
1081  std::vector<Matrix> Umtx_deriv;
1082  Matrix trace_tmp(1,3);
1083 
1084  int work_batch = 10;
1085 
1086  if( parallel == 0 ) {
1087 
1088  *f0 = instance->optimization_problem(parameters, reinterpret_cast<void*>(instance), trace_tmp);
1089  Matrix&& Umtx_loc = instance->get_Umtx();
1090  Umtx_deriv = instance->apply_derivate_to( parameters, Umtx_loc, parallel );
1091 
1092  work_batch = parameter_num_loc;
1093  }
1094  else {
1095 
1096  tbb::parallel_invoke(
1097  [&]{
1098  *f0 = instance->optimization_problem(parameters, reinterpret_cast<void*>(instance), trace_tmp);
1099  },
1100  [&]{
1101  Matrix&& Umtx_loc = instance->get_Umtx();
1102  Umtx_deriv = instance->apply_derivate_to( parameters, Umtx_loc, parallel );
1103  }
1104  );
1105 
1106  work_batch = 10;
1107  }
1108 
1109 
1110  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,work_batch), [&](tbb::blocked_range<int> r) {
1111  for (int idx=r.begin(); idx<r.end(); ++idx) {
1112 
1113 
1114  //for( int idx=0; idx<parameter_num_loc; idx++ ) {
1115 
1116  double grad_comp;
1117  if ( cost_fnc == FROBENIUS_NORM ) {
1118  grad_comp = (get_cost_function(Umtx_deriv[idx], trace_offset_loc) - 1.0);
1119  }
1120  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1121  Matrix_real deriv_tmp = get_cost_function_with_correction( Umtx_deriv[idx], qbit_num, trace_offset_loc );
1122  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*deriv_tmp[1]*correction1_scale - 1.0);
1123  }
1124  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1125  Matrix_real deriv_tmp = get_cost_function_with_correction2( Umtx_deriv[idx], qbit_num, trace_offset_loc );
1126  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*(deriv_tmp[1]*correction1_scale + deriv_tmp[2]*correction2_scale) - 1.0);
1127  }
1128  else if (cost_fnc == HILBERT_SCHMIDT_TEST){
1129  double d = 1.0/Umtx_deriv[idx].cols;
1130  QGD_Complex16 deriv_tmp = get_trace(Umtx_deriv[idx]);
1131  grad_comp = -2.0*d*d*trace_tmp[0].real*deriv_tmp.real-2.0*d*d*trace_tmp[0].imag*deriv_tmp.imag;
1132  }
1133  else if ( cost_fnc == HILBERT_SCHMIDT_TEST_CORRECTION1 ){
1134  Matrix&& deriv_tmp = get_trace_with_correction( Umtx_deriv[idx], qbit_num);
1135  double d = 1.0/Umtx_deriv[idx].cols;
1136  grad_comp = -2.0*d*d* (trace_tmp[0].real*deriv_tmp[0].real+trace_tmp[0].imag*deriv_tmp[0].imag+std::sqrt(prev_cost_fnv_val)*correction1_scale*(trace_tmp[1].real*deriv_tmp[1].real+trace_tmp[1].imag*deriv_tmp[1].imag));
1137  }
1138  else if ( cost_fnc == HILBERT_SCHMIDT_TEST_CORRECTION2 ){
1139  Matrix&& deriv_tmp = get_trace_with_correction2( Umtx_deriv[idx], qbit_num);
1140  double d = 1.0/Umtx_deriv[idx].cols;
1141  grad_comp = -2.0*d*d* (trace_tmp[0].real*deriv_tmp[0].real+trace_tmp[0].imag*deriv_tmp[0].imag+std::sqrt(prev_cost_fnv_val)*(correction1_scale*(trace_tmp[1].real*deriv_tmp[1].real+trace_tmp[1].imag*deriv_tmp[1].imag) + correction2_scale*(trace_tmp[2].real*deriv_tmp[2].real+trace_tmp[2].imag*deriv_tmp[2].imag)));
1142  }
1143  else if ( cost_fnc == SUM_OF_SQUARES) {
1144  grad_comp = get_cost_function_sum_of_squares(Umtx_deriv[idx]);
1145  }
1146  else if (cost_fnc == INFIDELITY){
1147  double d = Umtx_deriv[idx].cols;
1148  QGD_Complex16 deriv_tmp = get_trace(Umtx_deriv[idx]);
1149  grad_comp = -2.0/d/(d+1)*trace_tmp[0].real*deriv_tmp.real-2.0/d/(d+1)*trace_tmp[0].imag*deriv_tmp.imag;
1150  }
1151  else {
1152  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1153  throw err;
1154  }
1155 
1156  grad[idx] = grad_comp;
1157 
1158 
1159 
1160  }
1161 
1162  });
1163 
1164  instance->number_of_iters = instance->number_of_iters + parameter_num_loc + 1;
1165 
1166  std::stringstream sstream;
1167  sstream << *f0 << std::endl;
1168  instance->print(sstream, 5);
1169 
1170 #ifdef __DFE__
1171 }
1172 #endif
1173 
1174 
1175 }
1176 
1177 
1178 
1186 void Optimization_Interface::optimization_problem_combined( Matrix_real parameters, void* void_instance, double* f0, Matrix_real& grad ){
1187  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1188  instance->optimization_problem_combined_non_static(parameters, void_instance, f0, grad );
1189  return;
1190 }
1191 
1192 
1201 
1202  optimization_problem_combined( parameters, this, f0, grad );
1203  return;
1204 }
1205 
1206 
1207 
1215 void Optimization_Interface::optimization_problem_combined_unitary( Matrix_real parameters, void* void_instance, Matrix& Umtx, std::vector<Matrix>& Umtx_deriv ) {
1216  // vector containing gradients of the transformed matrix
1217  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1218 
1219  int parallel = instance->get_parallel_configuration();
1220 
1221  if ( parallel ) {
1222 
1223  Matrix Umtx_loc = instance->get_Umtx();
1224  Umtx = Umtx_loc.copy();
1225  instance->apply_to( parameters, Umtx );
1226 
1227 
1228  Umtx_loc = instance->get_Umtx();
1229  Umtx_deriv = instance->apply_derivate_to( parameters, Umtx_loc, parallel );
1230 
1231  }
1232  else {
1233 
1234  tbb::parallel_invoke(
1235  [&]{
1236  Matrix Umtx_loc = instance->get_Umtx();
1237  Umtx = Umtx_loc.copy();
1238  instance->apply_to( parameters, Umtx );
1239  },
1240  [&]{
1241  Matrix Umtx_loc = instance->get_Umtx();
1242  Umtx_deriv = instance->apply_derivate_to( parameters, Umtx_loc, parallel );
1243  });
1244 
1245  }
1246 
1247 
1248 
1249 
1250 
1251 
1252 
1253 }
1254 
1255 
1262 void Optimization_Interface::optimization_problem_combined_unitary( Matrix_real parameters, Matrix& Umtx, std::vector<Matrix>& Umtx_deriv ) {
1263 
1264  optimization_problem_combined_unitary( parameters, this, Umtx, Umtx_deriv);
1265  return;
1266 
1267 }
1268 
1269 
1275 
1276  return cost_fnc;
1277 
1278 }
1279 
1280 
1285 void
1287 
1288  cost_fnc = variant;
1289 
1290  std::stringstream sstream;
1291  sstream << "Optimization_Interface::set_cost_function_variant: Cost function variant set to " << cost_fnc << std::endl;
1292  print(sstream, 2);
1293 
1294 
1295 }
1296 
1297 
1298 
1303 void Optimization_Interface::set_max_inner_iterations( int max_inner_iterations_in ) {
1304 
1305  max_inner_iterations = max_inner_iterations_in;
1306 
1307 }
1308 
1309 
1310 
1315 void Optimization_Interface::set_random_shift_count_max( int random_shift_count_max_in ) {
1316 
1317  random_shift_count_max = random_shift_count_max_in;
1318 
1319 }
1320 
1321 
1327 
1328  alg = alg_in;
1329 
1330  switch ( alg ) {
1331  case ADAM:
1332  max_inner_iterations = 1e5;
1333  random_shift_count_max = 100;
1335  return;
1336 
1337  case ADAM_BATCHED:
1338  max_inner_iterations = 2.5e3;
1341  return;
1342 
1343  case GRAD_DESCEND:
1344  max_inner_iterations = 10000;
1346  max_outer_iterations = 1e8;
1347  return;
1348 
1349  case COSINE:
1350  max_inner_iterations = 2.5e3;
1353  return;
1354 
1356  max_inner_iterations = 2.5e3;
1359  return;
1360 
1361  case AGENTS:
1362  max_inner_iterations = 2.5e3;
1365  return;
1366 
1367  case AGENTS_COMBINED:
1368  max_inner_iterations = 2.5e3;
1371  return;
1372 
1373  case BFGS:
1374  max_inner_iterations = 10000;
1376  max_outer_iterations = 1e8;
1377  return;
1378 
1379  case BFGS2:
1380  max_inner_iterations = 1e5;
1381  random_shift_count_max = 100;
1383  return;
1384 
1385  case BAYES_OPT:
1386  max_inner_iterations = 100;
1387  random_shift_count_max = 100;
1389  return;
1390  case BAYES_AGENTS:
1391  max_inner_iterations = 100;
1392  random_shift_count_max = 100;
1394  return;
1395 
1396  default:
1397  std::string error("Optimization_Interface::set_optimizer: unimplemented optimization algorithm");
1398  throw error;
1399  }
1400 
1401 
1402 
1403 }
1404 
1405 
1406 
1407 
1411 double
1413 
1414  return prev_cost_fnv_val;
1415 
1416 }
1417 
1418 
1419 
1424 double
1426 
1427  return correction1_scale;
1428 
1429 }
1430 
1431 
1432 
1437 double
1439 
1440  return correction2_scale;
1441 
1442 }
1443 
1444 
1445 
1446 
1447 
1448 
1452 int
1454 
1455  return number_of_iters;
1456 
1457 }
1458 
1459 
1464 void
1466 
1467  release_gates();
1468 
1469  set_qbit_num( gate_structure_in->get_qbit_num() );
1470 
1471  combine( gate_structure_in );
1472 
1473 }
1474 
1475 
1479 int
1481 
1482  return trace_offset;
1483 
1484 }
1485 
1486 
1490 void
1492 
1493 
1494  if ( (trace_offset_in + Umtx.cols) > Umtx.rows ) {
1495  std::string error("Optimization_Interface::set_trace_offset: trace offset must be smaller or equal to the difference of the rows and columns in the input unitary.");
1496  throw error;
1497 
1498  }
1499 
1500 
1501  trace_offset = trace_offset_in;
1502 
1503 
1504  std::stringstream sstream;
1505  sstream << "Optimization_Interface::set_trace_offset: trace offset set to " << trace_offset << std::endl;
1506  print(sstream, 2);
1507 
1508 }
1509 
1510 
1511 #ifdef __DFE__
1512 
1513 void
1514 Optimization_Interface::upload_Umtx_to_DFE() {
1515  if (Umtx.cols == Umtx.rows) {
1516  lock_lib();
1517 
1518  if ( get_initialize_id() != id ) {
1519  // initialize DFE library
1521  }
1522 
1524 
1525 
1526  unlock_lib();
1527  }
1528 
1529 }
1530 
1531 
1532 #endif
1533 
1537 int
1539 
1540  return accelerator_num;
1541 
1542 }
1543 
1544 
1545 
1546 
1547 
optimization_aglorithms alg
The optimization algorithm to be used in the optimization.
bool adaptive_eta
logical variable indicating whether adaptive learning reate is used in the ADAM algorithm ...
Header file for a class containing basic methods for the decomposition process.
void export_current_cost_fnc(double current_minimum)
Call to print out into a file the current cost function and the second Rényi entropy on the subsyste...
void print(const std::stringstream &sstream, int verbose_level=1) const
Call to print output messages in the function of the verbosity level.
Definition: logging.cpp:55
int get_num_iters()
Get the number of processed iterations during the optimization process.
void set_optimizer(optimization_aglorithms alg_in)
Call to set the optimizer engine to be used in solving the optimization problem.
void solve_layer_optimization_problem_AGENTS(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via the AGENT algorithm.
Definition: AGENTS.cpp:42
Matrix get_Umtx()
Call to retrive a pointer to the unitary to be transformed.
void set_custom_gate_structure(Gates_block *gate_structure_in)
Call to set custom layers to the gate structure that are intended to be used in the subdecomposition...
Matrix_real get_cost_function_with_correction(Matrix matrix, int qbit_num, int trace_offset=0)
Call co calculate the cost function of the optimization process, and the first correction to the cost...
double current_minimum
The current minimum of the optimization problem.
void add_gate(Gate *gate)
Append a general gate to the list of gates.
static void optimization_problem_combined_unitary(Matrix_real parameters, void *void_instance, Matrix &Umtx, std::vector< Matrix > &Umtx_deriv)
Call to calculate both the effect of the circuit on th eunitary and it&#39;s gradient componets...
double correction1_scale
prefactor of the single-bitflip errors in the cost function. (see Eq. (21) in arXiv:2210.09191)
void lock_lib()
Call to lock the access to the execution of the DFE library.
Definition: common_DFE.cpp:145
double get_correction2_scale()
Call to get the prefactor of the two-bitflip errors in the cost function.
cost_function_type cost_fnc
The chosen variant of the cost function.
void uploadMatrix2DFE(Matrix &input)
Call to upload the input matrix to the DFE engine.
Definition: common_DFE.cpp:65
void add_u1(int target_qbit)
Append a U1 gate to the list of gates.
std::vector< Gate * > get_gates()
Call to get the gates stored in the class.
void release_gates()
Call to release the stored gates.
int get_accelerator_num()
Get the number of accelerators to be reserved on DFEs on users demand.
double optimization_problem(double *parameters)
Evaluate the optimization problem of the optimization.
void solve_layer_optimization_problem_GRAD_DESCEND(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via the GRAD_DESCEND (line search in the direct...
void set_trace_offset(int trace_offset_in)
Set the trace offset used in the evaluation of the cost function.
double prev_cost_fnv_val
the previous value of the cost funtion to be used to evaluate bitflip errors in the cost funtion (see...
int trace_offset
The offset in the first columns from which the "trace" is calculated. In this case Tr(A) = sum_(i-off...
tbb::spin_mutex my_mutex_optimization_interface
std::map< int, int > max_layer_num
A map of <int n: int num> indicating that how many layers should be used in the subdecomposition proc...
void set_random_shift_count_max(int random_shift_count_max_in)
Call to set the maximal number of parameter randomization tries to escape a local minimum...
Matrix_real get_cost_function_with_correction2(Matrix matrix, int qbit_num, int trace_offset=0)
Call co calculate the cost function of the optimization process, and the first correction to the cost...
virtual void apply_to(Matrix_real &parameters_mtx, Matrix &input, int parallel=0)
Call to apply the gate on the input array/matrix Gates_block*input.
scalar * get_data() const
Call to get the pointer to the stored data.
virtual double optimization_problem_non_static(Matrix_real parameters, void *void_instance)
The optimization problem of the final optimization.
double correction2_scale
prefactor of the double-bitflip errors in the cost function. (see Eq. (21) in arXiv:2210.09191)
cost_function_type get_cost_function_variant()
Call to get the variant of the cost function used in the calculations.
int get_trace_offset()
Get the trace ffset used in the evaluation of the cost function.
void set_qbit_num(int qbit_num_in)
Set the number of qubits spanning the matrix of the gates stored in the block of gates.
int max_outer_iterations
Maximal number of iterations allowed in the optimization process.
void unlock_lib()
Call to unlock the access to the execution of the DFE library.
Definition: common_DFE.cpp:155
optimization_aglorithms
implemented optimization strategies
int LAPACKE_dgesv(int matrix_layout, int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ldb)
std::string project_name
the name of the project
int init_dfe_lib(const int accelerator_num, int qbit_num, int initialize_id_in)
Call to initialize the DFE library support and allocate the requested devices.
Definition: common_DFE.cpp:101
void set_max_inner_iterations(int max_inner_iterations_in)
Call to set the maximal number of iterations for which an optimization engine tries to solve the opti...
void solve_layer_optimization_problem_BAYES_AGENTS(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via Bayes & Agents algorithm.
gate_type type
The type of the operation (see enumeration gate_type)
Definition: Gate.h:83
QGD_Complex16 get_trace(Matrix &matrix)
Call to calculate the real and imaginary parts of the trace.
int get_parameter_num()
Call to get the number of free parameters.
double get_infidelity(Matrix &matrix)
Call to calculate infidelity.
double get_second_Renyi_entropy(Matrix_real &parameters_mtx, Matrix &input_state, matrix_base< int > &qbit_list)
Call to evaluate the seconf Rényi entropy.
int accelerator_num
number of utilized accelerators
int rows
The number of rows.
Definition: matrix_base.hpp:42
int cols
The number of columns.
Definition: matrix_base.hpp:44
int number_of_iters
number of iterations
virtual void add_finalyzing_layer()
Call to add further layer to the gate structure used in the subdecomposition.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
void combine(Gates_block *op_block)
Call to append the gates of an gate block to the current block.
double randomization_rate
randomization rate
std::map< int, int > iteration_loops
A map of <int n: int num> indicating the number of iteration in each step of the decomposition.
void solve_layer_optimization_problem_BFGS2(int num_of_parameters, Matrix_real solution_guess)
Call to solve layer by layer the optimization problem via BBFG algorithm.
Definition: BFGS2.cpp:42
double get_cost_function(Matrix matrix, int trace_offset=0)
Call co calculate the cost function during the final optimization process.
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
double CPU_time
time spent on optimization
A class containing basic methods for the decomposition process.
double circuit_simulation_time
Time spent on circuit simulation/cost function evaluation.
virtual std::vector< Matrix > apply_derivate_to(Matrix_real &parameters_mtx, Matrix &input, int parallel)
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...
decomposed_matrix
the unitary matrix from the result object
Definition: example.py:90
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:117
void solve_layer_optimization_problem_BFGS(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via BBFG algorithm.
Definition: BFGS.cpp:42
Class to store data of complex arrays and its properties.
Definition: matrix.h:38
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...
double get_previous_cost_function_value()
Call to retrieve the previous value of the cost funtion to be used to evaluate bitflip errors in the ...
void calc_decomposition_error(Matrix &decomposed_matrix)
Calculate the error of the decomposition according to the spectral norm of , where is the unitary pr...
int size() const
Call to get the number of the allocated elements.
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:64
cost_function_type
Type definition of the fifferent types of the cost function.
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:41
int get_initialize_id()
Call to get the identification number of the inititalization of the library.
Definition: common_DFE.cpp:190
guess_type
Type definition of the types of the initial guess.
Fixed point data related to a gate operation.
Definition: common_DFE.h:61
static void optimization_problem_combined(Matrix_real parameters, void *void_instance, double *f0, Matrix_real &grad)
Call to calculate both the cost function and the its gradient components.
std::map< std::string, Config_Element > config
config metadata utilized during the optimization
void solve_layer_optimization_problem_ADAM(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via ADAM algorithm.
void unload_dfe_lib()
Call to unload the DFE libarary and release the allocated devices.
Definition: common_DFE.cpp:78
double get_cost_function_sum_of_squares(Matrix &matrix)
static void optimization_problem_grad(Matrix_real parameters, void *void_instance, Matrix_real &grad)
Calculate the derivative of the cost function with respect to the free parameters.
int parameter_num
the number of free parameters of the operation
Definition: Gate.h:91
void solve_layer_optimization_problem(int num_of_parameters, Matrix_real solution_guess)
Call to solve layer by layer the optimization problem via calling one of the implemented algorithms...
Header file for the paralleized calculation of the cost function of the final optimization problem (s...
void solve_layer_optimization_problem_GRAD_DESCEND_PARAMETER_SHIFT_RULE(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via the GRAD_DESCEND_PARAMETER_SHIFT_RULE algor...
void solve_optimization_problem(double *solution_guess, int solution_guess_num)
This method can be used to solve the main optimization problem which is devidid into sub-layer optimi...
Matrix Umtx
The unitary to be decomposed.
virtual ~Optimization_Interface()
Destructor of the class.
double get_correction1_scale()
Call to get the prefactor of the single-bitflip errors in the cost function.
Header file for a class ???
int calcqgdKernelDFE(size_t rows, size_t cols, DFEgate_kernel_type *gates, int gatesNum, int gateSetNum, int traceOffset, double *trace)
Call to execute the calculation on the reserved DFE engines.
Definition: common_DFE.cpp:207
void solve_layer_optimization_problem_COSINE(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via the COSINE algorithm.
Definition: COSINE.cpp:43
Matrix copy()
Call to create a copy of the matrix.
Definition: matrix.cpp:105
static std::map< int, int > max_layer_num_def
A map of <int n: int num> indicating that how many layers should be used in the subdecomposition proc...
double real
the real part of a complex number
Definition: QGDTypes.h:40
void final_optimization()
final optimization procedure improving the accuracy of the decompositin when all the qubits were alre...
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:81
bool optimize_layer_num
logical value. Set true to optimize the minimum number of gate layers required in the decomposition...
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:504
void randomize_parameters(Matrix_real &input, Matrix_real &output, const double &f0)
Call to randomize the parameter.
bool check_optimization_solution()
check_optimization_solution
double decomposition_error
error of the final decomposition
int max_inner_iterations
the maximal number of iterations for which an optimization engine tries to solve the optimization pro...
void solve_layer_optimization_problem_AGENTS_COMBINED(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via the AGENT COMBINED algorithm.
Definition: AGENTS.cpp:920
int random_shift_count_max
the maximal number of parameter randomization tries to escape a local minimum.
Optimization_Interface()
Nullary constructor of the class.
void solve_layer_optimization_problem_ADAM_BATCHED(int num_of_parameters, Matrix_real &solution_guess_)
Call to solve layer by layer the optimization problem via batched ADAM algorithm. ...
Matrix_real optimized_parameters_mtx
The optimized parameters for the gates.
int get_parallel_configuration()
Get the parallel configuration from the config.
void set_cost_function_variant(cost_function_type variant)
Call to set the variant of the cost function used in the calculations.
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...
double radius
parameter to contron the radius of parameter randomization around the curren tminimum ...
Matrix_real optimization_problem_batched(std::vector< Matrix_real > &parameters_vec)
The cost function of the optimization with batched input (implemented only for the Frobenius norm cos...
double global_target_minimum
The global target minimum of the optimization problem.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:39
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.
virtual void optimization_problem_combined_non_static(Matrix_real parameters, void *void_instance, double *f0, Matrix_real &grad)
Call to calculate both the cost function and the its gradient components.
void solve_layer_optimization_problem_BAYES_OPT(int num_of_parameters, Matrix_real &solution_guess)
Call to solve layer by layer the optimization problem via Bayes algorithm.
std::mt19937 gen
Standard mersenne_twister_engine seeded with rd()
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42