Sequential Quantum Gate Decomposer  v1.9.6
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 
40 extern "C" int LAPACKE_dgesv( int matrix_layout, int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ldb);
41 
42 
48 
49  // 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).
50  optimize_layer_num = false;
51 
52  // A string describing the type of the class
54 
55  // The global minimum of the optimization problem
57 
58  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
59  adaptive_eta = true;
60 
61  // parameter to contron the radius of parameter randomization around the curren tminimum
62  radius = 1.0;
63  randomization_rate = 0.3;
64 
65  // The chosen variant of the cost function
67 
68  number_of_iters.store(0, std::memory_order_relaxed);
69 
70 
71 
72  // variables to calculate the cost function with first and second corrections
73  prev_cost_fnv_val = 1.0;
74  correction1_scale = 1/1.7;
75  correction2_scale = 1/2.0;
76 
77 
78  // number of utilized accelerators
79  accelerator_num = 0;
80 
81  // set the trace offset
82  trace_offset = 0;
83 
84 
85 
86  // Time spent on circuit simulation/cost function evaluation
88  // time spent on optimization
89  CPU_time = 0.0;
90 
91 }
92 
93 
99 
102  id = other.id;
103 
106  alg = other.alg;
107  cost_fnc = other.cost_fnc;
111  use_cuts = other.use_cuts;
112  osr_rank = other.osr_rank;
113  use_softmax = other.use_softmax;
114  number_of_iters.store(other.number_of_iters.load(std::memory_order_relaxed), std::memory_order_relaxed);
115  adaptive_eta = other.adaptive_eta;
116  radius = other.radius;
119  trace_offset = other.trace_offset;
121  CPU_time = other.CPU_time;
122 
123 }
124 
133 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) {
134 
135  // 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).
136  optimize_layer_num = optimize_layer_num_in;
137 
138  // A string describing the type of the class
140 
141  // The global minimum of the optimization problem
143 
144  number_of_iters.store(0, std::memory_order_relaxed);
145 
146 
147  // number of iteratrion loops in the optimization
148  iteration_loops[2] = 3;
149 
150  // filling in numbers that were not given in the input
151  for ( std::map<int,int>::iterator it = max_layer_num_def.begin(); it!=max_layer_num_def.end(); it++) {
152  if ( max_layer_num.count( it->first ) == 0 ) {
153  max_layer_num.insert( std::pair<int, int>(it->first, it->second) );
154  }
155  }
156 
157  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
158  adaptive_eta = true;
159 
160  // parameter to contron the radius of parameter randomization around the curren tminimum
161  radius = 1.0;
162  randomization_rate = 0.3;
163 
164  // The chosen variant of the cost function
166 
167 
168  // variables to calculate the cost function with first and second corrections
169  prev_cost_fnv_val = 1.0;
170  correction1_scale = 1/1.7;
171  correction2_scale = 1/2.0;
172 
173 
174  // set the trace offset
175  trace_offset = 0;
176 
177  // unique id indentifying the instance of the class
178  std::uniform_int_distribution<> distrib_int(0, INT_MAX);
179  id = distrib_int(gen);
180 
181 
182 
183  // Time spent on circuit simulation/cost function evaluation
185  // time spent on optimization
186  CPU_time = 0.0;
187 
188 #if defined __DFE__
189  // number of utilized accelerators
190  accelerator_num = accelerator_num_in;
191 #elif defined __GROQ__
192  // number of utilized accelerators
193  accelerator_num = accelerator_num_in;
194 #else
195  accelerator_num = 0;
196 #endif
197 
198 }
199 
203 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) {
204 
205  // 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).
206  optimize_layer_num = optimize_layer_num_in;
207 
208  // A string describing the type of the class
210 
211  // The global minimum of the optimization problem
213 
214  number_of_iters.store(0, std::memory_order_relaxed);
215 
216  // number of iteratrion loops in the optimization
217  iteration_loops[2] = 3;
218 
219  // filling in numbers that were not given in the input
220  for ( std::map<int,int>::iterator it = max_layer_num_def.begin(); it!=max_layer_num_def.end(); it++) {
221  if ( max_layer_num.count( it->first ) == 0 ) {
222  max_layer_num.insert( std::pair<int, int>(it->first, it->second) );
223  }
224  }
225 
226  // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
227  adaptive_eta = true;
228 
229  // parameter to contron the radius of parameter randomization around the curren tminimum
230  radius = 1.0;
231  randomization_rate = 0.3;
232 
233  // The chosen variant of the cost function
235 
236  // variables to calculate the cost function with first and second corrections
237  prev_cost_fnv_val = 1.0;
238  correction1_scale = 1/1.7;
239  correction2_scale = 1/2.0;
240 
241  // set the trace offset
242  trace_offset = 0;
243 
244  // unique id indentifying the instance of the class
245  std::uniform_int_distribution<> distrib_int(0, INT_MAX);
246  id = distrib_int(gen);
247 
248  // Time spent on circuit simulation/cost function evaluation
250  // time spent on optimization
251  CPU_time = 0.0;
252 
253 #if defined __DFE__
254  // number of utilized accelerators
255  accelerator_num = accelerator_num_in;
256 #elif defined __GROQ__
257  // number of utilized accelerators
258  accelerator_num = accelerator_num_in;
259 #else
260  accelerator_num = 0;
261 #endif
262 
263 }
264 
265 
266 
271 
272 
273 #ifdef __DFE__
274  if ( Umtx.cols == Umtx.rows && qbit_num >= 2 && get_accelerator_num() > 0 ) {
275  unload_dfe_lib();//releive_DFE();
276  }
277 #endif
278 
279 
280 
281 }
282 
283 
290 
291  if ( this == &other ) {
292  return *this;
293  }
294 
295  Decomposition_Base::operator=(other);
296 
299  id = other.id;
300 
303  alg = other.alg;
304  cost_fnc = other.cost_fnc;
308  use_cuts = other.use_cuts;
309  osr_rank = other.osr_rank;
310  use_softmax = other.use_softmax;
311  number_of_iters.store(other.number_of_iters.load(std::memory_order_relaxed), std::memory_order_relaxed);
312  adaptive_eta = other.adaptive_eta;
313  radius = other.radius;
316  trace_offset = other.trace_offset;
318  CPU_time = other.CPU_time;
319 
320  return *this;
321 
322 }
323 
324 
330 
332 
333 }
334 
335 
336 
343 
344  FILE* pFile;
345  std::string filename("costfuncs_and_entropy.txt");
346 
347  if (project_name != "") {
348  filename = project_name + "_" + filename;
349  }
350 
351  const char* c_filename = filename.c_str();
352 #ifdef _WIN32
353  errno_t err = fopen_s(&pFile, c_filename, "a");
354  if (err != 0) {
355  pFile = NULL;
356  }
357 #else
358  pFile = fopen(c_filename, "a");
359 #endif
360 
361  if (pFile==NULL) {
362  fputs ("File error",stderr);
363  std::string error("Cannot open file.");
364  throw error;
365  }
366 
367  Matrix input_state(Power_of_2(qbit_num),1);
368 
369  std::uniform_int_distribution<> distrib(0, qbit_num-2);
370 
371  memset(input_state.get_data(), 0, (input_state.size()*2)*sizeof(double) );
372  input_state[0].real = 1.0;
373 
374  matrix_base<int> qbit_sublist(1,2);
375  qbit_sublist[0] = 0;//distrib(gen);
376  qbit_sublist[1] = 1;//qbit_sublist[0]+1;
377 
378  double renyi_entropy = get_second_Renyi_entropy(parameters, input_state, qbit_sublist);
379 
380  fprintf(pFile,"%i\t%f\t%f\n", number_of_iters.load(std::memory_order_relaxed), current_minimum, renyi_entropy);
381  fclose(pFile);
382 
383  return;
384 }
385 
386 
394 
395 
396  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
397 
398  instance->export_current_cost_fnc( current_minimum, parameters );
399 
400 }
401 
402 
406 void
408 
409 
410  // creating block of gates
411  Gates_block* block = new Gates_block( qbit_num );
412 
413  // adding U1 gates for final phase corrections
414  for (int qbit=0; qbit<qbit_num; qbit++) {
415  block->add_u1(qbit);
416  }
417 
418  // adding the opeartion block to the gates
419  add_gate( block );
420 
421 }
422 
423 
424 
425 
431 void
433 
434 
435  // the norm is the square root of the largest einegvalue.*/
436  switch (cost_fnc) {
437  case FROBENIUS_NORM:
438  decomposition_error = get_cost_function(decomposed_matrix);
439  break;
441  Matrix_real&& ret = get_cost_function_with_correction(decomposed_matrix, qbit_num);
442  decomposition_error = ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale;
443  break; }
445  Matrix_real&& ret = get_cost_function_with_correction2(decomposed_matrix, qbit_num);
446  decomposition_error = ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale);
447  break; }
449  decomposition_error = get_hilbert_schmidt_test(decomposed_matrix);
450  break;
452  Matrix&& ret = get_trace_with_correction(decomposed_matrix, qbit_num);
453  double d = 1.0/decomposed_matrix.cols;
454  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));
455  break;
456  }
458  Matrix&& ret = get_trace_with_correction2(decomposed_matrix, qbit_num);
459  double d = 1.0/decomposed_matrix.cols;
460  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)));
461  break;
462  }
463  case SUM_OF_SQUARES:
465  break;
466  case INFIDELITY:
467  decomposition_error = get_infidelity(decomposed_matrix);
468  break;
469  case OSR_ENTANGLEMENT:
471  break;
472  default: {
473  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
474  throw err;
475  } }
476 
477 }
478 
479 
480 
485 
486  //The stringstream input to store the output messages.
487  std::stringstream sstream;
488  sstream << "***************************************************************" << std::endl;
489  sstream << "Final fine tuning of the parameters in the " << qbit_num << "-qubit decomposition" << std::endl;
490  sstream << "***************************************************************" << std::endl;
491  print(sstream, 1);
492 
493 
494 
495 
496  //# setting the global minimum
498 
499  if ( optimized_parameters_mtx.size() == 0 ) {
501  }
502  else {
504  if ( check_optimization_solution() ) return;
505 
507  }
508 }
509 
510 
511 
512 
519 
520 
521  switch ( alg ) {
522  case ADAM:
523  solve_layer_optimization_problem_ADAM( num_of_parameters, solution_guess);
524  break;
525  case ADAM_BATCHED:
526  solve_layer_optimization_problem_ADAM_BATCHED( num_of_parameters, solution_guess);
527  break;
528  case GRAD_DESCEND:
529  solve_layer_optimization_problem_GRAD_DESCEND( num_of_parameters, solution_guess);
530  break;
531  case AGENTS:
532  solve_layer_optimization_problem_AGENTS( num_of_parameters, solution_guess);
533  break;
534  case COSINE:
535  solve_layer_optimization_problem_COSINE( num_of_parameters, solution_guess);
536  break;
538  solve_layer_optimization_problem_GRAD_DESCEND_PARAMETER_SHIFT_RULE( num_of_parameters, solution_guess);
539  break;
540  case AGENTS_COMBINED:
541  solve_layer_optimization_problem_AGENTS_COMBINED( num_of_parameters, solution_guess);
542  break;
543  case BFGS:
544  solve_layer_optimization_problem_BFGS( num_of_parameters, solution_guess);
545  break;
546  case BAYES_OPT:
547  solve_layer_optimization_problem_BAYES_OPT( num_of_parameters, solution_guess);
548  break;
549  case BAYES_AGENTS:
550  solve_layer_optimization_problem_BAYES_AGENTS( num_of_parameters, solution_guess);
551  break;
552  case BFGS2:
553  solve_layer_optimization_problem_BFGS2( num_of_parameters, solution_guess);
554  break;
555  default:
556  std::string error("Optimization_Interface::solve_layer_optimization_problem: unimplemented optimization algorithm");
557  throw error;
558  }
559 
560  if ( use_float ) {
562  for (int idx=0; idx<optimized_parameters_mtx.size(); idx++) {
563  optimized_parameters_mtx_float[idx] = static_cast<float>(optimized_parameters_mtx[idx]);
564  }
565  }
566 
567 }
568 
576 
577  // random generator of real numbers
578  std::uniform_real_distribution<> distrib_prob(0.0, 1.0);
579  std::uniform_real_distribution<> distrib_real(-2*M_PI, 2*M_PI);
580 
581 
582  double radius_loc;
583  if ( config.count("Randomized_Radius") > 0 ) {
584  config["Randomized_Radius"].get_property( radius_loc );
585  }
586  else {
587  radius_loc = radius;
588  }
589 
590  const int num_of_parameters = input.size();
591 
592  int changed_parameters = 0;
593  for ( int jdx=0; jdx<num_of_parameters; jdx++) {
594  if ( distrib_prob(gen) <= randomization_rate ) {
595  output[jdx] = (cost_fnc!=VQE) ? input[jdx] + distrib_real(gen)*std::sqrt(f0)*radius_loc : input[jdx] + distrib_real(gen)*radius_loc;
596  changed_parameters++;
597  }
598  else {
599  output[jdx] = input[jdx];
600  }
601  }
602 
603 #ifdef __MPI__
604  //MPI_Bcast( (void*)output.get_data(), num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
605 #endif
606 
607 
608 }
609 
610 
617 
618  // get the transformed matrix with the gates in the list
619  Matrix_real parameters_mtx(parameters, 1, parameter_num );
620 
621 
622  return optimization_problem( parameters_mtx );
623 
624 
625 }
626 
627 
634 
635  // get the transformed matrix with the gates in the list
636  if ( parameters.size() != parameter_num ) {
637  std::stringstream sstream;
638  sstream << "Optimization_Interface::optimization_problem: Number of free paramaters should be " << parameter_num << ", but got " << parameters.size() << std::endl;
639  print(sstream, 0);
640  std::string err("Optimization_Interface::optimization_problem: Wrong number of parameters.");
641  throw err;
642  }
643 
644  // Float32 circuit application is useful for experimental hot paths, but
645  // optimizer cost values must stay in double precision. The objective and
646  // gradients guide convergence and fidelity; evaluating them through
647  // Matrix_float introduces enough drift to steer the solver to bad minima.
648  const bool use_float_cost_path = false;
649  if ( use_float_cost_path && use_float ) {
650  static tbb::enumerable_thread_specific<Matrix_real_float> parameters_float_tls;
651  static tbb::enumerable_thread_specific<Matrix_float> matrix_new_tls;
652  Matrix_real_float& parameters_float = parameters_float_tls.local();
653  Matrix_float& matrix_new = matrix_new_tls.local();
654  parameters.copy_to(parameters_float);
655  Umtx_float.copy_to(matrix_new);
656  Gates_block::apply_to( parameters_float, matrix_new );
657  return calculate_cost_function(matrix_new, NULL);
658  }
659 
660  static tbb::enumerable_thread_specific<Matrix> matrix_new_tls;
661  Matrix& matrix_new = matrix_new_tls.local();
662  Umtx.copy_to(matrix_new);
663  Gates_block::apply_to( parameters, matrix_new );
664 
665  return calculate_cost_function(matrix_new, NULL);
666 
667 }
668 
669 
677 
678  switch (cost_fnc) {
679  case FROBENIUS_NORM:
680  return get_cost_function(matrix_new, trace_offset);
683  return ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale; }
686  return ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale); }
688  if ( ret_temp != NULL ) {
689  QGD_Complex16 trace_temp = get_trace(matrix_new);
690  (*ret_temp)[0].real = trace_temp.real;
691  (*ret_temp)[0].imag = trace_temp.imag;
692  double d = 1.0/matrix_new.cols;
693  return 1 - d*d*(trace_temp.real*trace_temp.real + trace_temp.imag*trace_temp.imag);
694  }
695  return get_hilbert_schmidt_test(matrix_new);
697  Matrix&& ret = get_trace_with_correction(matrix_new, qbit_num);
698  double d = 1.0/matrix_new.cols;
699  if ( ret_temp != NULL ) {
700  for (int idx=0; idx<3; idx++) {
701  (*ret_temp)[idx].real = ret[idx].real;
702  (*ret_temp)[idx].imag = ret[idx].imag;
703  }
704  }
705  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)); }
707  Matrix&& ret = get_trace_with_correction2(matrix_new, qbit_num);
708  double d = 1.0/matrix_new.cols;
709  if ( ret_temp != NULL ) {
710  for (int idx=0; idx<4; idx++) {
711  (*ret_temp)[idx].real = ret[idx].real;
712  (*ret_temp)[idx].imag = ret[idx].imag;
713  }
714  }
715  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))); }
716  case SUM_OF_SQUARES:
717  return get_cost_function_sum_of_squares(matrix_new);
718  case INFIDELITY:
719  if ( ret_temp != NULL ) {
720  QGD_Complex16 trace_temp = get_trace(matrix_new);
721  (*ret_temp)[0].real = trace_temp.real;
722  (*ret_temp)[0].imag = trace_temp.imag;
723  double d = matrix_new.cols;
724  return 1.0-((trace_temp.real*trace_temp.real+trace_temp.imag*trace_temp.imag)/d+1)/(d+1);
725  }
726  return get_infidelity(matrix_new);
727  case OSR_ENTANGLEMENT:
729  default: {
730  std::string err("Optimization_Interface::optimization_problem: Cost function variant not implmented.");
731  throw err;
732  } }
733 
734 }
735 
737 
738  // Experimental helper only. Optimizer objective/gradient callbacks must
739  // use calculate_cost_function(Matrix&, ...) so the solver is guided by
740  // double precision cost values.
741  switch (cost_fnc) {
742  case FROBENIUS_NORM:
743  return get_cost_function(matrix_new, trace_offset);
746  return ret[0] - std::sqrt(prev_cost_fnv_val)*ret[1]*correction1_scale; }
749  return ret[0] - std::sqrt(prev_cost_fnv_val)*(ret[1]*correction1_scale + ret[2]*correction2_scale); }
750  case HILBERT_SCHMIDT_TEST: {
751  QGD_Complex16 trace_temp = get_trace(matrix_new);
752  if ( ret_temp != NULL ) {
753  (*ret_temp)[0].real = static_cast<float>(trace_temp.real);
754  (*ret_temp)[0].imag = static_cast<float>(trace_temp.imag);
755  }
756  double d = 1.0/matrix_new.cols;
757  return 1 - d*d*(trace_temp.real*trace_temp.real + trace_temp.imag*trace_temp.imag); }
758  case INFIDELITY: {
759  QGD_Complex16 trace_temp = get_trace(matrix_new);
760  if ( ret_temp != NULL ) {
761  (*ret_temp)[0].real = static_cast<float>(trace_temp.real);
762  (*ret_temp)[0].imag = static_cast<float>(trace_temp.imag);
763  }
764  double d = matrix_new.cols;
765  return 1.0-((trace_temp.real*trace_temp.real+trace_temp.imag*trace_temp.imag)/d+1)/(d+1); }
768  case SUM_OF_SQUARES:
769  case OSR_ENTANGLEMENT: {
770  if (cost_fnc == OSR_ENTANGLEMENT) {
772  }
773  Matrix matrix_new64 = matrix_new.to_float64();
774  return calculate_cost_function(matrix_new64, NULL); }
775  default: {
776  std::string err("Optimization_Interface::calculate_cost_function(Matrix_float&): Cost function variant not implmented.");
777  throw err;
778  } }
779 
780 }
781 
782 #ifdef __DFE__
783 
789 Optimization_Interface::optimization_problem_batched_DFE( std::vector<Matrix_real>& parameters_vec) {
790 
791 
792  Matrix_real cost_fnc_mtx(parameters_vec.size(), 1);
793 
794  int gatesNum, gateSetNum, redundantGateSets;
795  DFEgate_kernel_type* DFEgates = convert_to_batched_DFE_gates( parameters_vec, gatesNum, gateSetNum, redundantGateSets );
796 
797  Matrix_real trace_DFE_mtx(gateSetNum, 3);
798 
799 
800 
801  increment_num_iters(static_cast<int>(parameters_vec.size()));
802 
803 
804 
805 #ifdef __MPI__
806  // the number of decomposing layers are divided between the MPI processes
807 
808  int mpi_gateSetNum = gateSetNum / world_size;
809  int mpi_starting_gateSetIdx = gateSetNum/world_size * current_rank;
810 
811  Matrix_real mpi_trace_DFE_mtx(mpi_gateSetNum, 3);
812 
813 
814  increment_num_iters(mpi_gateSetNum);
815 
816 
817  {
818  DFE_Lib_Read_Lock dfe_lock;
819  calcqgdKernelDFE( Umtx.rows, Umtx.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, trace_offset, mpi_trace_DFE_mtx.get_data() );
820  }
821 
822  int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
823  MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
824 
825 #else
826  {
827  DFE_Lib_Read_Lock dfe_lock;
828  calcqgdKernelDFE( Umtx.rows, Umtx.cols, DFEgates, gatesNum, gateSetNum, trace_offset, trace_DFE_mtx.get_data() );
829  }
830 
831 #endif // __MPI__
832 
833 
834  // calculate the cost function
835  if ( cost_fnc == FROBENIUS_NORM ) {
836  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
837  cost_fnc_mtx[idx] = 1-trace_DFE_mtx[idx*3]/Umtx.cols;
838  }
839  }
840  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
841  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
842  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;
843  }
844  }
845  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
846  for ( int idx=0; idx<parameters_vec.size(); idx++ ) {
847  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;
848  }
849  }
850  else {
851  std::string err("Optimization_Interface::optimization_problem_batched: Cost function variant not implmented for DFE.");
852  throw err;
853  }
854 
855 
856 
857 
858 
859  delete[] DFEgates;
860 
861  return cost_fnc_mtx;
862 
863 }
864 #endif
865 
866 
867 
868 #ifdef __GROQ__
869 
870 
877 double
878 Optimization_Interface::optimization_problem_Groq( Matrix_real& parameters, int chosen_device) {
879 
880  throw std::string("Optimization_Interface::optimization_problem_Groq should be implemented in derrived classes.");
881 
882  return 0.0;
883 
884 }
885 
886 
887 
894 Optimization_Interface::optimization_problem_batched_Groq( std::vector<Matrix_real>& parameters_vec) {
895 
896  int task_num = parameters_vec.size();
897 
898  Matrix_real cost_fnc_mtx(task_num, 1);
899 
900  if ( get_initialize_id() != id ) {
902  }
903 
904  if ( accelerator_num == 1 ) {
905 
906  int chosen_device = 0;
907 
908  for( int idx=0; idx<task_num; idx++ ) {
909  cost_fnc_mtx[idx] = optimization_problem_Groq( parameters_vec[idx], chosen_device );
910  }
911 
912  }
913  else if ( accelerator_num == 2 ) {
914 
915  for( int idx=0; idx<task_num; idx=idx+2 ) {
916  tbb::parallel_invoke(
917  [&]() { cost_fnc_mtx[idx] = optimization_problem_Groq( parameters_vec[idx], 0 ); },
918  [&]() { if ( (idx+1) < task_num ) cost_fnc_mtx[idx+1] = optimization_problem_Groq( parameters_vec[idx+1], 1 ); }
919  );
920  }
921 
922  }
923  else {
924  throw std::string("Unsupported number of Groq accelerators.");
925  }
926 
927  return cost_fnc_mtx;
928 
929 
930 }
931 #endif
932 
939 Optimization_Interface::optimization_problem_batched( std::vector<Matrix_real>& parameters_vec) {
940 
941 
942 
943 
944 
945 #if defined __DFE__
946  if ( Umtx.cols == Umtx.rows && get_accelerator_num() > 0 ) {
947  return optimization_problem_batched_DFE( parameters_vec );
948  }
949 #elif defined __GROQ__
950  if ( Umtx.cols == 1 && get_accelerator_num() > 0 ) {
951  return optimization_problem_batched_Groq( parameters_vec );
952  }
953 #endif
954 
955 
956  Matrix_real cost_fnc_mtx(static_cast<int>(parameters_vec.size()), 1);
957  int parallel = get_parallel_configuration();
958 
959 #ifdef __MPI__
960 
961 
962  // the number of decomposing layers are divided between the MPI processes
963 
964  int batch_element_num = parameters_vec.size();
965  int mpi_batch_element_num = batch_element_num / world_size;
966  int mpi_batch_element_remainder = batch_element_num % world_size;
967 
968  if ( mpi_batch_element_remainder > 0 ) {
969  std::string err("Optimization_Interface::optimization_problem_batched: The size of the batch should be divisible with the number of processes.");
970  throw err;
971  }
972 
973  int mpi_starting_batchIdx = mpi_batch_element_num * current_rank;
974 
975 
976  Matrix_real cost_fnc_mtx_loc(mpi_batch_element_num, 1);
977 
978  auto calculate_local_batch_element = [&](int idx) {
979  cost_fnc_mtx_loc[idx] = optimization_problem( parameters_vec[idx + mpi_starting_batchIdx] );
980  };
981 
982  if ( parallel == 0 ) {
983  for (int idx=0; idx<mpi_batch_element_num; ++idx) {
984  calculate_local_batch_element(idx);
985  }
986  }
987  else {
988  int work_batch = 1;
989  tbb::parallel_for( tbb::blocked_range<int>(0, (int)mpi_batch_element_num, work_batch), [&](tbb::blocked_range<int> r) {
990  for (int idx=r.begin(); idx<r.end(); ++idx) {
991  calculate_local_batch_element(idx);
992  }
993  });
994  }
995 
996  //number_of_iters = number_of_iters + mpi_batch_element_num;
997 
998 
999 
1000  int bytes = cost_fnc_mtx_loc.size()*sizeof(double);
1001  MPI_Allgather(cost_fnc_mtx_loc.get_data(), bytes, MPI_BYTE, cost_fnc_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
1002 
1003 
1004 #else
1005 
1006  auto calculate_batch_element = [&](int idx) {
1007  cost_fnc_mtx[idx] = optimization_problem( parameters_vec[idx] );
1008  };
1009 
1010  if ( parallel == 0 ) {
1011  for (int idx=0; idx<(int)parameters_vec.size(); ++idx) {
1012  calculate_batch_element(idx);
1013  }
1014  }
1015  else {
1016  int work_batch = 1;
1017  tbb::parallel_for( tbb::blocked_range<int>(0, (int)parameters_vec.size(), work_batch), [&](tbb::blocked_range<int> r) {
1018  for (int idx=r.begin(); idx<r.end(); ++idx) {
1019  calculate_batch_element(idx);
1020  }
1021  });
1022  }
1023 
1024 
1025 #endif // __MPI__
1026 
1027 
1028  return cost_fnc_mtx;
1029 
1030 }
1031 
1032 
1033 
1034 
1035 
1043 double Optimization_Interface::optimization_problem( Matrix_real parameters, void* void_instance, Matrix ret_temp) {
1044 
1045  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1046  instance->increment_num_iters();
1047 
1048  // Keep the Matrix_float path compiled for experiments, but never use it
1049  // for optimizer objective evaluations. Cost/trace calculations need
1050  // double precision even when float32 circuit application is enabled
1051  // elsewhere.
1052  const bool use_float_cost_path = false;
1053  if ( use_float_cost_path && instance->get_use_float() ) {
1054  static tbb::enumerable_thread_specific<Matrix_real_float> parameters_float_tls;
1055  static tbb::enumerable_thread_specific<Matrix_float> matrix_new_tls;
1056  static tbb::enumerable_thread_specific<Matrix_float> ret_temp_float_tls;
1057  Matrix_real_float& parameters_float = parameters_float_tls.local();
1058  Matrix_float& matrix_new = matrix_new_tls.local();
1059  Matrix_float& ret_temp_float = ret_temp_float_tls.local();
1060  parameters.copy_to(parameters_float);
1061  instance->Umtx_float.copy_to(matrix_new);
1062  instance->Gates_block::apply_to( parameters_float, matrix_new );
1063  if (ret_temp_float.rows != ret_temp.rows || ret_temp_float.cols != ret_temp.cols || ret_temp_float.stride != ret_temp.stride) {
1064  ret_temp_float = Matrix_float(ret_temp.rows, ret_temp.cols, ret_temp.stride);
1065  }
1066  return instance->calculate_cost_function(matrix_new, &ret_temp_float);
1067  }
1068 
1069  static tbb::enumerable_thread_specific<Matrix> matrix_new_tls;
1070  Matrix& matrix_new = matrix_new_tls.local();
1071  instance->Umtx.copy_to(matrix_new);
1072  instance->Gates_block::apply_to( parameters, matrix_new );
1073 
1074  return instance->calculate_cost_function(matrix_new, &ret_temp);
1075 
1076 
1077 }
1078 
1079 
1080 
1088 
1089  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1090  Matrix ret(1,3);
1091  double cost_func = instance->optimization_problem(parameters, void_instance, ret);
1092  return cost_func;
1093 }
1094 
1095 
1096 
1097 
1104 double Optimization_Interface::optimization_problem( Matrix_real parameters, void* void_instance){
1105  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1106  return instance->optimization_problem_non_static(parameters, void_instance);
1107 }
1108 
1109 
1110 
1111 
1112 
1113 
1114 
1121 void Optimization_Interface::optimization_problem_grad( Matrix_real parameters, void* void_instance, Matrix_real& grad ) {
1122 
1123  // The function value at x0
1124  double f0;
1125 
1126  // calculate the approximate gradient
1127  optimization_problem_combined( parameters, void_instance, &f0, grad);
1128 
1129 }
1130 
1131 
1132 
1133 
1134 
1142 void Optimization_Interface::optimization_problem_combined_non_static( Matrix_real parameters, void* void_instance, double* f0, Matrix_real& grad ) {
1143 
1144  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1145 
1146  int parallel = instance->get_parallel_configuration();
1147 
1148  // the number of free parameters
1149  int parameter_num_loc = instance->get_parameter_num();
1150 
1151  // the variant of the cost function
1153 
1154  // value of the cost function from the previous iteration to weigth the correction to the trace
1156  double correction1_scale = instance->get_correction1_scale();
1157  double correction2_scale = instance->get_correction2_scale();
1158 
1159  int qbit_num = instance->get_qbit_num();
1160  int trace_offset_loc = instance->get_trace_offset();
1161 
1162  // Gradient-driven optimization is sensitive to low-precision objective
1163  // values. Leave this float32 combined path in place as experimental code,
1164  // but force production optimizer cost/gradient evaluation through the
1165  // double precision branch below.
1166  const bool use_float_combined_cost_path = false;
1167  if ( use_float_combined_cost_path && instance->get_use_float() ) {
1168  static tbb::enumerable_thread_specific<Matrix_real_float> parameters_float_tls;
1169  Matrix_real_float& parameters_float = parameters_float_tls.local();
1170  parameters.copy_to(parameters_float);
1171  Matrix_float Umtx_loc = instance->get_Umtx_float();
1172  static tbb::enumerable_thread_specific<std::vector<Matrix_float>> combined_result_tls;
1173  std::vector<Matrix_float>& combined_result = combined_result_tls.local();
1174  instance->Gates_block::apply_to_combined( parameters_float, Umtx_loc, parallel, combined_result );
1175  Matrix_float& matrix_new = combined_result[0];
1176 
1177  Matrix_float trace_tmp(1,3);
1178  *f0 = instance->calculate_cost_function(matrix_new, &trace_tmp);
1179 
1180  Matrix Upartial;
1181  Matrix_float Upartial_float;
1182  Matrix matrix_new64;
1183  if (cost_fnc == SUM_OF_SQUARES) {
1184  matrix_new64 = matrix_new.to_float64();
1185  Upartial = get_deriv_sum_of_squares(matrix_new64);
1186  }
1187  else if (cost_fnc == OSR_ENTANGLEMENT) {
1188  Upartial_float = get_deriv_osr_entanglement(matrix_new, use_cuts, osr_rank, use_softmax);
1189  }
1190 
1191  auto calculate_gradient_component = [&](int idx) {
1192  double grad_comp;
1193  Matrix_float& deriv_mtx = combined_result[static_cast<size_t>(idx) + 1];
1194  switch (cost_fnc) {
1195  case FROBENIUS_NORM:
1196  grad_comp = (get_cost_function(deriv_mtx, trace_offset_loc) - 1.0);
1197  break;
1199  Matrix_real_float deriv_tmp = get_cost_function_with_correction(deriv_mtx, qbit_num, trace_offset_loc);
1200  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*deriv_tmp[1]*correction1_scale - 1.0);
1201  break;
1202  }
1204  Matrix_real_float deriv_tmp = get_cost_function_with_correction2(deriv_mtx, qbit_num, trace_offset_loc);
1205  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*(deriv_tmp[1]*correction1_scale + deriv_tmp[2]*correction2_scale) - 1.0);
1206  break;
1207  }
1208  case HILBERT_SCHMIDT_TEST: {
1209  double d = 1.0/deriv_mtx.cols;
1210  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1211  grad_comp = -2.0*d*d*trace_tmp[0].real*deriv_tmp.real-2.0*d*d*trace_tmp[0].imag*deriv_tmp.imag;
1212  break;
1213  }
1214  case INFIDELITY: {
1215  double d = deriv_mtx.cols;
1216  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1217  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;
1218  break;
1219  }
1220  case SUM_OF_SQUARES: {
1221  Matrix deriv64 = deriv_mtx.to_float64();
1222  grad_comp = real_trace_conj_dot(Upartial, deriv64);
1223  break;
1224  }
1225  case OSR_ENTANGLEMENT:
1226  grad_comp = real_trace_conj_dot(Upartial_float, deriv_mtx);
1227  break;
1230  Matrix deriv64 = deriv_mtx.to_float64();
1231  Matrix matrix_new_for_trace = matrix_new.to_float64();
1232  Matrix trace_tmp64(1,3);
1233  instance->calculate_cost_function(matrix_new_for_trace, &trace_tmp64);
1234  if (cost_fnc == HILBERT_SCHMIDT_TEST_CORRECTION1) {
1235  Matrix&& deriv_tmp = get_trace_with_correction(deriv64, qbit_num);
1236  double d = 1.0/deriv64.cols;
1237  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));
1238  }
1239  else {
1240  Matrix&& deriv_tmp = get_trace_with_correction2(deriv64, qbit_num);
1241  double d = 1.0/deriv64.cols;
1242  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)));
1243  }
1244  break;
1245  }
1246  default: {
1247  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1248  throw err;
1249  } }
1250  grad[idx] = grad_comp;
1251  };
1252 
1253  if ( parallel == 0 ) {
1254  for (int idx=0; idx<parameter_num_loc; ++idx) {
1255  calculate_gradient_component(idx);
1256  }
1257  }
1258  else {
1259  int work_batch = 10;
1260  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,work_batch), [&](tbb::blocked_range<int> r) {
1261  for (int idx=r.begin(); idx<r.end(); ++idx) {
1262  calculate_gradient_component(idx);
1263  }
1264  });
1265  }
1266 
1267  instance->increment_num_iters(parameter_num_loc + 1);
1268  std::stringstream sstream;
1269  sstream << *f0 << std::endl;
1270  instance->print(sstream, 5);
1271  return;
1272  }
1273 
1274 #ifdef __DFE__
1275 
1277 //std::cout << "number of qubits: " << instance->qbit_num << std::endl;
1278 //tbb::tick_count t0_DFE = tbb::tick_count::now();/////////////////////////////////
1279 if ( Umtx.cols == Umtx.rows && instance->qbit_num >= 5 && instance->get_accelerator_num() > 0 ) {
1280 
1281  int gatesNum, redundantGateSets, gateSetNum;
1282  DFEgate_kernel_type* DFEgates = instance->convert_to_DFE_gates_with_derivates( parameters, gatesNum, gateSetNum, redundantGateSets );
1283 
1284  Matrix&& Umtx_loc = instance->get_Umtx();
1285  Matrix_real trace_DFE_mtx(gateSetNum, 3);
1286 
1287 
1288 #ifdef __MPI__
1289  // the number of decomposing layers are divided between the MPI processes
1290 
1291  int mpi_gateSetNum = gateSetNum / instance->world_size;
1292  int mpi_starting_gateSetIdx = gateSetNum/instance->world_size * instance->current_rank;
1293 
1294  Matrix_real mpi_trace_DFE_mtx(mpi_gateSetNum, 3);
1295 
1296  instance->increment_num_iters(mpi_gateSetNum);
1297 
1298  {
1299  DFE_Lib_Read_Lock dfe_lock;
1300  calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, trace_offset_loc, mpi_trace_DFE_mtx.get_data() );
1301  }
1302 
1303  int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
1304  MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
1305 
1306 #else
1307 
1308  instance->increment_num_iters(gateSetNum);
1309 
1310  {
1311  DFE_Lib_Read_Lock dfe_lock;
1312  calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates, gatesNum, gateSetNum, trace_offset_loc, trace_DFE_mtx.get_data() );
1313  }
1314 
1315 #endif
1316 
1317  std::stringstream sstream;
1318  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;
1319  instance->print(sstream, 5);
1320 
1321 
1322  if ( cost_fnc == FROBENIUS_NORM ) {
1323  *f0 = 1-trace_DFE_mtx[0]/Umtx_loc.cols;
1324  }
1325  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1326  *f0 = 1 - (trace_DFE_mtx[0] + std::sqrt(prev_cost_fnv_val)*trace_DFE_mtx[1]*correction1_scale)/Umtx_loc.cols;
1327  }
1328  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1329  *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;
1330  }
1331  else {
1332  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1333  throw err;
1334  }
1335 
1336  //double f0_DFE = *f0;
1337 
1338  //Matrix_real grad_components_DFE_mtx(1, parameter_num_loc);
1339  for (int idx=0; idx<parameter_num_loc; idx++) {
1340 
1341  if ( cost_fnc == FROBENIUS_NORM ) {
1342  grad[idx] = -trace_DFE_mtx[3*(idx+1)]/Umtx_loc.cols;
1343  }
1344  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION1 ) {
1345  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;
1346  }
1347  else if ( cost_fnc == FROBENIUS_NORM_CORRECTION2 ) {
1348  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;
1349  }
1350  else {
1351  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1352  throw err;
1353  }
1354 
1355  //grad_components_DFE_mtx[idx] = gsl_vector_get( grad, idx );
1356 
1357 
1358  }
1359 
1360  delete[] DFEgates;
1361 
1362 //tbb::tick_count t1_DFE = tbb::tick_count::now();/////////////////////////////////
1363 //std::cout << "uploaded data to DFE: " << (int)(gatesNum*gateSetNum*sizeof(DFEgate_kernel_type)) << " bytes" << std::endl;
1364 //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;
1365 
1367 }
1368 else {
1369 
1370 #endif
1371 
1372 #ifdef __DFE__
1373 tbb::tick_count t0_CPU = tbb::tick_count::now();
1374 #endif
1375 
1376  // vector containing gradients of the transformed matrix
1377  Matrix trace_tmp(1,3);
1378  Matrix Umtx_loc = instance->get_Umtx();
1379  static tbb::enumerable_thread_specific<std::vector<Matrix>> combined_result_tls;
1380  std::vector<Matrix>& combined_result = combined_result_tls.local();
1381  instance->apply_to_combined( parameters, Umtx_loc, parallel, combined_result );
1382  Matrix& matrix_new = combined_result[0];
1383 
1384  *f0 = instance->calculate_cost_function(matrix_new, &trace_tmp);
1385 
1386  Matrix Upartial;
1387  if (cost_fnc == SUM_OF_SQUARES) {
1388  Upartial = get_deriv_sum_of_squares(matrix_new);
1389  } else if (cost_fnc == OSR_ENTANGLEMENT) {
1390  Upartial = get_deriv_osr_entanglement(matrix_new, use_cuts, osr_rank, use_softmax);
1391  }
1392 
1393 
1394  auto calculate_gradient_component = [&](int idx) {
1395  double grad_comp;
1396  Matrix& deriv_mtx = combined_result[static_cast<size_t>(idx) + 1];
1397  switch (cost_fnc) {
1398  case FROBENIUS_NORM:
1399  grad_comp = (get_cost_function(deriv_mtx, trace_offset_loc) - 1.0);
1400  break;
1402  Matrix_real deriv_tmp = get_cost_function_with_correction( deriv_mtx, qbit_num, trace_offset_loc );
1403  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*deriv_tmp[1]*correction1_scale - 1.0);
1404  break;
1405  }
1407  Matrix_real deriv_tmp = get_cost_function_with_correction2( deriv_mtx, qbit_num, trace_offset_loc );
1408  grad_comp = (deriv_tmp[0] - std::sqrt(prev_cost_fnv_val)*(deriv_tmp[1]*correction1_scale + deriv_tmp[2]*correction2_scale) - 1.0);
1409  break;
1410  }
1411  case HILBERT_SCHMIDT_TEST: {
1412  double d = 1.0/deriv_mtx.cols;
1413  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1414  grad_comp = -2.0*d*d*trace_tmp[0].real*deriv_tmp.real-2.0*d*d*trace_tmp[0].imag*deriv_tmp.imag;
1415  break;
1416  }
1418  Matrix&& deriv_tmp = get_trace_with_correction( deriv_mtx, qbit_num);
1419  double d = 1.0/deriv_mtx.cols;
1420  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));
1421  break;
1422  }
1424  Matrix&& deriv_tmp = get_trace_with_correction2( deriv_mtx, qbit_num);
1425  double d = 1.0/deriv_mtx.cols;
1426  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)));
1427  break;
1428  }
1429  case SUM_OF_SQUARES:
1430  case OSR_ENTANGLEMENT:
1431  grad_comp = real_trace_conj_dot(Upartial, deriv_mtx);
1432  /*{
1433  Matrix matrix_new = instance->get_Umtx().copy();
1434  auto paramcopy = parameters.copy();
1435  paramcopy[idx] += 1e-10;
1436  instance->apply_to( paramcopy, matrix_new );
1437  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);
1438  double check = (f1 - *f0) / 1e-10;
1439  //printf("%d: %g %g\n", idx, grad_comp, check);
1440  grad_comp = check;
1441  }*/
1442  break;
1443  case INFIDELITY: {
1444  double d = deriv_mtx.cols;
1445  QGD_Complex16 deriv_tmp = get_trace(deriv_mtx);
1446  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;
1447  break;
1448  }
1449  default: {
1450  std::string err("Optimization_Interface::optimization_problem_combined: Cost function variant not implmented.");
1451  throw err;
1452  } }
1453 
1454  grad[idx] = grad_comp;
1455  };
1456 
1457  if ( parallel == 0 ) {
1458  for (int idx=0; idx<parameter_num_loc; ++idx) {
1459  calculate_gradient_component(idx);
1460  }
1461  }
1462  else {
1463  int work_batch = 10;
1464  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,work_batch), [&](tbb::blocked_range<int> r) {
1465  for (int idx=r.begin(); idx<r.end(); ++idx) {
1466  calculate_gradient_component(idx);
1467  }
1468  });
1469  }
1470 
1471  instance->increment_num_iters(parameter_num_loc + 1);
1472 
1473  std::stringstream sstream;
1474  sstream << *f0 << std::endl;
1475  instance->print(sstream, 5);
1476 
1477 #ifdef __DFE__
1478 }
1479 #endif
1480 
1481 
1482 }
1483 
1484 
1485 
1493 void Optimization_Interface::optimization_problem_combined( Matrix_real parameters, void* void_instance, double* f0, Matrix_real& grad ){
1494  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1495  instance->optimization_problem_combined_non_static(parameters, void_instance, f0, grad );
1496  return;
1497 }
1498 
1499 
1508 
1509  optimization_problem_combined( parameters, this, f0, grad );
1510  return;
1511 }
1512 
1513 
1514 
1522 void Optimization_Interface::optimization_problem_combined_unitary( Matrix_real parameters, void* void_instance, Matrix& Umtx, std::vector<Matrix>& Umtx_deriv ) {
1523  // vector containing gradients of the transformed matrix
1524  Optimization_Interface* instance = reinterpret_cast<Optimization_Interface*>(void_instance);
1525 
1526  int parallel = instance->get_parallel_configuration();
1527 
1528  Matrix Umtx_loc = instance->get_Umtx();
1529  static tbb::enumerable_thread_specific<std::vector<Matrix>> combined_result_tls;
1530  std::vector<Matrix>& combined_result = combined_result_tls.local();
1531  instance->apply_to_combined( parameters, Umtx_loc, parallel, combined_result );
1532  combined_result[0].copy_to(Umtx);
1533  Umtx_deriv.resize(combined_result.size() - 1);
1534  for (size_t idx = 1; idx < combined_result.size(); ++idx) {
1535  combined_result[idx].copy_to(Umtx_deriv[idx - 1]);
1536  }
1537 
1538 
1539 
1540 
1541 
1542 
1543 
1544 }
1545 
1546 
1553 void Optimization_Interface::optimization_problem_combined_unitary( Matrix_real parameters, Matrix& Umtx, std::vector<Matrix>& Umtx_deriv ) {
1554 
1555  optimization_problem_combined_unitary( parameters, this, Umtx, Umtx_deriv);
1556  return;
1557 
1558 }
1559 
1560 
1566 
1567  return cost_fnc;
1568 
1569 }
1570 
1571 
1576 void
1578 
1579  cost_fnc = variant;
1580 
1581  std::stringstream sstream;
1582  sstream << "Optimization_Interface::set_cost_function_variant: Cost function variant set to " << cost_fnc << std::endl;
1583  print(sstream, 2);
1584 
1585 
1586 }
1587 
1588 
1589 
1594 void Optimization_Interface::set_max_inner_iterations( int max_inner_iterations_in ) {
1595 
1596  max_inner_iterations = max_inner_iterations_in;
1597 
1598 }
1599 
1600 
1601 
1606 void Optimization_Interface::set_random_shift_count_max( int random_shift_count_max_in ) {
1607 
1608  random_shift_count_max = random_shift_count_max_in;
1609 
1610 }
1611 
1612 
1618 
1619  alg = alg_in;
1620 
1621  switch ( alg ) {
1622  case ADAM:
1623  max_inner_iterations = 100000;
1624  random_shift_count_max = 100;
1626  return;
1627 
1628  case ADAM_BATCHED:
1629  max_inner_iterations = 2500;
1632  return;
1633 
1634  case GRAD_DESCEND:
1635  max_inner_iterations = 10000;
1637  max_outer_iterations = 100000000;
1638  return;
1639 
1640  case COSINE:
1641  max_inner_iterations = 2500;
1644  return;
1645 
1647  max_inner_iterations = 2500;
1650  return;
1651 
1652  case AGENTS:
1653  max_inner_iterations = 2500;
1656  return;
1657 
1658  case AGENTS_COMBINED:
1659  max_inner_iterations = 2500;
1662  return;
1663 
1664  case BFGS:
1665  max_inner_iterations = 10000;
1667  max_outer_iterations = 100000000;
1668  return;
1669 
1670  case BFGS2:
1671  max_inner_iterations = 100000;
1672  random_shift_count_max = 100;
1674  return;
1675 
1676  case BAYES_OPT:
1677  max_inner_iterations = 100;
1678  random_shift_count_max = 100;
1680  return;
1681  case BAYES_AGENTS:
1682  max_inner_iterations = 100;
1683  random_shift_count_max = 100;
1685  return;
1686 
1687  default:
1688  std::string error("Optimization_Interface::set_optimizer: unimplemented optimization algorithm");
1689  throw error;
1690  }
1691 
1692 
1693 
1694 }
1695 
1696 
1697 
1698 
1702 double
1704 
1705  return prev_cost_fnv_val;
1706 
1707 }
1708 
1709 
1710 
1715 double
1717 
1718  return correction1_scale;
1719 
1720 }
1721 
1722 
1723 
1728 double
1730 
1731  return correction2_scale;
1732 
1733 }
1734 
1735 
1736 
1737 
1738 
1739 
1743 int
1745 
1746  return number_of_iters.load(std::memory_order_relaxed);
1747 
1748 }
1749 
1750 
1756 
1757  number_of_iters.fetch_add(delta, std::memory_order_relaxed);
1758 
1759 }
1760 
1761 
1766 void
1768 
1769  release_gates();
1770 
1771  set_qbit_num( gate_structure_in->get_qbit_num() );
1772 
1773  combine( gate_structure_in );
1774 
1775 }
1776 
1777 
1781 int
1783 
1784  return trace_offset;
1785 
1786 }
1787 
1788 
1792 void
1794 
1795 
1796  if ( (trace_offset_in + Umtx.cols) > Umtx.rows ) {
1797  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.");
1798  throw error;
1799 
1800  }
1801 
1802 
1803  trace_offset = trace_offset_in;
1804 
1805 
1806  std::stringstream sstream;
1807  sstream << "Optimization_Interface::set_trace_offset: trace offset set to " << trace_offset << std::endl;
1808  print(sstream, 2);
1809 
1810 }
1811 
1812 
1813 #ifdef __DFE__
1814 
1815 void
1816 Optimization_Interface::upload_Umtx_to_DFE() {
1817  if (Umtx.cols == Umtx.rows) {
1819  }
1820 
1821 }
1822 
1823 
1824 #endif
1825 
1829 int
1831 
1832  return accelerator_num;
1833 
1834 }
1835 
1836 
1837 void Optimization_Interface::set_osr_params( std::vector<std::vector<int>> use_cuts_in, int osr_rank_in, bool use_softmax_in )
1838 {
1839  use_cuts = use_cuts_in;
1840  osr_rank = osr_rank_in;
1841  use_softmax = use_softmax_in;
1842  //std::stringstream sstream;
1843  //sstream << "Optimization_Interface::set_osr_params: OSR entanglement test parameters set. osr_rank: " << osr_rank << ", use_softmax: " << use_softmax << std::endl;
1844  //print(sstream, 2);
1845 }
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
A class containing basic methods for the decomposition process.
double circuit_simulation_time
Time spent on circuit simulation/cost function evaluation.
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