Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
BFGS2.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 @author: Peter Rakyta, Ph.D.
18 */
24 #include "Optimization_Interface.h"
26 #include "BFGS_Powell.h"
27 
28 #include <fstream>
29 
30 
31 #ifdef __DFE__
32 #include "common_DFE.h"
33 #endif
34 
35 
42 
43 
44 #ifdef __DFE__
45  if ( qbit_num >= 2 && get_accelerator_num() > 0 ) {
46  upload_Umtx_to_DFE();
47  }
48 #endif
49 
50 
51  if (gates.size() == 0 ) {
52  return;
53  }
54 
55 
56  if (solution_guess.size() == 0 ) {
57  solution_guess = Matrix_real(num_of_parameters,1);
58  }
59 
60 
61  if (optimized_parameters_mtx.size() == 0) {
62  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
63  memcpy(optimized_parameters_mtx.get_data(), solution_guess.get_data(), num_of_parameters*sizeof(double) );
64  }
65 
66  // maximal number of iteration loops
67  int iteration_loops_max;
68  try {
69  iteration_loops_max = std::max(iteration_loops[qbit_num], 1);
70  }
71  catch (...) {
72  iteration_loops_max = 1;
73  }
74 
75 
76  double current_minimum_hold = current_minimum;
77 
78 
79 
80 
81 
82 tbb::tick_count bfgs_start = tbb::tick_count::now();
83 CPU_time = 0.0;
84 
85 
86  // random generator of real numbers
87  std::uniform_real_distribution<> distrib_real(0.0, 1.0);
88 
89 
90  long long max_inner_iterations_loc;
91  if ( config.count("max_inner_iterations_bfgs2") > 0 ) {
92  config["max_inner_iterations_bfgs2"].get_property( max_inner_iterations_loc );
93  }
94  else if ( config.count("max_inner_iterations") > 0 ) {
95  config["max_inner_iterations"].get_property( max_inner_iterations_loc );
96  }
97  else {
98  max_inner_iterations_loc =max_inner_iterations;
99  }
100 
101 
102  long long export_circuit_2_binary_loc;
103  if ( config.count("export_circuit_2_binary_bfgs2") > 0 ) {
104  config["export_circuit_2_binary_bfgs2"].get_property( export_circuit_2_binary_loc );
105  }
106  else if ( config.count("export_circuit_2_binary") > 0 ) {
107  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
108  }
109  else {
110  export_circuit_2_binary_loc = 0;
111  }
112 
113 
114 
115  double optimization_tolerance_loc;
116  if ( config.count("optimization_tolerance_adam") > 0 ) {
117  config["optimization_tolerance_adam"].get_property( optimization_tolerance_loc );
118  }
119  else if ( config.count("optimization_tolerance") > 0 ) {
120  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
121  }
122  else {
123  optimization_tolerance_loc = optimization_tolerance;
124  }
125 
126 
127  // The number if iterations after which the current results are displed/exported
128  int output_periodicity;
129  if ( config.count("output_periodicity_cosine") > 0 ) {
130  long long value = 1;
131  config["output_periodicity_cosine"].get_property( value );
132  output_periodicity = (int) value;
133  }
134  if ( config.count("output_periodicity") > 0 ) {
135  long long value = 1;
136  config["output_periodicity"].get_property( value );
137  output_periodicity = (int) value;
138  }
139  else {
140  output_periodicity = 0;
141  }
142 
143 
144 
145  std::stringstream sstream;
146  sstream << "max_inner_iterations: " << max_inner_iterations_loc << std::endl;
147  print(sstream, 2);
148 
149  long long use_basin_hopping = 0;
150  long long use_de = 0;
151  long long use_dual_annealing = 0;
152  if ( config.count("use_basin_hopping") > 0 ) {
153  config["use_basin_hopping"].get_property( use_basin_hopping );
154  } else if ( config.count("use_differential_evolution") > 0 ) {
155  config["use_differential_evolution"].get_property( use_de );
156  } else if ( config.count("use_dual_annealing") > 0 ) {
157  config["use_dual_annealing"].get_property( use_dual_annealing );
158  }
159  if (!use_basin_hopping && !use_de && !use_dual_annealing) {
160  use_dual_annealing = 1; //use_basin_hopping = 1;
161  }
162 
163  if (use_basin_hopping) {
164  // --- Basin-hopping parameters (SciPy-like defaults) ---
165  double bh_T = 1.0; // "temperature" for Metropolis acceptance
166  double bh_stepsize = 0.5;
167  long long bh_interval = 50; // how often to adapt stepsize
168  double bh_target_accept = 0.5;
169  double bh_stepwise_factor = 0.9;
170  // Allow overrides via config (all optional)
171  if (config.count("bh_T") > 0) config["bh_T"].get_property(bh_T);
172  if (config.count("bh_stepsize") > 0) config["bh_stepsize"].get_property(bh_stepsize);
173  if (config.count("bh_interval") > 0) { long long v; config["bh_interval"].get_property(v); bh_interval = std::max<long long>(1, v); }
174  if (config.count("bh_target_accept_rate") > 0) config["bh_target_accept_rate"].get_property(bh_target_accept);
175  if (config.count("bh_stepwise_factor") > 0) config["bh_stepwise_factor"].get_property(bh_stepwise_factor);
176 
177  // Clamp a couple of parameters to SciPy’s expected ranges
178  bh_target_accept = std::min(0.999, std::max(0.001, bh_target_accept));
179  if (!(bh_stepwise_factor > 0.0 && bh_stepwise_factor < 1.0)) bh_stepwise_factor = 0.9;
180 
181  // ---------------- Basin-hopping driver ----------------
182  long long accept_count_window = 0;
183  long long window_len = 0;
184  long long no_improve_count = 0;
185  double stepsize_now = bh_stepsize; // adaptive stepsize (SciPy-style)
186 
187  BFGS_Powell cBFGS_Powell(optimization_problem_combined, this);
188  double f_trial = cBFGS_Powell.Start_Optimization(solution_guess, max_inner_iterations);
189  if (f_trial < current_minimum) {
190  current_minimum = f_trial;
191  memcpy(optimized_parameters_mtx.get_data(), solution_guess.get_data(), num_of_parameters*sizeof(double));
192  }
193  Matrix_real x_current = solution_guess.copy(); // current basin representative
194 
195 
196  for (long long iter_idx=0; iter_idx<iteration_loops_max; iter_idx++) {
197 
198  for (int j = 0; j < num_of_parameters; ++j) {
199  double delta = (distrib_real(gen) * 2.0 - 1.0) * stepsize_now * M_PI;
200  solution_guess[j] = fmod(solution_guess[j] + delta, 2.0 * M_PI);
201  }
202 
203  f_trial = cBFGS_Powell.Start_Optimization(solution_guess, max_inner_iterations);
204 
205  // --- Metropolis acceptance (always accept downhill; uphill with prob exp(-(f_new - f_old)/T))
206  bool accept = false;
207  if (f_trial <= current_minimum_hold) {
208  accept = true;
209  } else {
210  double dE = f_trial - current_minimum_hold;
211  double prob = std::exp(-dE / std::max(1e-300, bh_T));
212  accept = (distrib_real(gen) < prob);
213  }
214 
215  if (accept) {
216  // move to new basin
217  memcpy(x_current.get_data(), solution_guess.get_data(), num_of_parameters*sizeof(double));
218  current_minimum_hold = f_trial;
219  ++accept_count_window;
220  } else {
221  memcpy(solution_guess.get_data(), x_current.get_data(), num_of_parameters*sizeof(double));
222  }
223 
224  // --- Track global best
225  //bool improved_global = false;
226  if (f_trial < current_minimum) {
227  current_minimum = f_trial; // keep public minimum in sync
228  memcpy(optimized_parameters_mtx.get_data(), solution_guess.get_data(), num_of_parameters*sizeof(double));
229  //improved_global = true;
230  no_improve_count = 0;
231  } else {
232  ++no_improve_count;
233  }
234 
235  if ( iter_idx % 5000 == 0 ) {
236  std::stringstream sstream;
237  sstream << "BFGS2: processed iterations " << (double)iter_idx/max_inner_iterations_loc*100 << "%, current minimum:" << current_minimum << std::endl;
238  print(sstream, 2);
239 
240  if ( export_circuit_2_binary_loc>0) {
241  std::string filename("initial_circuit_iteration.binary");
242  if (project_name != "") {
243  filename=project_name+ "_" +filename;
244  }
246  }
247  }
248 
249  if ( output_periodicity>0 && iter_idx % output_periodicity == 0 ) {
251  }
252 
253  #ifdef __MPI__
254  MPI_Bcast( (void*)solution_guess.get_data(), num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
255  #endif
256 
257 
258  if (current_minimum < optimization_tolerance_loc ) {
259  break;
260  }
261  if (no_improve_count >= random_shift_count_max) {
262  break; // SciPy's niter_success criterion
263  }
264 
265  // --- Adaptive stepsize every 'interval' iterations (SciPy behavior)
266  ++window_len;
267  if (bh_interval > 0 && (window_len % bh_interval) == 0) {
268  double accept_rate = (double)accept_count_window / (double)bh_interval;
269  // If acceptance is high, enlarge steps; else shrink steps.
270  if (accept_rate > bh_target_accept) {
271  stepsize_now /= bh_stepwise_factor; // increase (since factor<1)
272  } else {
273  stepsize_now *= bh_stepwise_factor; // decrease
274  }
275  // reset window counters
276  accept_count_window = 0;
277  window_len = 0;
278  }
279 
280 
281  }
282  } else if (use_de) {
283  enum strategy_enum { STRAT_BEST1BIN, STRAT_RAND1BIN };
284  enum init_enum { INIT_RANDOM, INIT_LHS };
285 
286  // ---------------- DE hyperparameters (SciPy-ish) ----------------
287  long long de_strategy = STRAT_BEST1BIN; // or STRAT_RAND1BIN
288  long long de_popsize = 15; // SciPy default NP ~= 15*D (so popsize is NP/D). We map so NP = de_popsize*num_params.
289  double de_mutation = 0.8; // F
290  double de_recombination = 0.7; // CR
291  double de_tol = 1e-6;
292  long long de_init = INIT_RANDOM; // or INIT_LHS
293  long long de_polish = 1; // run BFGS after DE?
294 
295 
296  if (config.count("de_strategy") > 0) config["de_strategy"].get_property(de_strategy);
297  if (config.count("de_popsize") > 0) config["de_popsize"].get_property(de_popsize);
298  if (config.count("de_mutation") > 0) config["de_mutation"].get_property(de_mutation);
299  if (config.count("de_recombination") > 0) config["de_recombination"].get_property(de_recombination);
300  if (config.count("de_tol") > 0) config["de_tol"].get_property(de_tol);
301  if (config.count("de_init") > 0) config["de_init"].get_property(de_init);
302  if (config.count("de_polish") > 0) { long long v; config["de_polish"].get_property(v); de_polish = v ? 1 : 0; }
303 
304  // Sanity + derived
305  if (de_mutation <= 0.0) de_mutation = 0.8;
306  if (de_recombination < 0.0) de_recombination = 0.7; else if (de_recombination > 1.0) de_recombination = 1.0;
307  if (de_popsize <= 0) de_popsize = 15;
308 
309  const int D = num_of_parameters;
310  int NP = static_cast<int>(de_popsize * D);
311  NP = std::max(5, NP);
312 
313  // ---------------- Population storage ----------------
314  std::vector<Matrix_real> pop(NP, Matrix_real(D, 1));
315  std::vector<double> fit(NP, DBL_MAX);
316 
317  // Initialize population
318  auto init_random = [&]() {
319  for (int i = 0; i < NP; ++i) {
320  for (int j = 0; j < D; ++j) {
321  pop[i][j] = distrib_real(gen) * 2.0 * M_PI; // in [0, 2π)
322  }
323  }
324  };
325 
326  auto init_lhs = [&]() {
327  // Simple stratified (Latin hypercube-like) per-dimension on [0, 2π)
328  std::vector<int> idx(NP);
329  for (int i = 0; i < NP; ++i) idx[i] = i;
330 
331  for (int j = 0; j < D; ++j) {
332  std::shuffle(idx.begin(), idx.end(), gen);
333  for (int i = 0; i < NP; ++i) {
334  double a = (double)idx[i] / (double)NP;
335  double b = (double)(idx[i] + 1) / (double)NP;
336  double r = a + (b - a) * distrib_real(gen);
337  pop[i][j] = fmod(r * (2.0 * M_PI), 2.0 * M_PI);
338  }
339  }
340  };
341 
342  if (de_init == INIT_LHS) init_lhs(); else init_random();
343 
344  // Evaluate initial population
345  #ifdef __MPI__
346  // If you want, you can distribute evaluations here; for simplicity we do sequential eval.
347  #endif
348  double best_f = DBL_MAX;
349  int best_i = 0;
350  for (int i = 0; i < NP; ++i) {
351  fit[i] = this->optimization_problem(pop[i]);
352  if (fit[i] < best_f) { best_f = fit[i]; best_i = i; }
353  }
354  if (best_f < current_minimum) {
355  current_minimum = best_f;
356  memcpy(optimized_parameters_mtx.get_data(), pop[best_i].get_data(), D * sizeof(double));
357  }
358 
359  // ---------------- DE loop ----------------
360  std::vector<int> idx_all(NP);
361  for (int i = 0; i < NP; ++i) idx_all[i] = i;
362 
363  auto pick_3_distinct = [&](int exclude) {
364  // pick r1, r2, r3 distinct and != exclude
365  int r1, r2, r3;
366  do { r1 = idx_all[std::uniform_int_distribution<int>(0, NP-1)(gen)]; } while (r1 == exclude);
367  do { r2 = idx_all[std::uniform_int_distribution<int>(0, NP-1)(gen)]; } while (r2 == exclude || r2 == r1);
368  do { r3 = idx_all[std::uniform_int_distribution<int>(0, NP-1)(gen)]; } while (r3 == exclude || r3 == r1 || r3 == r2);
369  return std::tuple<int,int,int>(r1, r2, r3);
370  };
371 
372  Matrix_real trial(D, 1), mutant(D, 1);
373  long long gen_no_improve = 0;
374  double last_best = best_f;
375 
376  for (long long generation = 0; generation < iteration_loops_max; ++generation) {
377 
378  for (int i = 0; i < NP; ++i) {
379  // ----- Mutation -----
380  if (de_strategy == STRAT_BEST1BIN) {
381  // mutant = best + F*(r1 - r2)
382  int r1, r2;
383  do { r1 = idx_all[std::uniform_int_distribution<int>(0, NP-1)(gen)]; } while (r1 == i || r1 == best_i);
384  do { r2 = idx_all[std::uniform_int_distribution<int>(0, NP-1)(gen)]; } while (r2 == i || r2 == r1 || r2 == best_i);
385  for (int j = 0; j < D; ++j) {
386  mutant[j] = fmod(pop[best_i][j] + de_mutation * (pop[r1][j] - pop[r2][j]), 2.0 * M_PI);
387  }
388  } else { // "rand1bin" default
389  // mutant = r1 + F*(r2 - r3)
390  int r1, r2, r3;
391  std::tie(r1, r2, r3) = pick_3_distinct(i);
392  for (int j = 0; j < D; ++j) {
393  mutant[j] = fmod(pop[r1][j] + de_mutation * (pop[r2][j] - pop[r3][j]), 2.0 * M_PI);
394  }
395  }
396 
397  // ----- Binomial crossover -----
398  int jrand = std::uniform_int_distribution<int>(0, D - 1)(gen);
399  for (int j = 0; j < D; ++j) {
400  if (j == jrand || distrib_real(gen) < de_recombination) {
401  trial[j] = mutant[j];
402  } else {
403  trial[j] = pop[i][j];
404  }
405  }
406 
407  // ----- Selection -----
408  double f_trial = this->optimization_problem(trial);
409  if (f_trial <= fit[i]) {
410  memcpy(pop[i].get_data(), trial.get_data(), D * sizeof(double));
411  fit[i] = f_trial;
412 
413  if (f_trial < best_f) {
414  best_f = f_trial;
415  best_i = i;
416  }
417  }
418  }
419 
420  // Update global best into project-wide state
421  if (best_f < current_minimum) {
422  current_minimum = best_f;
423  memcpy(optimized_parameters_mtx.get_data(), pop[best_i].get_data(), D * sizeof(double));
424  }
425 
426  // Progress / export
427  if (generation % 50 == 0) {
428  std::stringstream sstream;
429  sstream << "DE: generation " << generation << "/" << iteration_loops_max
430  << ", best=" << current_minimum << std::endl;
431  print(sstream, 2);
432 
433  if (export_circuit_2_binary_loc > 0) {
434  std::string filename("initial_circuit_iteration.binary");
435  if (project_name != "") filename = project_name + "_" + filename;
437  }
438  }
439 
440  if (output_periodicity > 0 && generation % output_periodicity == 0) {
442  }
443 
444  #ifdef __MPI__
445  // Optional sync of best-so-far to workers
446  MPI_Bcast((void*)optimized_parameters_mtx.get_data(), D, MPI_DOUBLE, 0, MPI_COMM_WORLD);
447  #endif
448 
449  // Stopping: reached target
450  if (current_minimum < optimization_tolerance_loc) break;
451 
452  // Stopping: no improvement by > de_tol in this generation
453  if (std::abs(last_best - best_f) <= de_tol) {
454  ++gen_no_improve;
455  } else {
456  gen_no_improve = 0;
457  }
458  last_best = best_f;
459 
460  if (gen_no_improve >= 1) {
461  // one stagnant generation with < de_tol delta (simple, effective)
462  break;
463  }
464  }
465 
466  // ---------------- Polish with BFGS (optional) ----------------
467  if (de_polish) {
468  BFGS_Powell cBFGS_Powell(optimization_problem_combined, this);
469  auto params = optimized_parameters_mtx.copy();
470  double f_pol = cBFGS_Powell.Start_Optimization(params, /*max_inner_iterations*/ static_cast<long>(std::max<long long>(200, static_cast<long long>(D)*50)));
471  if (f_pol < current_minimum) {
472  current_minimum = f_pol;
473  memcpy(optimized_parameters_mtx.get_data(), params.get_data(), D * sizeof(double));
474  }
475  }
476  } else if (use_dual_annealing) {
477  // ---------------- Dual annealing hyperparameters (SciPy-like) ----------------
478  double da_initial_temp = 5230.0; // initial temperature
479  double da_restart_temp_ratio = 2e-5; // threshold for restart
480  double da_visit = 2.62; // visiting distribution parameter (>1)
481  double da_accept = -5.0; // acceptance distribution parameter
482  long long da_maxiter = iteration_loops_max; // max temperature steps
483  long long da_maxfun = 10000000; // max function evaluations
484  double da_tol = 1e-6; // required improvement threshold
485  long long da_no_local_search = 0; // 0 => do local search (polish), 1 => skip
486  if (config.count("da_initial_temp") > 0) config["da_initial_temp"].get_property(da_initial_temp);
487  if (config.count("da_restart_temp_ratio") > 0) config["da_restart_temp_ratio"].get_property(da_restart_temp_ratio);
488  if (config.count("da_visit") > 0) config["da_visit"].get_property(da_visit);
489  if (config.count("da_accept") > 0) config["da_accept"].get_property(da_accept);
490  if (config.count("da_maxiter") > 0) {
491  long long v; config["da_maxiter"].get_property(v);
492  da_maxiter = std::max<long long>(1, v);
493  }
494  if (config.count("da_maxfun") > 0) {
495  long long v; config["da_maxfun"].get_property(v);
496  da_maxfun = std::max<long long>(1, v);
497  }
498  if (config.count("da_tol") > 0) config["da_tol"].get_property(da_tol);
499  if (config.count("da_no_local_search") > 0) {
500  long long v; config["da_no_local_search"].get_property(v);
501  da_no_local_search = v ? 1 : 0;
502  }
503 
504  // Sanity
505  if (da_initial_temp <= 0.0) da_initial_temp = 5230.0;
506  if (da_restart_temp_ratio <= 0.0 || da_restart_temp_ratio >= 1.0) da_restart_temp_ratio = 2e-5;
507  if (da_visit <= 1.0) da_visit = 2.62; // visiting parameter must be >1
508 
509  const int D = num_of_parameters;
510 
511  // Ensure initial guess is in [0, 2π)
512  for (int j = 0; j < D; ++j) {
513  solution_guess[j] = std::fmod(solution_guess[j], 2.0 * M_PI);
514  }
515 
516  // ---------------- Initial evaluation ----------------
517  double f_current = this->optimization_problem(solution_guess);
518  long long nfev = 1;
519 
520  Matrix_real x_best = solution_guess.copy();
521  double f_best = f_current;
522 
523  memcpy(optimized_parameters_mtx.get_data(), x_best.get_data(), D * sizeof(double));
524  current_minimum = f_best;
525 
526  double last_best = f_best;
527  int restart_count = 0;
528 
529  // ---------------- Dual annealing loop ----------------
530  for (long long k = 0; k < da_maxiter && nfev < da_maxfun; ++k) {
531 
532  // Temperature schedule (simple inverse cooling)
533  double Tk = da_initial_temp / (1.0 + (double)k + (double)restart_count);
534 
535  // Restart if temperature too low
536  if (Tk < da_initial_temp * da_restart_temp_ratio) {
537  ++restart_count;
538  // re-center around current global best
539  memcpy(solution_guess.get_data(), x_best.get_data(), D * sizeof(double));
540  f_current = f_best;
541  Tk = da_initial_temp / (1.0 + (double)restart_count);
542  }
543 
544  Matrix_real x_trial(D, 1);
545 
546  // Generalized visiting distribution (heavy-tailed)
547  // For each dimension j, generate a random step, scale by temperature, wrap to [0, 2π)
548  for (int j = 0; j < D; ++j) {
549  double u = distrib_real(gen); // in (0,1)
550  double sign = (u < 0.5) ? -1.0 : 1.0;
551  double v = distrib_real(gen); // second uniform
552  // Heavy-tail factor controlled by da_visit (>1)
553  double step_mag = std::pow(1.0 / std::max(1e-12, v), 1.0 / (da_visit - 1.0)) - 1.0;
554  // Scale by temperature, then map to angle scale
555  double step = sign * step_mag * Tk / da_initial_temp * (2.0 * M_PI);
556  x_trial[j] = std::fmod(solution_guess[j] + step, 2.0 * M_PI);
557  }
558 
559  double f_trial = this->optimization_problem(x_trial);
560  ++nfev;
561 
562  // Acceptance probability (Metropolis-like, shaped by da_accept)
563  bool accept = false;
564  if (f_trial <= f_current) {
565  accept = true;
566  } else {
567  double dE = f_trial - f_current;
568  // Scale controlled by temperature and da_accept (negative default)
569  double scale = std::pow(std::max(1e-12, Tk / da_initial_temp), -da_accept);
570  scale = std::max(scale, 1e-12);
571  double prob = std::exp(-dE / scale);
572  if (distrib_real(gen) < prob) {
573  accept = true;
574  }
575  }
576 
577  if (accept) {
578  memcpy(solution_guess.get_data(), x_trial.get_data(), D * sizeof(double));
579  f_current = f_trial;
580  }
581 
582  // Track global best
583  if (f_trial < f_best) {
584  f_best = f_trial;
585  memcpy(x_best.get_data(), x_trial.get_data(), D * sizeof(double));
586  memcpy(optimized_parameters_mtx.get_data(), x_best.get_data(), D * sizeof(double));
587  current_minimum = f_best;
588  }
589 
590  // Progress / export
591  if (k % 50 == 0) {
592  std::stringstream sstream;
593  sstream << "Dual annealing: iter " << k << "/" << da_maxiter
594  << ", best=" << current_minimum
595  << ", T=" << Tk << std::endl;
596  print(sstream, 2);
597 
598  if (export_circuit_2_binary_loc > 0) {
599  std::string filename("initial_circuit_iteration.binary");
600  if (project_name != "") filename = project_name + "_" + filename;
602  }
603  }
604 
605  if (output_periodicity > 0 && k % output_periodicity == 0) {
607  }
608 
609  #ifdef __MPI__
610  // Optional: keep workers synced to best-so-far
611  MPI_Bcast((void*)optimized_parameters_mtx.get_data(), D, MPI_DOUBLE, 0, MPI_COMM_WORLD);
612  #endif
613 
614  // Stopping: absolute target reached
615  if (current_minimum < optimization_tolerance_loc) {
616  break;
617  }
618 
619  // Stopping: improvement below da_tol
620  if (std::abs(last_best - f_best) <= da_tol) {
621  // one "cooling" step with negligible improvement
622  // You could require multiple stagnant steps if desired.
623  break;
624  }
625  last_best = f_best;
626  }
627 
628  // ---------------- Optional local polish with BFGS ----------------
629  if (!da_no_local_search) {
630  // Start from the best DA point
631 
632  BFGS_Powell cBFGS_Powell(optimization_problem_combined, this);
633  // You can tune max_inner_iterations here if you want
634  double f_pol = cBFGS_Powell.Start_Optimization(x_best, max_inner_iterations);
635 
636  if (f_pol < current_minimum) {
637  current_minimum = f_pol;
638  memcpy(optimized_parameters_mtx.get_data(), x_best.get_data(), D * sizeof(double));
639  }
640  }
641  }
642 
643  tbb::tick_count bfgs_end = tbb::tick_count::now();
644  CPU_time = CPU_time + (bfgs_end-bfgs_start).seconds();
645  //std::cout << "bfgs2 time: " << CPU_time << " " << current_minimum << std::endl;
646 
647 }
648 
649 
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
Matrix_real copy() const
Call to create a copy of the matrix.
int get_accelerator_num()
Get the number of accelerators to be reserved on DFEs on users demand.
double optimization_problem(double *parameters)
Evaluate the optimization problem of the optimization.
scalar * get_data() const
Call to get the pointer to the stored data.
std::vector< Gate * > gates
The list of stored gates.
Definition: Gates_block.h:49
A class implementing the BFGS optimizer based on conjugate gradient direction method of M...
Definition: BFGS_Powell.h:26
std::string project_name
the name of the project
double optimization_tolerance
The maximal allowed error of the optimization problem (The error of the decomposition would scale wit...
double Start_Optimization(Matrix_real &x, long maximal_iterations_in=5001)
Call this method to start the optimization.
#define M_PI
Definition: qgd_math.h:42
std::map< int, int > iteration_loops
A map of <int n: int num> indicating the number of iteration in each step of the decomposition.
void solve_layer_optimization_problem_BFGS2(int num_of_parameters, Matrix_real solution_guess)
Call to solve layer by layer the optimization problem via BBFG algorithm.
Definition: BFGS2.cpp:41
double CPU_time
time spent on optimization
int verbose
Set the verbosity level of the output messages.
Definition: logging.h:50
int size() const
Call to get the number of the allocated elements.
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
Header file for the paralleized calculation of the cost function of the final optimization problem (s...
volatile double current_minimum
The current minimum of the optimization problem.
void export_gate_list_to_binary(Matrix_real &parameters, Gates_block *gates_block, const std::string &filename, int verbosity)
Use to export a quantum circuit into binary format.
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:94
Header file for DFE support in unitary simulation.
int max_inner_iterations
the maximal number of iterations for which an optimization engine tries to solve the optimization pro...
int random_shift_count_max
the maximal number of parameter randomization tries to escape a local minimum.
Matrix_real optimized_parameters_mtx
The optimized parameters for the gates.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41
std::mt19937 gen
Standard mersenne_twister_engine seeded with rd()