Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Generative_Quantum_Machine_Learning_Base.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 <iostream>
24 #include <algorithm>
25 #include <random>
26 
27 static tbb::spin_mutex my_mutex;
28 
34 
35  // logical value describing whether the decomposition was finalized or not
37 
38 
39  // error of the unitarity of the final decomposition
40  decomposition_error = DBL_MAX;
41 
42 
43  // The current minimum of the optimization problem
44  current_minimum = DBL_MAX;
45 
46  global_target_minimum = -DBL_MAX;
47 
48  // logical value describing whether the optimization problem was solved or not
50 
51 
52  // The maximal allowed error of the optimization problem
53  optimization_tolerance = -DBL_MAX;
54 
55  // The convergence threshold in the optimization process
56  convergence_threshold = -DBL_MAX;
57 
58  alg = AGENTS;
59 
60 
62 
63  adaptive_eta = false;
64 
65  cost_fnc = GQML;
66 
67  ansatz = HEA;
68 
69  ev_P_star_P_star = -1.0;
70 
71 }
72 
73 
74 
75 
87 Generative_Quantum_Machine_Learning_Base::Generative_Quantum_Machine_Learning_Base(std::vector<int> sample_indices_in, std::vector<std::vector<int>> sample_bitstrings_in, Matrix_real P_star_in, Matrix_real sigma_in, int qbit_num_in, bool use_lookup_table_in, std::vector<std::vector<int>> cliques_in, bool use_exact_in, std::map<std::string, Config_Element>& config_in) : Optimization_Interface(Matrix(Power_of_2(qbit_num_in),1), qbit_num_in, false, config_in, RANDOM, 0) {
88 
89  sample_indices = sample_indices_in;
90 
91  sample_size = static_cast<int>(sample_indices.size());
92 
93  P_star = P_star_in;
94  // config maps
95  config = config_in;
96  // logical value describing whether the decomposition was finalized or not
98 
99 
100  // error of the unitarity of the final decomposition
101  decomposition_error = DBL_MAX;
102 
103 
104  // The current minimum of the optimization problem
105  current_minimum = DBL_MAX;
106 
107 
108  // logical value describing whether the optimization problem was solved or not
110 
111  // override optimization parameters governing the convergence used in gate decomposition applications
112  global_target_minimum = -DBL_MAX;
113  optimization_tolerance = -DBL_MAX;
114  convergence_threshold = -DBL_MAX;
115 
116 
117 
119 
120  adaptive_eta = false;
121 
122  qbit_num = qbit_num_in;
123 
124  alg = BAYES_OPT;
125 
126  cost_fnc = GQML;
127 
128  ansatz = HEA;
129 
130  ev_P_star_P_star = -1.0;
131 
132  sigma = sigma_in;
133 
134  sample_bitstrings = sample_bitstrings_in;
135 
136  use_lookup = use_lookup_table_in;
137 
138  use_exact = use_exact_in;
139 
140  for (int idx=0; idx<1<<qbit_num; idx++) {
141  std::vector<int> bitstring;
142  for (int j = qbit_num - 1; j >= 0; --j) {
143  bitstring.push_back(((idx) >> j) & 1);
144  }
145  all_bitstrings.push_back(bitstring);
146  }
147 
148  if (use_lookup) {
150  }
151 
152  if (use_exact) {
155  }
156  else {
159  }
160 
161 
162  cliques = cliques_in;
163 }
164 
165 
166 
167 
172 
173 }
174 
175 
176 
177 
182 
183  // initialize the initial state if it was not given
184  if ( initial_state.size() == 0 ) {
186  }
187 
188 
189  if (gates.size() == 0 ) {
190  std::string error("Variational_Quantum_Eigensolver_Base::Get_ground_state: for GQML process the circuit needs to be initialized");
191  throw error;
192  }
193 
195  if ( num_of_parameters == 0 ) {
196  std::string error("Variational_Quantum_Eigensolver_Base::Get_ground_state: No intial parameters were given");
197  throw error;
198  }
199 
200 
201  if ( num_of_parameters != get_parameter_num() ) {
202  std::string error("Variational_Quantum_Eigensolver_Base::Get_ground_state: The number of initial parameters does not match with the number of parameters in the circuit");
203  throw error;
204  }
205 
206 
207  // start the GQML process
208  Matrix_real solution_guess = optimized_parameters_mtx.copy();
209  solve_layer_optimization_problem(num_of_parameters, solution_guess);
210 
211  if (ev_P_star_P_star == -1) {
213  }
214  if (use_lookup && gaussian_lookup_table.size() == 0) {
216  }
217 
218  return;
219 }
220 
229  // The norm stores the distance between the two data points (the more qbit they differ in the bigger it is)
230  double norm = (x - y)*(x - y);
231  double result = (exp(-norm*0.5/sigma[0])+ exp(-norm*0.5/sigma[1])+ exp(-norm*0.5/sigma[2]))/3;
232  return result;
233 }
234 
239  gaussian_lookup_table = std::vector<std::vector<double>>(size_t(1)<<qbit_num, std::vector<double>(size_t(1)<<qbit_num, 0));
240  for (int idx1=0; idx1 < 1<<qbit_num; idx1++) {
241  for (int idx2=0; idx2 < 1<<qbit_num; idx2++) {
242  gaussian_lookup_table[idx1][idx2] = Gaussian_kernel(idx1, idx2, sigma);
243  }
244  }
245 }
246 
252  double ev=0.0;
253  tbb::combinable<double> priv_partial_ev{[](){return 0.0;}};
254  tbb::parallel_for( tbb::blocked_range<int>(0, sample_size, 1024), [&](tbb::blocked_range<int> r) {
255  double& ev_local = priv_partial_ev.local();
256  for (int idx1=r.begin(); idx1<r.end(); idx1++) {
257  for (int idx2=0; idx2<sample_size; idx2++) {
258  if (idx1 != idx2) {
259  ev_local += Gaussian_kernel(idx1, idx2, sigma);
260  }
261  }
262  }
263  });
264  priv_partial_ev.combine_each([&ev](double a) {
265  ev += a;
266  });
267  ev /= sample_size*(sample_size-1);
268  return ev;
269 }
270 
276  double ev=0.0;
277  tbb::combinable<double> priv_partial_ev{[](){return 0.0;}};
278  tbb::parallel_for( tbb::blocked_range<int>(0, 1<<qbit_num, 1024), [&](tbb::blocked_range<int> r) {
279  double& ev_local = priv_partial_ev.local();
280  for (int idx1=r.begin(); idx1<r.end(); idx1++) {
281  for (int idx2=0; idx2<1<<qbit_num; idx2++) {
282  ev_local += P_star[idx1]*P_star[idx2]*Gaussian_kernel(idx1, idx2, sigma);
283  }
284  }
285  });
286  priv_partial_ev.combine_each([&ev](double a) {
287  ev += a;
288  });
289  return ev;
290 }
291 
298  std::vector<double> P_theta;
299 
300  for (int x_idx=0; x_idx < static_cast<int>(State_right.size()); x_idx++){
301  P_theta.push_back(State_right[x_idx].real*State_right[x_idx].real +State_right[x_idx].imag*State_right[x_idx].imag);
302  }
303 
304  double TV = 0.0;
305  for (int i=0; i < static_cast<int>(P_theta.size()); i++) {
306  TV += abs(P_theta[i]-P_star[i]);
307  }
308  return TV*0.5;
309 }
310 
311 
318  // If the ev of the P_star hasnt been evaluated we need to evaluate it
319  if (ev_P_star_P_star < 0 ) {
321  }
322 
323  // We calculate the distribution created by our circuit at the given traing data points "we sample our distribution"
324  std::vector<double> P_theta(size_t(1)<<qbit_num);
325  tbb::parallel_for( tbb::blocked_range<int>(0, 1<<qbit_num, 1024), [&](tbb::blocked_range<int> r) {
326  for (int idx=r.begin(); idx<r.end(); idx++) {
327  P_theta[idx] = State_right[idx].real*State_right[idx].real + State_right[idx].imag*State_right[idx].imag;
328  }
329  });
330 
331  // Calculate the expectation values
332  double ev_P_theta_P_theta = 0.0;
333  double ev_P_theta_P_star = 0.0;
334  tbb::combinable<double> priv_partial_ev_P_theta_P_theta{[](){return 0.0;}};
335  tbb::combinable<double> priv_partial_ev_P_theta_P_star{[](){return 0.0;}};
336  tbb::parallel_for( tbb::blocked_range2d<int>(0, 1<<qbit_num, 0, 1<<qbit_num), [&](tbb::blocked_range2d<int> r) {
337  double& ev_P_theta_P_theta_local = priv_partial_ev_P_theta_P_theta.local();
338  double& ev_P_theta_P_star_local = priv_partial_ev_P_theta_P_star.local();
339  for (int idx1=r.rows().begin(); idx1<r.rows().end(); idx1++) {
340  for (int idx2=r.cols().begin(); idx2<r.cols().end(); idx2++) {
341  if (use_lookup) {
342  ev_P_theta_P_theta_local += P_theta[idx1]*P_theta[idx2]*gaussian_lookup_table[idx1][idx2];
343  ev_P_theta_P_star_local += P_theta[idx1]*P_star[idx2]*gaussian_lookup_table[idx1][idx2];
344  }
345  else {
346  ev_P_theta_P_theta_local += P_theta[idx1]*P_theta[idx2]*Gaussian_kernel(idx1, idx2, sigma);
347  ev_P_theta_P_star_local += P_theta[idx1]*P_star[idx2]*Gaussian_kernel(idx1, idx2, sigma);
348  }
349  }
350  }
351  });
352  priv_partial_ev_P_theta_P_theta.combine_each([&ev_P_theta_P_theta](double a) {
353  ev_P_theta_P_theta += a;
354  });
355  priv_partial_ev_P_theta_P_star.combine_each([&ev_P_theta_P_star](double a) {
356  ev_P_theta_P_star += a;
357  });
358 
359 
360  {
361  tbb::spin_mutex::scoped_lock my_lock{my_mutex};
362 
363  number_of_iters++;
364 
365  }
366 
367  double result = ev_P_theta_P_theta + ev_P_star_P_star - 2*ev_P_theta_P_star;
368  return result;
369 }
370 
371 
378 
379  // If the ev of the P_star hasnt been evaluated we need to evaluate it
380  if (ev_P_star_P_star < 0 ) {
382  }
383 
384  // We calculate the distribution created by our circuit at the given traing data points "we sample our distribution"
385  std::vector<double> P_theta(size_t(1)<<qbit_num);
386  tbb::parallel_for( tbb::blocked_range<int>(0, 1<<qbit_num, 1024), [&](tbb::blocked_range<int> r) {
387  for (int idx=r.begin(); idx<r.end(); idx++) {
388  P_theta[idx] = State_right[idx].real*State_right[idx].real + State_right[idx].imag*State_right[idx].imag;
389  }
390  });
391 
392  std::random_device rd;
393  std::mt19937 gen(rd());
394  std::discrete_distribution<> dist(P_theta.begin(), P_theta.end());
395 
396  std::vector<int> theta_sample_indices(sample_size);
397  tbb::parallel_for( tbb::blocked_range<int>(0, sample_size, 1024), [&](tbb::blocked_range<int> r) {
398  for (int sample_idx=r.begin(); sample_idx < r.end(); sample_idx++) {
399  theta_sample_indices[sample_idx] = dist(gen);
400  }
401  });
402 
403 
404  // Calculate the expectation values
405  double ev_P_theta_P_theta = 0.0;
406  double ev_P_theta_P_star = 0.0;
407  tbb::combinable<double> priv_partial_ev_P_theta_P_theta{[](){return 0.0;}};
408  tbb::combinable<double> priv_partial_ev_P_theta_P_star{[](){return 0.0;}};
409  tbb::parallel_for( tbb::blocked_range2d<int>(0, sample_size, 0, sample_size), [&](tbb::blocked_range2d<int> r) {
410  double& ev_P_theta_P_theta_local = priv_partial_ev_P_theta_P_theta.local();
411  double& ev_P_theta_P_star_local = priv_partial_ev_P_theta_P_star.local();
412  for (int idx1=r.rows().begin(); idx1<r.rows().end(); idx1++) {
413  for (int idx2=r.cols().begin(); idx2<r.cols().end(); idx2++) {
414  if (use_lookup) {
415  if (idx1 != idx2) {
416  ev_P_theta_P_theta_local += gaussian_lookup_table[theta_sample_indices[idx1]][theta_sample_indices[idx2]];
417  }
418  ev_P_theta_P_star_local += gaussian_lookup_table[theta_sample_indices[idx1]][sample_indices[idx2]];
419  }
420  else {
421  if (idx1 != idx2) {
422  ev_P_theta_P_theta_local += Gaussian_kernel(theta_sample_indices[idx1],theta_sample_indices[idx2], sigma);
423  }
424  ev_P_theta_P_star_local += Gaussian_kernel(theta_sample_indices[idx1], sample_indices[idx2], sigma);
425  }
426  }
427  }
428  });
429  priv_partial_ev_P_theta_P_theta.combine_each([&ev_P_theta_P_theta](double a) {
430  ev_P_theta_P_theta += a;
431  });
432  priv_partial_ev_P_theta_P_star.combine_each([&ev_P_theta_P_star](double a) {
433  ev_P_theta_P_star += a;
434  });
435 
436  ev_P_theta_P_theta /= sample_size*(sample_size-1);
437  ev_P_theta_P_star /= sample_size*sample_size;
438 
439  {
440  tbb::spin_mutex::scoped_lock my_lock{my_mutex};
441 
442  number_of_iters++;
443 
444  }
445  double result = ev_P_theta_P_theta + ev_P_star_P_star - 2*ev_P_theta_P_star;
446  return result;
447 }
448 
449 
457 
458  double MMD=0.0;
459 
460  Generative_Quantum_Machine_Learning_Base* instance = reinterpret_cast<Generative_Quantum_Machine_Learning_Base*>(void_instance);
461 
462  Matrix State = instance->initial_state.copy();
463 
464  instance->apply_to(parameters, State);
465  MMD = (instance->*MMD_of_the_distributions)(State);
466 
467  return MMD;
468 }
469 
470 
477 
478  // initialize the initial state if it was not given
479  if ( initial_state.size() == 0 ) {
481  }
482 
484 
485  apply_to(parameters, State);
486 
487 
488  double MMD = (this->*MMD_of_the_distributions)(State);
489 
490  return MMD;
491 }
492 
493 
494 
503 
504  Generative_Quantum_Machine_Learning_Base* instance = reinterpret_cast<Generative_Quantum_Machine_Learning_Base*>(void_instance);
505 
506  // initialize the initial state if it was not given
507  if ( instance->initial_state.size() == 0 ) {
508  instance->initialize_zero_state();
509  }
510 
511  // the number of free parameters
512  int parameter_num_loc = parameters.size();
513 
514  Matrix_real cost_function_terms;
515 
516  // vector containing gradients of the transformed matrix
517  std::vector<Matrix> State_deriv;
518  Matrix State;
519 
520  int parallel = get_parallel_configuration();
521 
522  tbb::parallel_invoke(
523  [&]{
524  State = instance->initial_state.copy();
525  instance->apply_to(parameters, State);
526  *f0 = (instance->*MMD_of_the_distributions)(State);
527 
528  },
529  [&]{
530  Matrix State_loc = instance->initial_state.copy();
531 
532  State_deriv = instance->apply_derivate_to( parameters, State_loc, parallel );
533  State_loc.release_data();
534  });
535 
536  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,2), [&](tbb::blocked_range<int> r) {
537  for (int idx=r.begin(); idx<r.end(); ++idx) {
538  grad[idx] = 2*(instance->*MMD_of_the_distributions)(State_deriv[idx]);
539  }
540  });
541 
542  /*
543  double delta = 0.0000001;
544 
545  tbb::parallel_for( tbb::blocked_range<int>(0,parameter_num_loc,1), [&](tbb::blocked_range<int> r) {
546  for (int idx=r.begin(); idx<r.end(); ++idx) {
547  Matrix_real param_tmp = parameters.copy();
548  param_tmp[idx] += delta;
549 
550  Matrix State = instance->initial_state.copy();
551  instance->apply_to(param_tmp, State);
552  double f_loc = instance->Expectation_value_of_energy_real(State, State);
553 
554 
555  grad[idx] = (f_loc-*f0)/delta;
556  }
557  });
558 
559 */
560  return;
561 }
562 
563 
564 
565 
566 
567 
575 
576  optimization_problem_combined_non_static( parameters, this, f0, grad );
577  return;
578 }
579 
580 
581 
582 
590 
591  double f0;
592  Generative_Quantum_Machine_Learning_Base* instance = reinterpret_cast<Generative_Quantum_Machine_Learning_Base*>(void_instance);
593  instance->optimization_problem_combined_non_static(parameters, void_instance, &f0, grad);
594  return;
595 
596 }
597 
598 
599 
600 
607 
608  // get the transformed matrix with the gates in the list
609  Matrix_real parameters_mtx(parameters, 1, parameter_num );
610 
611  return optimization_problem( parameters_mtx );
612 
613 
614 }
615 
622 
623  FILE* pFile;
624  std::string filename("costfuncs_entropy_and_tv.txt");
625 
626  if (project_name != ""){filename = project_name + "_" + filename;}
627 
628  const char* c_filename = filename.c_str();
629 #ifdef _WIN32
630 #pragma warning(suppress: 4996)
631 #endif
632  pFile = fopen(c_filename, "a");
633 
634  if (pFile==NULL) {
635  fputs ("File error",stderr);
636  std::string error("Cannot open file.");
637  throw error;
638  }
639 
640  Matrix input_state(Power_of_2(qbit_num),1);
641 
642  std::uniform_int_distribution<> distrib(0, qbit_num-2);
643 
644  memset(input_state.get_data(), 0, (input_state.size()*2)*sizeof(double) );
645  input_state[0].real = 1.0;
646 
647  matrix_base<int> qbit_sublist(1,2);
648  qbit_sublist[0] = 0;//distrib(gen);
649  qbit_sublist[1] = 1;//qbit_sublist[0]+1;
650 
651  double renyi_entropy = get_second_Renyi_entropy(parameters, input_state, qbit_sublist);
652 
653  // initialize the initial state if it was not given
654  if ( initial_state.size() == 0 ) {
656  }
657 
659  apply_to(parameters, State);
660 
661  double tv_distance = TV_of_the_distributions(State);
662 
663  fprintf(pFile,"%i\t%f\t%f\t%f\n", (int)number_of_iters, current_minimum, renyi_entropy, tv_distance);
664  fclose(pFile);
665 
666  return;
667 }
668 
669 
670 
675 
676  initial_state = Matrix( 1 << qbit_num , 1);
677 
678 
679  initial_state[0].real = 1.0;
680  initial_state[0].imag = 0.0;
681  memset(initial_state.get_data()+2, 0, (initial_state.size()*2-2)*sizeof(double) );
682 
683  return;
684 }
685 
686 
687 
688 
689 
695 
696  ansatz = ansatz_in;
697 
698  return;
699 }
700 
701 
702 
703 
704 
711 
712 
713  switch (ansatz){
714 
715  case HEA:
716  {
717 
718  release_gates();
719 
720  if ( qbit_num < 2 ) {
721  std::string error("Variational_Quantum_Eigensolver_Base::generate_initial_circuit: number of qubits should be at least 2");
722  throw error;
723  }
724 
725  for (int layer_idx=0; layer_idx<layers ;layer_idx++){
726 
727  for( int idx=0; idx<inner_blocks; idx++) {
728  add_u3(1);
729  add_u3(0);
730  add_cnot(1,0);
731  }
732 
733 
735  if (control_qbit+2<qbit_num){
736 
737  for( int idx=0; idx<inner_blocks; idx++) {
738  add_u3(control_qbit+1);
739  add_u3(control_qbit+2);
740 
742  }
743 
744  }
745 
746  for( int idx=0; idx<inner_blocks; idx++) {
747  add_u3(control_qbit+1);
749 
751 
752  }
753 
754  }
755  }
756 
757 
758  return;
759  }
760  case HEA_ZYZ:
761  {
762 
763  release_gates();
764 
765  if ( qbit_num < 2 ) {
766  std::string error("Variational_Quantum_Eigensolver_Base::generate_initial_circuit: number of qubits should be at least 2");
767  throw error;
768  }
769 
770  for (int layer_idx=0; layer_idx<layers ;layer_idx++){
771 
772  for( int idx=0; idx<inner_blocks; idx++) {
773  Gates_block* block_1 = new Gates_block( qbit_num );
774  Gates_block* block_2 = new Gates_block( qbit_num );
775 
776  // single qubit gates in a ablock are then merged into a single gate kernel.
777  block_1->add_rz(1);
778  block_1->add_ry(1);
779  block_1->add_rz(1);
780 
781  add_gate( block_1 );
782 
783  block_2->add_rz(0);
784  block_2->add_ry(0);
785  block_2->add_rz(0);
786 
787  add_gate( block_2 );
788 
789  add_cnot(1,0);
790  }
791 
793  if (control_qbit+2<qbit_num){
794 
795  for( int idx=0; idx<inner_blocks; idx++) {
796 
797  Gates_block* block_1 = new Gates_block( qbit_num );
798  Gates_block* block_2 = new Gates_block( qbit_num );
799 
800  block_1->add_rz(control_qbit+1);
801  block_1->add_ry(control_qbit+1);
802  block_1->add_rz(control_qbit+1);
803  add_gate( block_1 );
804 
805  block_2->add_rz(control_qbit+2);
806  block_2->add_ry(control_qbit+2);
807  block_2->add_rz(control_qbit+2);
808  add_gate( block_2 );
809 
811  }
812 
813  }
814 
815  for( int idx=0; idx<inner_blocks; idx++) {
816 
817  Gates_block* block_1 = new Gates_block( qbit_num );
818  Gates_block* block_2 = new Gates_block( qbit_num );
819 
820  block_1->add_rz(control_qbit+1);
821  block_1->add_ry(control_qbit+1);
822  block_1->add_rz(control_qbit+1);
823  add_gate( block_1 );
824 
825  block_2->add_rz(control_qbit);
826  block_2->add_ry(control_qbit);
827  block_2->add_rz(control_qbit);
828  add_gate( block_2 );
829 
831  }
832 
833  }
834  }
835 
836  return;
837  }
838 
839  case QCMRF:
840  {
841  int num_cliques = static_cast<int>(cliques.size());
842  if (cliques.size() == 0) {
843  std::string error("Variational_Quantum_Eigensolver_Base::generate_initial_circuit: input the cliques for using QCMRF ansatz");
844  throw error;
845  }
846  release_gates();
847  for (int qbit_idx=0; qbit_idx < qbit_num; qbit_idx++) {
848  add_h(qbit_idx);
849  }
850 
851  std::vector<std::vector<int>> all_subsets;
852  for (int clique_idx = 0; clique_idx < num_cliques; clique_idx++) {
853  std::vector<int> subset;
854  generate_clique_circuit(0, cliques[clique_idx], all_subsets, subset);
855  }
856  for (int qbit_idx=0; qbit_idx < qbit_num; qbit_idx++) {
857  add_u3(qbit_idx);
858  }
859  return;
860  }
861  default:
862  std::string error("Generative_Quantum_Machine_Learning_Base::generate_initial_circuit: ansatz not implemented");
863  throw error;
864  }
865 
866 }
867 
873  for (size_t idx=0; idx<qbits.size()-1; idx++) {
874  add_cnot(qbits[idx+1], qbits[idx]);
875  }
876  add_rz(qbits[qbits.size()-1]);
877  for (int idx=static_cast<int>(qbits.size())-1; idx>0; idx--) {
878  add_cnot(qbits[idx], qbits[idx-1]);
879  }
880 }
881 
888 void Generative_Quantum_Machine_Learning_Base::generate_clique_circuit(int i, std::vector<int>& arr, std::vector<std::vector<int>>& res, std::vector<int>& subset) {
889  if (static_cast<size_t>(i) == arr.size()) {
890  if (subset.size() != 0) {
891  if (std::find(res.begin(), res.end(), subset) == res.end()) {
892  res.push_back(subset);
893  MultyRZ(subset);
894  }
895  }
896  return;
897  }
898 
899  subset.push_back(arr[i]);
900  generate_clique_circuit(i+1, arr, res, subset);
901 
902  subset.pop_back();
903  generate_clique_circuit(i+1, arr, res, subset);
904 }
905 
906 void
908 
909  release_gates();
910  Matrix_real optimized_parameters_mtx_tmp;
911  Gates_block* gate_structure_tmp = import_gate_list_from_binary(optimized_parameters_mtx_tmp, filename);
912 
913  if ( gates.size() > 0 ) {
914  gate_structure_tmp->combine( static_cast<Gates_block*>(this) );
915 
916  release_gates();
917  combine( gate_structure_tmp );
918 
919 
920  Matrix_real optimized_parameters_mtx_tmp2( 1, optimized_parameters_mtx_tmp.size() + optimized_parameters_mtx.size() );
921  memcpy( optimized_parameters_mtx_tmp2.get_data(), optimized_parameters_mtx_tmp.get_data(), optimized_parameters_mtx_tmp.size()*sizeof(double) );
922  memcpy( optimized_parameters_mtx_tmp2.get_data()+optimized_parameters_mtx_tmp.size(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
923  optimized_parameters_mtx = optimized_parameters_mtx_tmp2;
924  }
925  else {
926  combine( gate_structure_tmp );
927  optimized_parameters_mtx = optimized_parameters_mtx_tmp;
928 
929  std::stringstream sstream;
930  // get the number of gates used in the decomposition
931  std::map<std::string, int>&& gate_nums = get_gate_nums();
932 
933  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
934  sstream << it->second << " " << it->first << " gates" << std::endl;
935  }
936  print(sstream, 1);
937  }
938 
939 }
940 
941 
942 
943 
949 
950 
951 
952  // check the size of the input state
953  if ( initial_state_in.size() != 1 << qbit_num ) {
954  std::string error("Variational_Quantum_Eigensolver_Base::set_initial_state: teh number of elements in the input state does not match with the number of qubits.");
955  throw error;
956  }
957 
958  initial_state = Matrix( 1 << qbit_num, 1 );
959  memcpy( initial_state.get_data(), initial_state_in.get_data(), initial_state_in.size()*sizeof( QGD_Complex16 ) );
960 
961 }
962 
963 
964 
965 
virtual double optimization_problem(Matrix_real &parameters) override
The optimization problem of the final optimization.
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 ...
virtual ~Generative_Quantum_Machine_Learning_Base()
Destructor of the class.
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
ansatz_type
Type definition of the fifferent types of ansatz.
void add_h(int target_qbit)
Append a Hadamard gate to the list of gates.
static tbb::spin_mutex my_mutex
bool decomposition_finalized
The optimized parameters for the gates.
std::map< std::string, int > get_gate_nums()
Call to get the number of the individual gate types in the list of gates.
static void optimization_problem_grad_vqe(Matrix_real parameters, void *void_instance, Matrix_real &grad)
Calculate the derivative of the cost function with respect to the free parameters.
void set_gate_structure(std::string filename)
Call to set custom layers to the gate structure that are intended to be used in the GQML process...
double expectation_value_P_star_P_star_exact()
Call to evaluate the expectation value of the square of the distribution.
Matrix_real copy() const
Call to create a copy of the matrix.
int control_qbit
The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations...
Definition: Gate.h:100
bool optimization_problem_solved
logical value describing whether the optimization problem was solved or not
void add_gate(Gate *gate)
Append a general gate to the list of gates.
cost_function_type cost_fnc
The chosen variant of the cost function.
void generate_circuit(int layers, int inner_blocks)
Call to generate the circuit ansatz.
double Gaussian_kernel(int x, int y, Matrix_real &sigma)
Call to evaluate the value of one gaussian kernel function.
void release_gates()
Call to release the stored gates.
double MMD_of_the_distributions_approx(Matrix &State_right)
Call to evaluate the approximated maximum mean discrepancy of the given distribution and the one crea...
void set_ansatz(ansatz_type ansatz_in)
Call to set the ansatz type.
void release_data()
Call to release the data stored by the matrix.
Generative_Quantum_Machine_Learning_Base()
Nullary constructor of the class.
std::atomic< int > number_of_iters
number of iterations
void add_cnot(int target_qbit, int control_qbit)
Append a CNOT gate gate to the list of gates.
scalar * get_data() const
Call to get the pointer to the stored data.
virtual void optimization_problem_combined_non_static(Matrix_real parameters, void *void_instance, double *f0, Matrix_real &grad) override
Call to calculate both the cost function and the its gradient components.
Gates_block * import_gate_list_from_binary(Matrix_real &parameters, const std::string &filename, int verbosity)
Use to import a quantum circuit from a binary format.
double MMD_of_the_distributions_exact(Matrix &State_right)
Call to evaluate the maximum mean discrepancy of the given distribution and the one created by our ci...
double ev_P_star_P_star
The expectation value of the the square of the given ditribution (only needed to calculate once) ...
int P_theta
Definition: GQML_test.py:81
std::vector< Gate * > gates
The list of stored gates.
Definition: Gates_block.h:49
double expectation_value_P_star_P_star_approx()
Call to evaluate the approximated expectation value of the square of the distribution.
std::vector< std::vector< int > > sample_bitstrings
Same as the x_vectors but in binary.
std::string project_name
the name of the project
double TV_of_the_distributions(Matrix &State_right)
Call to evaluate the total variational distance of the given distribution and the one created by our ...
double optimization_tolerance
The maximal allowed error of the optimization problem (The error of the decomposition would scale wit...
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.
void export_current_cost_fnc(double current_minimum, Matrix_real &parameters) override
Call to print out into a file the current cost function and the second Rényi entropy on the subsyste...
double convergence_threshold
The convergence threshold in the optimization process.
Matrix initial_state
Quantum state used as an initial state in the VQE iterations.
void add_u3(int target_qbit)
Append a U3 gate to the list of gates.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
void add_rz(int target_qbit)
Append a RZ gate to the list of gates.
void combine(Gates_block *op_block)
Call to append the gates of an gate block to the current block.
void add_ry(int target_qbit)
Append a RY gate to the list of gates.
A base class to solve GQML problems This class can be used to approximate a given distribution via a ...
virtual void optimization_problem_combined(Matrix_real parameters, double *f0, Matrix_real grad) override
Call to calculate both the cost function and the its gradient components.
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.
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
Matrix copy() const
Call to create a copy of the matrix.
Definition: matrix.h:57
void start_optimization()
Call to start solving the GQML problem.
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:136
Double-precision complex matrix (float64).
Definition: matrix.h:38
Matrix_real sigma
Parameter of the Gaussian kernel.
int size() const
Call to get the number of the allocated elements.
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:82
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
void fill_lookup_table()
Call to calculate and save the values of the gaussian kernel needed for traing.
ansatz_type ansatz
Ansatz type (HEA stands for hardware efficient ansatz)
Class to solve GQML problems.
virtual double optimization_problem_non_static(Matrix_real parameters, void *void_instance) override
The optimization problem of the final optimization.
std::map< std::string, Config_Element > config
config metadata utilized during the optimization
void set_initial_state(Matrix initial_state_in)
Call to set the initial quantum state in the VQE iterations.
double(Generative_Quantum_Machine_Learning_Base::* MMD_of_the_distributions)(Matrix &)
void generate_clique_circuit(int i, std::vector< int > &qbits, std::vector< std::vector< int >> &res, std::vector< int > &subset)
Call to generate the circuit ansatz for the given clique.
Matrix_real P_star
The distribution we are trying to approximate.
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...
void initialize_zero_state()
Initialize the state used in the quantun circuit.
volatile double current_minimum
The current minimum of the optimization problem.
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:94
void MultyRZ(std::vector< int > &qbits)
Call to generate a MultiRZ gate.
double decomposition_error
error of the final decomposition
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.
int get_parallel_configuration()
Get the parallel configuration from the config.
int get_parameter_num() override
Call to get the number of free parameters.
std::vector< int > sample_indices
The state vector&#39;s corresponding indices of the training data.
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
std::mt19937 gen
Standard mersenne_twister_engine seeded with rd()
virtual std::vector< Matrix > apply_derivate_to(Matrix_real &parameters_mtx, Matrix &input, int parallel) override
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...