Sequential Quantum Gate Decomposer  v1.9.7
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 "matrix_float.h"
26 #include "Adam.h"
27 #include "grad_descend.h"
28 #include "BFGS_Powell.h"
29 #include "Bayes_Opt.h"
30 
31 #include "RL_experience.h"
32 
33 #ifdef _WIN32
34 #include <cstdio>
35 #endif
36 
37 #include <fstream>
38 #include <tbb/enumerable_thread_specific.h>
39 #include <tbb/tick_count.h>
40 
41 extern "C" int LAPACKE_dgesv( int matrix_layout, int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ldb);
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.store(0, std::memory_order_relaxed);
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 
86 
87  // Time spent on circuit simulation/cost function evaluation
89  // time spent on optimization
90  CPU_time = 0.0;
91 
92 }
93 
94 
100 
103  id = other.id;
104 
107  alg = other.alg;
108  cost_fnc = other.cost_fnc;
112  use_cuts = other.use_cuts;
113  osr_rank = other.osr_rank;
114  use_softmax = other.use_softmax;
115  number_of_iters.store(other.number_of_iters.load(std::memory_order_relaxed), std::memory_order_relaxed);
116  adaptive_eta = other.adaptive_eta;
117  radius = other.radius;
120  trace_offset = other.trace_offset;
122  CPU_time = other.CPU_time;
123 
124 }
125 
134 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) {
135 
136  // 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).
137  optimize_layer_num = optimize_layer_num_in;
138 
139  // A string describing the type of the class
141 
142  // The global minimum of the optimization problem
144 
145  number_of_iters.store(0, std::memory_order_relaxed);
146 
147 
148  // number of iteratrion loops in the optimization
149  iteration_loops[2] = 3;
150 
151  // filling in numbers that were not given in the input
152  for ( std::map<int,int>::iterator it = max_layer_num_def.begin(); it!=max_layer_num_def.end(); it++) {
153  if ( max_layer_num.count( it->first ) == 0 ) {
154  max_layer_num.insert( std::pair<int, int>(it->first, it->second) );
155  }
156  }
157 
158  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
159  adaptive_eta = true;
160 
161  // parameter to contron the radius of parameter randomization around the curren tminimum
162  radius = 1.0;
163  randomization_rate = 0.3;
164 
165  // The chosen variant of the cost function
167 
168 
169  // variables to calculate the cost function with first and second corrections
170  prev_cost_fnv_val = 1.0;
171  correction1_scale = 1/1.7;
172  correction2_scale = 1/2.0;
173 
174 
175  // set the trace offset
176  trace_offset = 0;
177 
178  // unique id indentifying the instance of the class
179  std::uniform_int_distribution<> distrib_int(0, INT_MAX);
180  id = distrib_int(gen);
181 
182 
183 
184  // Time spent on circuit simulation/cost function evaluation
186  // time spent on optimization
187  CPU_time = 0.0;
188 
189 #if defined __DFE__
190  // number of utilized accelerators
191  accelerator_num = accelerator_num_in;
192 #elif defined __GROQ__
193  // number of utilized accelerators
194  accelerator_num = accelerator_num_in;
195 #else
196  accelerator_num = 0;
197 #endif
198 
199 }
200 
204 Optimization_Interface::Optimization_Interface( Matrix_float Umtx_in, int qbit_num_in, bool optimize_layer_num_in, std::map<std::string, Config_Element>& config, guess_type initial_guess_in, int accelerator_num_in ) : Decomposition_Base(Umtx_in, qbit_num_in, config, initial_guess_in) {
205 
206  // 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).
207  optimize_layer_num = optimize_layer_num_in;
208 
209  // A string describing the type of the class
211 
212  // The global minimum of the optimization problem
214 
215  number_of_iters.store(0, std::memory_order_relaxed);
216 
217  // number of iteratrion loops in the optimization
218  iteration_loops[2] = 3;
219 
220  // filling in numbers that were not given in the input
221  for ( std::map<int,int>::iterator it = max_layer_num_def.begin(); it!=max_layer_num_def.end(); it++) {
222  if ( max_layer_num.count( it->first ) == 0 ) {
223  max_layer_num.insert( std::pair<int, int>(it->first, it->second) );
224  }
225  }
226 
227  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
228  adaptive_eta = true;
229 
230  // parameter to contron the radius of parameter randomization around the curren tminimum
231  radius = 1.0;
232  randomization_rate = 0.3;
233 
234  // The chosen variant of the cost function
236 
237  // variables to calculate the cost function with first and second corrections
238  prev_cost_fnv_val = 1.0;
239  correction1_scale = 1/1.7;
240  correction2_scale = 1/2.0;
241 
242  // set the trace offset
243  trace_offset = 0;
244 
245  // unique id indentifying the instance of the class
246  std::uniform_int_distribution<> distrib_int(0, INT_MAX);
247  id = distrib_int(gen);
248 
249  // Time spent on circuit simulation/cost function evaluation
251  // time spent on optimization
252  CPU_time = 0.0;
253 
254 #if defined __DFE__
255  // number of utilized accelerators
256  accelerator_num = accelerator_num_in;
257 #elif defined __GROQ__
258  // number of utilized accelerators
259  accelerator_num = accelerator_num_in;
260 #else
261  accelerator_num = 0;
262 #endif
263 
264 }
265 
266 
267 
272 
273 
274 #ifdef __DFE__
275  if ( Umtx.cols == Umtx.rows && qbit_num >= 2 && get_accelerator_num() > 0 ) {
276  unload_dfe_lib();//releive_DFE();
277  }
278 #endif
279 
280 
281 
282 }
283 
284 
291 
292  if ( this == &other ) {
293  return *this;
294  }
295 
296  Decomposition_Base::operator=(other);
297 
300  id = other.id;
301 
304  alg = other.alg;
305  cost_fnc = other.cost_fnc;
309  use_cuts = other.use_cuts;
310  osr_rank = other.osr_rank;
311  use_softmax = other.use_softmax;
312  number_of_iters.store(other.number_of_iters.load(std::memory_order_relaxed), std::memory_order_relaxed);
313  adaptive_eta = other.adaptive_eta;
314  radius = other.radius;
317  trace_offset = other.trace_offset;
319  CPU_time = other.CPU_time;
320 
321  return *this;
322 
323 }
324 
325 
331 
333 
334 }
335 
336 
337 
344 
345  FILE* pFile;
346  std::string filename("costfuncs_and_entropy.txt");
347 
348  if (project_name != "") {
349  filename = project_name + "_" + filename;
350  }
351 
352  const char* c_filename = filename.c_str();
353 #ifdef _WIN32
354  errno_t err = fopen_s(&pFile, c_filename, "a");
355  if (err != 0) {
356  pFile = NULL;
357  }
358 #else
359  pFile = fopen(c_filename, "a");
360 #endif
361 
362  if (pFile==NULL) {
363  fputs ("File error",stderr);
364  std::string error("Cannot open file.");
365  throw error;
366  }
367 
368  Matrix input_state(Power_of_2(qbit_num),1);
369 
370  std::uniform_int_distribution<> distrib(0, qbit_num-2);
371 
372  memset(input_state.get_data(), 0, (input_state.size()*2)*sizeof(double) );
373  input_state[0].real = 1.0;
374 
375  matrix_base<int> qbit_sublist(1,2);
376  qbit_sublist[0] = 0;//distrib(gen);
377  qbit_sublist[1] = 1;//qbit_sublist[0]+1;
378 
379  double renyi_entropy = get_second_Renyi_entropy(parameters, input_state, qbit_sublist);
380 
381  fprintf(pFile,"%i\t%f\t%f\n", number_of_iters.load(std::memory_order_relaxed), current_minimum, renyi_entropy);
382  fclose(pFile);
383 
384  return;
385 }
386 
387 
395 
396 
397  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
398 
399  instance->export_current_cost_fnc( current_minimum, parameters );
400 
401 }
402 
403 
407 void
409 
410 
411  // creating block of gates
412  Gates_block* block = new Gates_block( qbit_num );
413 
414  // adding U1 gates for final phase corrections
415  for (int qbit=0; qbit<qbit_num; qbit++) {
416  block->add_u1(qbit);
417  }
418 
419  // adding the opeartion block to the gates
420  add_gate( block );
421 
422 }
423 
424 
425 
426 
432 void
434 
435 
436  // the norm is the square root of the largest einegvalue.*/
437  switch (cost_fnc) {
438  case FROBENIUS_NORM:
439  decomposition_error = get_cost_function(decomposed_matrix);
440  break;
442  Matrix_real&& ret = get_cost_function_with_correction(decomposed_matrix, qbit_num);
443  decomposition_error = ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale;
444  break; }
446  Matrix_real&& ret = get_cost_function_with_correction2(decomposed_matrix, qbit_num);
447  decomposition_error = ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale);
448  break; }
450  decomposition_error = get_hilbert_schmidt_test(decomposed_matrix);
451  break;
453  Matrix&& ret = get_trace_with_correction(decomposed_matrix, qbit_num);
454  double d = 1.0/decomposed_matrix.cols;
455  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));
456  break;
457  }
459  Matrix&& ret = get_trace_with_correction2(decomposed_matrix, qbit_num);
460  double d = 1.0/decomposed_matrix.cols;
461  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)));
462  break;
463  }
464  case SUM_OF_SQUARES:
466  break;
467  case INFIDELITY:
468  decomposition_error = get_infidelity(decomposed_matrix);
469  break;
470  case OSR_ENTANGLEMENT:
472  break;
473  default: {
474  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
475  throw err;
476  } }
477 
478 }
479 
480 
481 
486 
487  //The stringstream input to store the output messages.
488  std::stringstream sstream;
489  sstream << "***************************************************************" << std::endl;
490  sstream << "Final fine tuning of the parameters in the " << qbit_num << "-qubit decomposition" << std::endl;
491  sstream << "***************************************************************" << std::endl;
492  print(sstream, 1);
493 
494 
495 
496 
497  //# setting the global minimum
499 
500  if ( optimized_parameters_mtx.size() == 0 ) {
502  }
503  else {
505  if ( check_optimization_solution() ) return;
506 
508  }
509 }
510 
511 
512 
513 
520 
521 
522  switch ( alg ) {
523  case ADAM:
524  solve_layer_optimization_problem_ADAM( num_of_parameters, solution_guess);
525  break;
526  case ADAM_BATCHED:
527  solve_layer_optimization_problem_ADAM_BATCHED( num_of_parameters, solution_guess);
528  break;
529  case GRAD_DESCEND:
530  solve_layer_optimization_problem_GRAD_DESCEND( num_of_parameters, solution_guess);
531  break;
532  case AGENTS:
533  solve_layer_optimization_problem_AGENTS( num_of_parameters, solution_guess);
534  break;
535  case COSINE:
536  solve_layer_optimization_problem_COSINE( num_of_parameters, solution_guess);
537  break;
539  solve_layer_optimization_problem_GRAD_DESCEND_PARAMETER_SHIFT_RULE( num_of_parameters, solution_guess);
540  break;
541  case AGENTS_COMBINED:
542  solve_layer_optimization_problem_AGENTS_COMBINED( num_of_parameters, solution_guess);
543  break;
544  case BFGS:
545  solve_layer_optimization_problem_BFGS( num_of_parameters, solution_guess);
546  break;
547  case BAYES_OPT:
548  solve_layer_optimization_problem_BAYES_OPT( num_of_parameters, solution_guess);
549  break;
550  case BAYES_AGENTS:
551  solve_layer_optimization_problem_BAYES_AGENTS( num_of_parameters, solution_guess);
552  break;
553  case BFGS2:
554  solve_layer_optimization_problem_BFGS2( num_of_parameters, solution_guess);
555  break;
556  default:
557  std::string error("Optimization_Interface::solve_layer_optimization_problem: unimplemented optimization algorithm");
558  throw error;
559  }
560 
561  if ( use_float ) {
563  for (int idx=0; idx<optimized_parameters_mtx.size(); idx++) {
564  optimized_parameters_mtx_float[idx] = static_cast<float>(optimized_parameters_mtx[idx]);
565  }
566  }
567 
568 }
569 
577 
578  // random generator of real numbers
579  std::uniform_real_distribution<> distrib_prob(0.0, 1.0);
580  std::uniform_real_distribution<> distrib_real(-2*M_PI, 2*M_PI);
581 
582 
583  double radius_loc;
584  if ( config.count("Randomized_Radius") > 0 ) {
585  config["Randomized_Radius"].get_property( radius_loc );
586  }
587  else {
588  radius_loc = radius;
589  }
590 
591  const int num_of_parameters = input.size();
592 
593  int changed_parameters = 0;
594  for ( int jdx=0; jdx<num_of_parameters; jdx++) {
595  if ( distrib_prob(gen) <= randomization_rate ) {
596  output[jdx] = (cost_fnc!=VQE) ? input[jdx] + distrib_real(gen)*std::sqrt(f0)*radius_loc : input[jdx] + distrib_real(gen)*radius_loc;
597  changed_parameters++;
598  }
599  else {
600  output[jdx] = input[jdx];
601  }
602  }
603 
604 #ifdef __MPI__
605  //MPI_Bcast( (void*)output.get_data(), num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
606 #endif
607 
608 
609 }
610 
611 
618 
619  // get the transformed matrix with the gates in the list
620  Matrix_real parameters_mtx(parameters, 1, parameter_num );
621 
622 
623  return optimization_problem( parameters_mtx );
624 
625 
626 }
627 
628 
635 
636  // get the transformed matrix with the gates in the list
637  if ( parameters.size() != parameter_num ) {
638  std::stringstream sstream;
639  sstream << "Optimization_Interface::optimization_problem: Number of free paramaters should be " << parameter_num << ", but got " << parameters.size() << std::endl;
640  print(sstream, 0);
641  std::string err("Optimization_Interface::optimization_problem: Wrong number of parameters.");
642  throw err;
643  }
644 
645  // Float32 circuit application is useful for experimental hot paths, but
646  // optimizer cost values must stay in double precision. The objective and
647  // gradients guide convergence and fidelity; evaluating them through
648  // Matrix_float introduces enough drift to steer the solver to bad minima.
649  const bool use_float_cost_path = false;
650  if ( use_float_cost_path && use_float ) {
651  static tbb::enumerable_thread_specific<Matrix_real_float> parameters_float_tls;
652  static tbb::enumerable_thread_specific<Matrix_float> matrix_new_tls;
653  Matrix_real_float& parameters_float = parameters_float_tls.local();
654  Matrix_float& matrix_new = matrix_new_tls.local();
655  parameters.copy_to(parameters_float);
656  Umtx_float.copy_to(matrix_new);
657  Gates_block::apply_to( parameters_float, matrix_new );
658  return calculate_cost_function(matrix_new, NULL);
659  }
660 
661  static tbb::enumerable_thread_specific<Matrix> matrix_new_tls;
662  Matrix& matrix_new = matrix_new_tls.local();
663  Umtx.copy_to(matrix_new);
664  Gates_block::apply_to( parameters, matrix_new );
665 
666  return calculate_cost_function(matrix_new, NULL);
667 
668 }
669 
670 
678 
679  switch (cost_fnc) {
680  case FROBENIUS_NORM:
681  return get_cost_function(matrix_new, trace_offset);
684  return ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale; }
687  return ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale); }
689  if ( ret_temp != NULL ) {
690  QGD_Complex16 trace_temp = get_trace(matrix_new);
691  (*ret_temp)[0].real = trace_temp.real;
692  (*ret_temp)[0].imag = trace_temp.imag;
693  double d = 1.0/matrix_new.cols;
694  return 1 - d*d*(trace_temp.real*trace_temp.real + trace_temp.imag*trace_temp.imag);
695  }
696  return get_hilbert_schmidt_test(matrix_new);
698  Matrix&& ret = get_trace_with_correction(matrix_new, qbit_num);
699  double d = 1.0/matrix_new.cols;
700  if ( ret_temp != NULL ) {
701  for (int idx=0; idx<3; idx++) {
702  (*ret_temp)[idx].real = ret[idx].real;
703  (*ret_temp)[idx].imag = ret[idx].imag;
704  }
705  }
706  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)); }
708  Matrix&& ret = get_trace_with_correction2(matrix_new, qbit_num);
709  double d = 1.0/matrix_new.cols;
710  if ( ret_temp != NULL ) {
711  for (int idx=0; idx<4; idx++) {
712  (*ret_temp)[idx].real = ret[idx].real;
713  (*ret_temp)[idx].imag = ret[idx].imag;
714  }
715  }
716  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))); }
717  case SUM_OF_SQUARES:
718  return get_cost_function_sum_of_squares(matrix_new);
719  case INFIDELITY:
720  if ( ret_temp != NULL ) {
721  QGD_Complex16 trace_temp = get_trace(matrix_new);
722  (*ret_temp)[0].real = trace_temp.real;
723  (*ret_temp)[0].imag = trace_temp.imag;
724  double d = matrix_new.cols;
725  return 1.0-((trace_temp.real*trace_temp.real+trace_temp.imag*trace_temp.imag)/d+1)/(d+1);
726  }
727  return get_infidelity(matrix_new);
728  case OSR_ENTANGLEMENT:
730  default: {
731  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
732  throw err;
733  } }
734 
735 }
736 
738 
739  // Experimental helper only. Optimizer objective/gradient callbacks must
740  // use calculate_cost_function(Matrix&, ...) so the solver is guided by
741  // double precision cost values.
742  switch (cost_fnc) {
743  case FROBENIUS_NORM:
744  return get_cost_function(matrix_new, trace_offset);
747  return ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale; }
750  return ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale); }
751  case HILBERT_SCHMIDT_TEST: {
752  QGD_Complex16 trace_temp = get_trace(matrix_new);
753  if ( ret_temp != NULL ) {
754  (*ret_temp)[0].real = static_cast<float>(trace_temp.real);
755  (*ret_temp)[0].imag = static_cast<float>(trace_temp.imag);
756  }
757  double d = 1.0/matrix_new.cols;
758  return 1 - d*d*(trace_temp.real*trace_temp.real + trace_temp.imag*trace_temp.imag); }
759  case INFIDELITY: {
760  QGD_Complex16 trace_temp = get_trace(matrix_new);
761  if ( ret_temp != NULL ) {
762  (*ret_temp)[0].real = static_cast<float>(trace_temp.real);
763  (*ret_temp)[0].imag = static_cast<float>(trace_temp.imag);
764  }
765  double d = matrix_new.cols;
766  return 1.0-((trace_temp.real*trace_temp.real+trace_temp.imag*trace_temp.imag)/d+1)/(d+1); }
769  case SUM_OF_SQUARES:
770  case OSR_ENTANGLEMENT: {
771  if (cost_fnc == OSR_ENTANGLEMENT) {
773  }
774  Matrix matrix_new64 = matrix_new.to_float64();
775  return calculate_cost_function(matrix_new64, NULL); }
776  default: {
777  std::string err("Optimization_Interface::calculate_cost_function(Matrix_float&): Cost function variant not implmented.");
778  throw err;
779  } }
780 
781 }
782 
783 #ifdef __DFE__
784 
790 Optimization_Interface::optimization_problem_batched_DFE( std::vector<Matrix_real>& parameters_vec) {
791 
792 
793  Matrix_real cost_fnc_mtx(parameters_vec.size(), 1);
794 
795  int gatesNum, gateSetNum, redundantGateSets;
796  DFEgate_kernel_type* DFEgates = convert_to_batched_DFE_gates( parameters_vec, gatesNum, gateSetNum, redundantGateSets );
797 
798  Matrix_real trace_DFE_mtx(gateSetNum, 3);
799 
800 
801 
802  increment_num_iters(static_cast<int>(parameters_vec.size()));
803 
804 
805 
806 #ifdef __MPI__
807  // the number of decomposing layers are divided between the MPI processes
808 
809  int mpi_gateSetNum = gateSetNum / world_size;
810  int mpi_starting_gateSetIdx = gateSetNum/world_size * current_rank;
811 
812  Matrix_real mpi_trace_DFE_mtx(mpi_gateSetNum, 3);
813 
814 
815  increment_num_iters(mpi_gateSetNum);
816 
817 
818  {
819  DFE_Lib_Read_Lock dfe_lock;
820  calcqgdKernelDFE( Umtx.rows, Umtx.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, trace_offset, mpi_trace_DFE_mtx.get_data() );
821  }
822 
823  int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
824  MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
825 
826 #else
827  {
828  DFE_Lib_Read_Lock dfe_lock;
829  calcqgdKernelDFE( Umtx.rows, Umtx.cols, DFEgates, gatesNum, gateSetNum, trace_offset, trace_DFE_mtx.get_data() );
830  }
831 
832 #endif // __MPI__
833 
834 
835  // calculate the cost function
836  if ( cost_fnc == FROBENIUS_NORM ) {
837  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
838  cost_fnc_mtx[idx] = 1-trace_DFE_mtx[idx*3]/Umtx.cols;
839  }
840  }
841  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
842  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
843  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;
844  }
845  }
846  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
847  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
848  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;
849  }
850  }
851  else {
852  std::string err("Optimization_Interface::optimization_problem_batched: Cost function variant not implmented for DFE.");
853  throw err;
854  }
855 
856 
857 
858 
859 
860  delete[] DFEgates;
861 
862  return cost_fnc_mtx;
863 
864 }
865 #endif
866 
867 
868 
869 #ifdef __GROQ__
870 
871 
878 double
879 Optimization_Interface::optimization_problem_Groq( Matrix_real& parameters, int chosen_device) {
880 
881  throw std::string("Optimization_Interface::optimization_problem_Groq should be implemented in derrived classes.");
882 
883  return 0.0;
884 
885 }
886 
887 
888 
895 Optimization_Interface::optimization_problem_batched_Groq( std::vector<Matrix_real>& parameters_vec) {
896 
897  int task_num = parameters_vec.size();
898 
899  Matrix_real cost_fnc_mtx(task_num, 1);
900 
901  if ( get_initialize_id() != id ) {
903  }
904 
905  if ( accelerator_num == 1 ) {
906 
907  int chosen_device = 0;
908 
909  for( int idx=0; idx<task_num; idx++ ) {
910  cost_fnc_mtx[idx] = optimization_problem_Groq( parameters_vec[idx], chosen_device );
911  }
912 
913  }
914  else if ( accelerator_num == 2 ) {
915 
916  for( int idx=0; idx<task_num; idx=idx+2 ) {
917  tbb::parallel_invoke(
918  [&]() { cost_fnc_mtx[idx] = optimization_problem_Groq( parameters_vec[idx], 0 ); },
919  [&]() { if ( (idx+1) < task_num ) cost_fnc_mtx[idx+1] = optimization_problem_Groq( parameters_vec[idx+1], 1 ); }
920  );
921  }
922 
923  }
924  else {
925  throw std::string("Unsupported number of Groq accelerators.");
926  }
927 
928  return cost_fnc_mtx;
929 
930 
931 }
932 #endif
933 
940 Optimization_Interface::optimization_problem_batched( std::vector<Matrix_real>& parameters_vec) {
941 
942  tbb::tick_count t0_circuit_simulation = tbb::tick_count::now();
943 
944 #if defined __DFE__
945  if ( Umtx.cols == Umtx.rows && get_accelerator_num() > 0 ) {
946  Matrix_real cost_fnc_mtx = optimization_problem_batched_DFE( parameters_vec );
947  circuit_simulation_time += (tbb::tick_count::now() - t0_circuit_simulation).seconds();
948  return cost_fnc_mtx;
949  }
950 #elif defined __GROQ__
951  if ( Umtx.cols == 1 && get_accelerator_num() > 0 ) {
952  Matrix_real cost_fnc_mtx = optimization_problem_batched_Groq( parameters_vec );
953  circuit_simulation_time += (tbb::tick_count::now() - t0_circuit_simulation).seconds();
954  return cost_fnc_mtx;
955  }
956 #endif
957 
958 
959  Matrix_real cost_fnc_mtx(static_cast<int>(parameters_vec.size()), 1);
960  int parallel = get_parallel_configuration();
961 
962 #ifdef __MPI__
963 
964 
965  // the number of decomposing layers are divided between the MPI processes
966 
967  int batch_element_num = parameters_vec.size();
968  int mpi_batch_element_num = batch_element_num / world_size;
969  int mpi_batch_element_remainder = batch_element_num % world_size;
970 
971  if ( mpi_batch_element_remainder > 0 ) {
972  std::string err("Optimization_Interface::optimization_problem_batched: The size of the batch should be divisible with the number of processes.");
973  throw err;
974  }
975 
976  int mpi_starting_batchIdx = mpi_batch_element_num * current_rank;
977 
978 
979  Matrix_real cost_fnc_mtx_loc(mpi_batch_element_num, 1);
980 
981  auto calculate_local_batch_element = [&](int idx) {
982  cost_fnc_mtx_loc[idx] = optimization_problem( parameters_vec[idx + mpi_starting_batchIdx] );
983  };
984 
985  if ( parallel == 0 ) {
986  for (int idx=0; idx<mpi_batch_element_num; ++idx) {
987  calculate_local_batch_element(idx);
988  }
989  }
990  else {
991  int work_batch = 1;
992  tbb::parallel_for( tbb::blocked_range<int>(0, (int)mpi_batch_element_num, work_batch), [&](tbb::blocked_range<int> r) {
993  for (int idx=r.begin(); idx<r.end(); ++idx) {
994  calculate_local_batch_element(idx);
995  }
996  });
997  }
998 
999  //number_of_iters = number_of_iters + mpi_batch_element_num;
1000 
1001 
1002 
1003  int bytes = cost_fnc_mtx_loc.size()*sizeof(double);
1004  MPI_Allgather(cost_fnc_mtx_loc.get_data(), bytes, MPI_BYTE, cost_fnc_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
1005 
1006 
1007 #else
1008 
1009  auto calculate_batch_element = [&](int idx) {
1010  cost_fnc_mtx[idx] = optimization_problem( parameters_vec[idx] );
1011  };
1012 
1013  if ( parallel == 0 ) {
1014  for (int idx=0; idx<(int)parameters_vec.size(); ++idx) {
1015  calculate_batch_element(idx);
1016  }
1017  }
1018  else {
1019  int work_batch = 1;
1020  tbb::parallel_for( tbb::blocked_range<int>(0, (int)parameters_vec.size(), work_batch), [&](tbb::blocked_range<int> r) {
1021  for (int idx=r.begin(); idx<r.end(); ++idx) {
1022  calculate_batch_element(idx);
1023  }
1024  });
1025  }
1026 
1027 
1028 #endif // __MPI__
1029 
1030  circuit_simulation_time += (tbb::tick_count::now() - t0_circuit_simulation).seconds();
1031  return cost_fnc_mtx;
1032 
1033 }
1034 
1035 
1036 
1037 
1038 
1046 double Optimization_Interface::optimization_problem( Matrix_real parameters, void* void_instance, Matrix ret_temp) {
1047 
1048  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1049  instance->increment_num_iters();
1050 
1051  // Keep the Matrix_float path compiled for experiments, but never use it
1052  // for optimizer objective evaluations. Cost/trace calculations need
1053  // double precision even when float32 circuit application is enabled
1054  // elsewhere.
1055  const bool use_float_cost_path = false;
1056  if ( use_float_cost_path && instance->get_use_float() ) {
1057  static tbb::enumerable_thread_specific<Matrix_real_float> parameters_float_tls;
1058  static tbb::enumerable_thread_specific<Matrix_float> matrix_new_tls;
1059  static tbb::enumerable_thread_specific<Matrix_float> ret_temp_float_tls;
1060  Matrix_real_float& parameters_float = parameters_float_tls.local();
1061  Matrix_float& matrix_new = matrix_new_tls.local();
1062  Matrix_float& ret_temp_float = ret_temp_float_tls.local();
1063  parameters.copy_to(parameters_float);
1064  instance->Umtx_float.copy_to(matrix_new);
1065  instance->Gates_block::apply_to( parameters_float, matrix_new );
1066  if (ret_temp_float.rows != ret_temp.rows || ret_temp_float.cols != ret_temp.cols || ret_temp_float.stride != ret_temp.stride) {
1067  ret_temp_float = Matrix_float(ret_temp.rows, ret_temp.cols, ret_temp.stride);
1068  }
1069  return instance->calculate_cost_function(matrix_new, &ret_temp_float);
1070  }
1071 
1072  static tbb::enumerable_thread_specific<Matrix> matrix_new_tls;
1073  Matrix& matrix_new = matrix_new_tls.local();
1074  instance->Umtx.copy_to(matrix_new);
1075  instance->Gates_block::apply_to( parameters, matrix_new );
1076 
1077  return instance->calculate_cost_function(matrix_new, &ret_temp);
1078 
1079 
1080 }
1081 
1082 
1083 
1091 
1092  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1093  Matrix ret(1,3);
1094  double cost_func = instance->optimization_problem(parameters, void_instance, ret);
1095  return cost_func;
1096 }
1097 
1098 
1099 
1100 
1107 double Optimization_Interface::optimization_problem( Matrix_real parameters, void* void_instance){
1108  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1109  return instance->optimization_problem_non_static(parameters, void_instance);
1110 }
1111 
1112 
1113 
1114 
1115 
1116 
1117 
1124 void Optimization_Interface::optimization_problem_grad( Matrix_real parameters, void* void_instance, Matrix_real& grad ) {
1125 
1126  // The function value at x0
1127  double f0;
1128 
1129  // calculate the approximate gradient
1130  optimization_problem_combined( parameters, void_instance, &f0, grad);
1131 
1132 }
1133 
1134 
1135 
1136 
1137 
1145 void Optimization_Interface::optimization_problem_combined_non_static( Matrix_real parameters, void* void_instance, double* f0, Matrix_real& grad ) {
1146 
1147  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1148 
1149  int parallel = instance->get_parallel_configuration();
1150 
1151  // the number of free parameters
1152  int parameter_num_loc = instance->get_parameter_num();
1153 
1154  // the variant of the cost function
1156 
1157  // value of the cost function from the previous iteration to weigth the correction to the trace
1159  double correction1_scale = instance->get_correction1_scale();
1160  double correction2_scale = instance->get_correction2_scale();
1161 
1162  int qbit_num = instance->get_qbit_num();
1163  int trace_offset_loc = instance->get_trace_offset();
1164 
1165  // Gradient-driven optimization is sensitive to low-precision objective
1166  // values. Leave this float32 combined path in place as experimental code,
1167  // but force production optimizer cost/gradient evaluation through the
1168  // double precision branch below.
1169  const bool use_float_combined_cost_path = false;
1170  if ( use_float_combined_cost_path && instance->get_use_float() ) {
1171  static tbb::enumerable_thread_specific<Matrix_real_float> parameters_float_tls;
1172  Matrix_real_float& parameters_float = parameters_float_tls.local();
1173  parameters.copy_to(parameters_float);
1174  Matrix_float Umtx_loc = instance->get_Umtx_float();
1175  static tbb::enumerable_thread_specific<std::vector<Matrix_float>> combined_result_tls;
1176  std::vector<Matrix_float>& combined_result = combined_result_tls.local();
1177  instance->Gates_block::apply_to_combined( parameters_float, Umtx_loc, parallel, combined_result );
1178  Matrix_float& matrix_new = combined_result[0];
1179 
1180  Matrix_float trace_tmp(1,3);
1181  *f0 = instance->calculate_cost_function(matrix_new, &trace_tmp);
1182 
1183  Matrix Upartial;
1184  Matrix_float Upartial_float;
1185  Matrix matrix_new64;
1186  if (cost_fnc == SUM_OF_SQUARES) {
1187  matrix_new64 = matrix_new.to_float64();
1188  Upartial = get_deriv_sum_of_squares(matrix_new64);
1189  }
1190  else if (cost_fnc == OSR_ENTANGLEMENT) {
1191  Upartial_float = get_deriv_osr_entanglement(matrix_new, use_cuts, osr_rank, use_softmax);
1192  }
1193 
1194  auto calculate_gradient_component = [&](int idx) {
1195  double grad_comp;
1196  Matrix_float& deriv_mtx = combined_result[static_cast<size_t>(idx) + 1];
1197  switch (cost_fnc) {
1198  case FROBENIUS_NORM:
1199  grad_comp = (get_cost_function(deriv_mtx, trace_offset_loc) - 1.0);
1200  break;
1202  Matrix_real_float deriv_tmp = get_cost_function_with_correction(deriv_mtx, qbit_num, trace_offset_loc);
1203  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*deriv_tmp[1]*correction1_scale - 1.0);
1204  break;
1205  }
1207  Matrix_real_float deriv_tmp = get_cost_function_with_correction2(deriv_mtx, qbit_num, trace_offset_loc);
1208  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*(deriv_tmp[1]*correction1_scale + deriv_tmp[2]*correction2_scale) - 1.0);
1209  break;
1210  }
1211  case HILBERT_SCHMIDT_TEST: {
1212  double d = 1.0/deriv_mtx.cols;
1213  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1214  grad_comp = -2.0*d*d*trace_tmp[0].real*deriv_tmp.real-2.0*d*d*trace_tmp[0].imag*deriv_tmp.imag;
1215  break;
1216  }
1217  case INFIDELITY: {
1218  double d = deriv_mtx.cols;
1219  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1220  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;
1221  break;
1222  }
1223  case SUM_OF_SQUARES: {
1224  Matrix deriv64 = deriv_mtx.to_float64();
1225  grad_comp = real_trace_conj_dot(Upartial, deriv64);
1226  break;
1227  }
1228  case OSR_ENTANGLEMENT:
1229  grad_comp = real_trace_conj_dot(Upartial_float, deriv_mtx);
1230  break;
1233  Matrix deriv64 = deriv_mtx.to_float64();
1234  Matrix matrix_new_for_trace = matrix_new.to_float64();
1235  Matrix trace_tmp64(1,3);
1236  instance->calculate_cost_function(matrix_new_for_trace, &trace_tmp64);
1237  if (cost_fnc == HILBERT_SCHMIDT_TEST_CORRECTION1) {
1238  Matrix&& deriv_tmp = get_trace_with_correction(deriv64, qbit_num);
1239  double d = 1.0/deriv64.cols;
1240  grad_comp = -2.0*d*d*(trace_tmp64[0].real*deriv_tmp[0].real+trace_tmp64[0].imag*deriv_tmp[0].imag+std::sqrt(prev_cost_fnv_val)*correction1_scale*(trace_tmp64[1].real*deriv_tmp[1].real+trace_tmp64[1].imag*deriv_tmp[1].imag));
1241  }
1242  else {
1243  Matrix&& deriv_tmp = get_trace_with_correction2(deriv64, qbit_num);
1244  double d = 1.0/deriv64.cols;
1245  grad_comp = -2.0*d*d*(trace_tmp64[0].real*deriv_tmp[0].real+trace_tmp64[0].imag*deriv_tmp[0].imag+std::sqrt(prev_cost_fnv_val)*(correction1_scale*(trace_tmp64[1].real*deriv_tmp[1].real+trace_tmp64[1].imag*deriv_tmp[1].imag) + correction2_scale*(trace_tmp64[2].real*deriv_tmp[2].real+trace_tmp64[2].imag*deriv_tmp[2].imag)));
1246  }
1247  break;
1248  }
1249  default: {
1250  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1251  throw err;
1252  } }
1253  grad[idx] = grad_comp;
1254  };
1255 
1256  if ( parallel == 0 ) {
1257  for (int idx=0; idx<parameter_num_loc; ++idx) {
1258  calculate_gradient_component(idx);
1259  }
1260  }
1261  else {
1262  int work_batch = 10;
1263  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,work_batch), [&](tbb::blocked_range<int> r) {
1264  for (int idx=r.begin(); idx<r.end(); ++idx) {
1265  calculate_gradient_component(idx);
1266  }
1267  });
1268  }
1269 
1270  instance->increment_num_iters(parameter_num_loc + 1);
1271  std::stringstream sstream;
1272  sstream << *f0 << std::endl;
1273  instance->print(sstream, 5);
1274  return;
1275  }
1276 
1277 #ifdef __DFE__
1278 
1280 //std::cout << "number of qubits: " << instance->qbit_num << std::endl;
1281 //tbb::tick_count t0_DFE = tbb::tick_count::now();/////////////////////////////////
1282 if ( Umtx.cols == Umtx.rows && instance->qbit_num >= 5 && instance->get_accelerator_num() > 0 ) {
1283 
1284  int gatesNum, redundantGateSets, gateSetNum;
1285  DFEgate_kernel_type* DFEgates = instance->convert_to_DFE_gates_with_derivates( parameters, gatesNum, gateSetNum, redundantGateSets );
1286 
1287  Matrix&& Umtx_loc = instance->get_Umtx();
1288  Matrix_real trace_DFE_mtx(gateSetNum, 3);
1289 
1290 
1291 #ifdef __MPI__
1292  // the number of decomposing layers are divided between the MPI processes
1293 
1294  int mpi_gateSetNum = gateSetNum / instance->world_size;
1295  int mpi_starting_gateSetIdx = gateSetNum/instance->world_size * instance->current_rank;
1296 
1297  Matrix_real mpi_trace_DFE_mtx(mpi_gateSetNum, 3);
1298 
1299  instance->increment_num_iters(mpi_gateSetNum);
1300 
1301  {
1302  DFE_Lib_Read_Lock dfe_lock;
1303  calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, trace_offset_loc, mpi_trace_DFE_mtx.get_data() );
1304  }
1305 
1306  int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
1307  MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
1308 
1309 #else
1310 
1311  instance->increment_num_iters(gateSetNum);
1312 
1313  {
1314  DFE_Lib_Read_Lock dfe_lock;
1315  calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates, gatesNum, gateSetNum, trace_offset_loc, trace_DFE_mtx.get_data() );
1316  }
1317 
1318 #endif
1319 
1320  std::stringstream sstream;
1321  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;
1322  instance->print(sstream, 5);
1323 
1324 
1325  if ( cost_fnc == FROBENIUS_NORM ) {
1326  *f0 = 1-trace_DFE_mtx[0]/Umtx_loc.cols;
1327  }
1328  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1329  *f0 = 1 - (trace_DFE_mtx[0] + std::sqrt(prev_cost_fnv_val)*trace_DFE_mtx[1]*correction1_scale)/Umtx_loc.cols;
1330  }
1331  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1332  *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;
1333  }
1334  else {
1335  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1336  throw err;
1337  }
1338 
1339  //double f0_DFE = *f0;
1340 
1341  //Matrix_real grad_components_DFE_mtx(1, parameter_num_loc);
1342  for (int idx=0; idx<parameter_num_loc; idx++) {
1343 
1344  if ( cost_fnc == FROBENIUS_NORM ) {
1345  grad[idx] = -trace_DFE_mtx[3*(idx+1)]/Umtx_loc.cols;
1346  }
1347  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1348  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;
1349  }
1350  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1351  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;
1352  }
1353  else {
1354  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1355  throw err;
1356  }
1357 
1358  //grad_components_DFE_mtx[idx] = gsl_vector_get( grad, idx );
1359 
1360 
1361  }
1362 
1363  delete[] DFEgates;
1364 
1365 //tbb::tick_count t1_DFE = tbb::tick_count::now();/////////////////////////////////
1366 //std::cout << "uploaded data to DFE: " << (int)(gatesNum*gateSetNum*sizeof(DFEgate_kernel_type)) << " bytes" << std::endl;
1367 //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;
1368 
1370 }
1371 else {
1372 
1373 #endif
1374 
1375 #ifdef __DFE__
1376 tbb::tick_count t0_CPU = tbb::tick_count::now();
1377 #endif
1378 
1379  // vector containing gradients of the transformed matrix
1380  Matrix trace_tmp(1,3);
1381  Matrix Umtx_loc = instance->get_Umtx();
1382  static tbb::enumerable_thread_specific<std::vector<Matrix>> combined_result_tls;
1383  std::vector<Matrix>& combined_result = combined_result_tls.local();
1384  instance->apply_to_combined( parameters, Umtx_loc, parallel, combined_result );
1385  Matrix& matrix_new = combined_result[0];
1386 
1387  *f0 = instance->calculate_cost_function(matrix_new, &trace_tmp);
1388 
1389  Matrix Upartial;
1390  if (cost_fnc == SUM_OF_SQUARES) {
1391  Upartial = get_deriv_sum_of_squares(matrix_new);
1392  } else if (cost_fnc == OSR_ENTANGLEMENT) {
1393  Upartial = get_deriv_osr_entanglement(matrix_new, use_cuts, osr_rank, use_softmax);
1394  }
1395 
1396 
1397  auto calculate_gradient_component = [&](int idx) {
1398  double grad_comp;
1399  Matrix& deriv_mtx = combined_result[static_cast<size_t>(idx) + 1];
1400  switch (cost_fnc) {
1401  case FROBENIUS_NORM:
1402  grad_comp = (get_cost_function(deriv_mtx, trace_offset_loc) - 1.0);
1403  break;
1405  Matrix_real deriv_tmp = get_cost_function_with_correction( deriv_mtx, qbit_num, trace_offset_loc );
1406  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*deriv_tmp[1]*correction1_scale - 1.0);
1407  break;
1408  }
1410  Matrix_real deriv_tmp = get_cost_function_with_correction2( deriv_mtx, qbit_num, trace_offset_loc );
1411  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*(deriv_tmp[1]*correction1_scale + deriv_tmp[2]*correction2_scale) - 1.0);
1412  break;
1413  }
1414  case HILBERT_SCHMIDT_TEST: {
1415  double d = 1.0/deriv_mtx.cols;
1416  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1417  grad_comp = -2.0*d*d*trace_tmp[0].real*deriv_tmp.real-2.0*d*d*trace_tmp[0].imag*deriv_tmp.imag;
1418  break;
1419  }
1421  Matrix&& deriv_tmp = get_trace_with_correction( deriv_mtx, qbit_num);
1422  double d = 1.0/deriv_mtx.cols;
1423  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));
1424  break;
1425  }
1427  Matrix&& deriv_tmp = get_trace_with_correction2( deriv_mtx, qbit_num);
1428  double d = 1.0/deriv_mtx.cols;
1429  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)));
1430  break;
1431  }
1432  case SUM_OF_SQUARES:
1433  case OSR_ENTANGLEMENT:
1434  grad_comp = real_trace_conj_dot(Upartial, deriv_mtx);
1435  /*{
1436  Matrix matrix_new = instance->get_Umtx().copy();
1437  auto paramcopy = parameters.copy();
1438  paramcopy[idx] += 1e-10;
1439  instance->apply_to( paramcopy, matrix_new );
1440  double f1 = instance->get_cost_function_variant() == SUM_OF_SQUARES ? get_cost_function_sum_of_squares(matrix_new) : get_osr_entanglement_test(matrix_new, use_cuts, osr_rank, use_softmax);
1441  double check = (f1 - *f0) / 1e-10;
1442  //printf("%d: %g %g\n", idx, grad_comp, check);
1443  grad_comp = check;
1444  }*/
1445  break;
1446  case INFIDELITY: {
1447  double d = deriv_mtx.cols;
1448  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1449  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;
1450  break;
1451  }
1452  default: {
1453  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1454  throw err;
1455  } }
1456 
1457  grad[idx] = grad_comp;
1458  };
1459 
1460  if ( parallel == 0 ) {
1461  for (int idx=0; idx<parameter_num_loc; ++idx) {
1462  calculate_gradient_component(idx);
1463  }
1464  }
1465  else {
1466  int work_batch = 10;
1467  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,work_batch), [&](tbb::blocked_range<int> r) {
1468  for (int idx=r.begin(); idx<r.end(); ++idx) {
1469  calculate_gradient_component(idx);
1470  }
1471  });
1472  }
1473 
1474  instance->increment_num_iters(parameter_num_loc + 1);
1475 
1476  std::stringstream sstream;
1477  sstream << *f0 << std::endl;
1478  instance->print(sstream, 5);
1479 
1480 #ifdef __DFE__
1481 }
1482 #endif
1483 
1484 
1485 }
1486 
1487 
1488 
1496 void Optimization_Interface::optimization_problem_combined( Matrix_real parameters, void* void_instance, double* f0, Matrix_real& grad ){
1497  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1498  instance->optimization_problem_combined_non_static(parameters, void_instance, f0, grad );
1499  return;
1500 }
1501 
1502 
1511 
1512  optimization_problem_combined( parameters, this, f0, grad );
1513  return;
1514 }
1515 
1516 
1517 
1525 void Optimization_Interface::optimization_problem_combined_unitary( Matrix_real parameters, void* void_instance, Matrix& Umtx, std::vector<Matrix>& Umtx_deriv ) {
1526  // vector containing gradients of the transformed matrix
1527  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1528 
1529  int parallel = instance->get_parallel_configuration();
1530 
1531  Matrix Umtx_loc = instance->get_Umtx();
1532  static tbb::enumerable_thread_specific<std::vector<Matrix>> combined_result_tls;
1533  std::vector<Matrix>& combined_result = combined_result_tls.local();
1534  instance->apply_to_combined( parameters, Umtx_loc, parallel, combined_result );
1535  combined_result[0].copy_to(Umtx);
1536  Umtx_deriv.resize(combined_result.size() - 1);
1537  for (size_t idx = 1; idx < combined_result.size(); ++idx) {
1538  combined_result[idx].copy_to(Umtx_deriv[idx - 1]);
1539  }
1540 
1541 
1542 
1543 
1544 
1545 
1546 
1547 }
1548 
1549 
1556 void Optimization_Interface::optimization_problem_combined_unitary( Matrix_real parameters, Matrix& Umtx, std::vector<Matrix>& Umtx_deriv ) {
1557 
1558  optimization_problem_combined_unitary( parameters, this, Umtx, Umtx_deriv);
1559  return;
1560 
1561 }
1562 
1563 
1569 
1570  return cost_fnc;
1571 
1572 }
1573 
1574 
1579 void
1581 
1582  cost_fnc = variant;
1583 
1584  std::stringstream sstream;
1585  sstream << "Optimization_Interface::set_cost_function_variant: Cost function variant set to " << cost_fnc << std::endl;
1586  print(sstream, 2);
1587 
1588 
1589 }
1590 
1591 
1592 
1597 void Optimization_Interface::set_max_inner_iterations( int max_inner_iterations_in ) {
1598 
1599  max_inner_iterations = max_inner_iterations_in;
1600 
1601 }
1602 
1603 
1604 
1609 void Optimization_Interface::set_random_shift_count_max( int random_shift_count_max_in ) {
1610 
1611  random_shift_count_max = random_shift_count_max_in;
1612 
1613 }
1614 
1615 
1621 
1622  alg = alg_in;
1623 
1624  switch ( alg ) {
1625  case ADAM:
1626  max_inner_iterations = 100000;
1627  random_shift_count_max = 100;
1629  return;
1630 
1631  case ADAM_BATCHED:
1632  max_inner_iterations = 2500;
1635  return;
1636 
1637  case GRAD_DESCEND:
1638  max_inner_iterations = 10000;
1640  max_outer_iterations = 100000000;
1641  return;
1642 
1643  case COSINE:
1644  max_inner_iterations = 2500;
1647  return;
1648 
1650  max_inner_iterations = 2500;
1653  return;
1654 
1655  case AGENTS:
1656  max_inner_iterations = 2500;
1659  return;
1660 
1661  case AGENTS_COMBINED:
1662  max_inner_iterations = 2500;
1665  return;
1666 
1667  case BFGS:
1668  max_inner_iterations = 10000;
1670  max_outer_iterations = 100000000;
1671  return;
1672 
1673  case BFGS2:
1674  max_inner_iterations = 100000;
1675  random_shift_count_max = 100;
1677  return;
1678 
1679  case BAYES_OPT:
1680  max_inner_iterations = 100;
1681  random_shift_count_max = 100;
1683  return;
1684  case BAYES_AGENTS:
1685  max_inner_iterations = 100;
1686  random_shift_count_max = 100;
1688  return;
1689 
1690  default:
1691  std::string error("Optimization_Interface::set_optimizer: unimplemented optimization algorithm");
1692  throw error;
1693  }
1694 
1695 
1696 
1697 }
1698 
1699 
1700 
1701 
1705 double
1707 
1708  return prev_cost_fnv_val;
1709 
1710 }
1711 
1712 
1713 
1718 double
1720 
1721  return correction1_scale;
1722 
1723 }
1724 
1725 
1726 
1731 double
1733 
1734  return correction2_scale;
1735 
1736 }
1737 
1738 
1739 
1740 
1741 
1742 
1746 int
1748 
1749  return number_of_iters.load(std::memory_order_relaxed);
1750 
1751 }
1752 
1753 
1759 
1760  number_of_iters.fetch_add(delta, std::memory_order_relaxed);
1761 
1762 }
1763 
1764 
1769 void
1771 
1772  release_gates();
1773 
1774  set_qbit_num( gate_structure_in->get_qbit_num() );
1775 
1776  combine( gate_structure_in );
1777 
1778 }
1779 
1780 
1784 int
1786 
1787  return trace_offset;
1788 
1789 }
1790 
1791 
1795 void
1797 
1798 
1799  if ( (trace_offset_in + Umtx.cols) > Umtx.rows ) {
1800  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.");
1801  throw error;
1802 
1803  }
1804 
1805 
1806  trace_offset = trace_offset_in;
1807 
1808 
1809  std::stringstream sstream;
1810  sstream << "Optimization_Interface::set_trace_offset: trace offset set to " << trace_offset << std::endl;
1811  print(sstream, 2);
1812 
1813 }
1814 
1815 
1816 #ifdef __DFE__
1817 
1818 void
1819 Optimization_Interface::upload_Umtx_to_DFE() {
1820  if (Umtx.cols == Umtx.rows) {
1822  }
1823 
1824 }
1825 
1826 
1827 #endif
1828 
1832 int
1834 
1835  return accelerator_num;
1836 
1837 }
1838 
1839 
1840 void Optimization_Interface::set_osr_params( std::vector<std::vector<int>> use_cuts_in, int osr_rank_in, bool use_softmax_in )
1841 {
1842  use_cuts = use_cuts_in;
1843  osr_rank = osr_rank_in;
1844  use_softmax = use_softmax_in;
1845  //std::stringstream sstream;
1846  //sstream << "Optimization_Interface::set_osr_params: OSR entanglement test parameters set. osr_rank: " << osr_rank << ", use_softmax: " << use_softmax << std::endl;
1847  //print(sstream, 2);
1848 }
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 of single-precision complex array storage with automatic and thread safe reference counti...
Header file for a class containing basic methods for the decomposition process.
void set_osr_params(std::vector< std::vector< int >> use_cuts_in, int osr_rank_in, bool use_softmax_in)
int id
unique id indentifying the instance of the class
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
Class to store single-precision real arrays and properties.
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...
int init_groq_sv_lib(const int reserved_device_num, int initialize_id_in)
Call to allocated Groq cards for calculations.
std::map< int, int > identical_blocks
A map of <int n: int num> indicating that how many identical successive blocks should be used in the ...
Matrix_real get_cost_function_with_correction(const 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...
void add_gate(Gate *gate)
Append a general gate to the list of gates.
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
Matrix to_float64() const
Convert to double precision.
Definition: matrix_float.cpp:8
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)
bool use_float
Selects float32 circuit application for parameter/unitary/state data.
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.
Matrix get_deriv_sum_of_squares(Matrix &matrix)
void add_u1(int target_qbit)
Append a U1 gate to the list of gates.
Matrix_real_float optimized_parameters_mtx_float
Float32 optimized parameters used by the hot gate-application path.
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.
std::vector< std::vector< int > > use_cuts
cuts used for OSR entanglement cost function
double optimization_problem(double *parameters)
Evaluate the optimization problem of the optimization.
double get_osr_entanglement_test(Matrix &matrix, std::vector< std::vector< int >> &use_cuts, int rank=-1, bool use_softmax=false)
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...
std::atomic< int > number_of_iters
number of iterations
int trace_offset
The offset in the first columns from which the "trace" is calculated. In this case Tr(A) = sum_(i-off...
double calculate_cost_function(Matrix &matrix_new, Matrix *ret_temp=NULL)
Calculate the current cost function value from an already transformed matrix.
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...
void increment_num_iters(int delta=1)
Atomically increment the tracked number of optimization iterations.
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 init_dfe_lib_and_upload(const int accelerator_num, int qbit_num, int initialize_id_in, Matrix &input)
Initialize the DFE library if needed and upload the input matrix while holding the writer lock...
Definition: common_DFE.cpp:178
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.
Matrix_real get_cost_function_with_correction2(const 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...
int osr_rank
rank used for OSR entanglement cost function
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)
bool use_softmax
logical variable indicating whether to use softmax or average in the OSR entanglement cost function ...
std::string project_name
the name of the project
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:96
QGD_Complex16 get_trace(Matrix &matrix)
Call to calculate the real and imaginary parts of the trace.
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
Matrix get_deriv_osr_entanglement(Matrix &matrix, std::vector< std::vector< int >> &use_cuts, int rank=-1, bool use_softmax=false)
Matrix_float Umtx_float
Float32 copy of the unitary used when config["use_float"] is true.
#define M_PI
Definition: qgd_math.h:42
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.
double real_trace_conj_dot(Matrix &A, Matrix &B)
void randomize_parameters(Matrix_real &input, Matrix_real &output, const double f0)
Call to randomize the parameter.
virtual void apply_to(Matrix_real &parameters_mtx, Matrix &input, int parallel=0) override
Call to apply the gate on the input array/matrix Gates_block*input.
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:41
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
double CPU_time
Time spent on optimization [seconds].
A class containing basic methods for the decomposition process.
double circuit_simulation_time
Time spent on circuit simulation/cost function evaluation [seconds].
decomposed_matrix
the unitary matrix from the result object
Definition: example.py:90
void copy_to(Matrix_real &target) const
Copy the matrix to a reusable double-precision target matrix.
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:136
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
Double-precision complex matrix (float64).
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...
void copy_to(matrix_base< scalar > &target) const
Copy the current matrix storage into a reusable target matrix.
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.
double get_cost_function(const Matrix &matrix, int trace_offset=0)
Call co calculate the cost function during the final optimization process.
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:82
cost_function_type
Type definition of the different 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:44
int get_initialize_id()
Call to get the identification number of the inititalization of the library.
Definition: common_DFE.cpp:216
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
guess_type
Type definition of the types of the initial guess.
Fixed point data related to a gate operation.
Definition: common_DFE.h:62
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:110
double get_cost_function_sum_of_squares(Matrix &matrix)
Optimization_Interface & operator=(const Optimization_Interface &other)
Copy assignment operator.
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:108
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...
volatile double current_minimum
The current minimum of the optimization problem.
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:233
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
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...
Matrix_float get_Umtx_float()
Return the float32 unitary copy used by float execution.
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:94
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:1342
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:914
int random_shift_count_max
the maximal number of parameter randomization tries to escape a local minimum.
virtual std::vector< Matrix > apply_to_combined(Matrix_real &parameters_mtx, Matrix &input, int parallel) override
Combined forward + derivative application with shared precomputed trig cache.
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...
int get_parameter_num() override
Call to get the number of free parameters.
double radius
parameter to contron the radius of parameter randomization around the curren tminimum ...
bool get_use_float() const
True when config["use_float"] requests float32 circuit execution.
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:41
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