Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Decomposition_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 "Decomposition_Base.h"
25 #include <limits>
26 
27 // default layer numbers
29 
30 
35 
36 
37  // A string labeling the gate operation
38  name = "Decomposition_Interface";
39 
41 
42 
43  // logical value describing whether the decomposition was finalized or not
45 
46  // A string describing the type of the class
48 
49  // error of the unitarity of the final decomposition
50  decomposition_error = DBL_MAX;
51 
52  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
54 
55  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
57 
58  // The current minimum of the optimization problem
59  current_minimum = std::numeric_limits<double>::max();
60 
61  // The global minimum of the optimization problem
63 
64  // logical value describing whether the optimization problem was solved or not
66 
67  // number of iteratrion loops in the finale optimization
68  //iteration_loops = dict()
69 
70  // The maximal allowed error of the optimization problem
72 
73  // Maximal number of iteartions in the optimization process
75 
76  // number of operators in one sub-layer of the optimization process
77  optimization_block = -1;
78 
79  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
81 
82  // The convergence threshold in the optimization process
83  convergence_threshold = 1e-5;
84 
85  //global phase of the unitary matrix
88 
89  //the name of the SQUANDER project
90  std::string projectname = "";
91 
92 
93  // Will be used to obtain a seed for the random number engine
94  std::random_device rd;
95 
96  // seedign the random generator
97  gen = std::mt19937(rd());
98 
99 #if CBLAS==1
100  num_threads = mkl_get_max_threads();
101 #elif CBLAS==2
102  num_threads = openblas_get_num_threads();
103 #endif
104 
105 
106 
107 }
108 
109 
117 Decomposition_Base::Decomposition_Base( Matrix Umtx_in, int qbit_num_in, std::map<std::string, Config_Element>& config_in, guess_type initial_guess_in= CLOSE_TO_ZERO ) : Gates_block(qbit_num_in) {
118 
119  // A string labeling the gate operation
120  name = "Decomposition_Interface";
121 
123 
124 
125  // the unitary operator to be decomposed
126  Umtx = Umtx_in;
127 
128  // logical value describing whether the decomposition was finalized or not
129  decomposition_finalized = false;
130 
131  // A string describing the type of the class
133 
134  // error of the unitarity of the final decomposition
135  decomposition_error = DBL_MAX;
136 
137  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
139 
140  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
142 
143  // The current minimum of the optimization problem
144  current_minimum = std::numeric_limits<double>::max();
145 
146  // The global minimum of the optimization problem
148 
149  // logical value describing whether the optimization problem was solved or not
151 
152  // number of iteratrion loops in the finale optimization
153  //iteration_loops = dict()
154 
155  // The maximal allowed error of the optimization problem
156  optimization_tolerance = 1e-7;
157 
158  // Maximal number of iteartions in the optimization process
159  max_outer_iterations = 1e8;
160 
161  // number of operators in one sub-layer of the optimization process
162  optimization_block = -1;
163 
164  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
165  initial_guess = initial_guess_in;
166 
167  // The convergence threshold in the optimization process
168  convergence_threshold = 1e-5;
169 
170  //global phase of the unitary matrix
173 
174  //name of the SQUANDER project
175  std::string projectname = "";
176 
177 
178  // config maps
179  config = config_in;
180 
181 
182  // Will be used to obtain a seed for the random number engine
183  std::random_device rd;
184 
185  // seedign the random generator
186  gen = std::mt19937(rd());
187 
188 #if CBLAS==1
189  num_threads = mkl_get_max_threads();
190 #elif CBLAS==2
191  num_threads = openblas_get_num_threads();
192 #endif
193 
194 #ifdef __MPI__
195  // Get the number of processes
196  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
197 
198  // Get the rank of the process
199  MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
200 
201 #endif
202 
203 }
204 
209 /*
210  if (optimized_parameters != NULL ) {
211  qgd_free( optimized_parameters );
212  optimized_parameters = NULL;
213  }
214 */
215 }
216 
217 
222 void Decomposition_Base::set_optimization_blocks( int optimization_block_in) {
223  optimization_block = optimization_block_in;
224 }
225 
230 void Decomposition_Base::set_max_iteration( int max_outer_iterations_in) {
231  max_outer_iterations = max_outer_iterations_in;
232 }
233 
234 
235 
236 
241 void Decomposition_Base::list_gates( int start_index ) {
242 
244 
245 }
246 
247 
248 
249 
250 
256 void Decomposition_Base::solve_optimization_problem( double* solution_guess, int solution_guess_num ) {
257 
258 
259  if ( gates.size() == 0 ) {
260  return;
261  }
262 
263  // array containing minimums to check convergence of the solution
264  const int min_vec_num = 20;
265  double minimum_vec[min_vec_num];
266  for ( int idx=0; idx<min_vec_num; idx++) {
267  minimum_vec[idx] = 0;
268  }
269 
270  // setting the initial value for the current minimum
271  current_minimum = std::numeric_limits<double>::max();
272 
273  // store the gates
274  std::vector<Gate*> gates_loc = gates;
275 
276 
277 
278  // store the number of parameters
279  int parameter_num_loc = parameter_num;
280 
281  // store the initial unitary to be decomposed
282  Matrix Umtx_loc = Umtx;
283 
284  // storing the initial computational parameters
285  int optimization_block_loc = optimization_block;
286 
287  if ( optimization_block == -1 ) {
288  optimization_block = gates.size();
289  }
290 
291  // random generator of real numbers
292  std::uniform_real_distribution<> distrib_real(0.0, 2*M_PI);
293 
294  // the array storing the optimized parameters
295  Matrix_real optimized_parameters(1, parameter_num_loc);
296 
297  // preparing solution guess for the iterations
298  if ( initial_guess == ZEROS ) {
299  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
300  optimized_parameters[idx] = 0;
301  }
302  }
303  else if ( initial_guess == RANDOM ) {
304  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
305  optimized_parameters[idx] = distrib_real(gen);
306  }
307 
308 #ifdef __MPI__
309  MPI_Bcast( (void*)optimized_parameters.get_data(), parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
310 #endif
311 
312 
313  }
314  else if ( initial_guess == CLOSE_TO_ZERO ) {
315  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
316  optimized_parameters[idx] = distrib_real(gen)/100;
317  }
318 
319 #ifdef __MPI__
320  MPI_Bcast( (void*)optimized_parameters.get_data(), parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
321 #endif
322 
323  }
324  else {
325  std::string err("bad value for initial guess");
326  }
327 
328  if ( solution_guess_num > 0) {
329  memcpy(optimized_parameters.get_data() + parameter_num-solution_guess_num, solution_guess, solution_guess_num*sizeof(double));
330  }
331 
332 
333  // starting number of gate block applied prior to the optimalized gate blocks
334  int pre_gate_parameter_num = 0;
335 
336  // defining temporary variables for iteration cycles
337  int block_idx_end;
338  int block_idx_start = 0;//gates.size();
339  gates.clear();
340  int block_parameter_num;
341  Gate* fixed_gate_post = new Gate( qbit_num );
342  std::vector<Matrix, tbb::cache_aligned_allocator<Matrix>> gates_mtxs_post;
343 
344  // the identity matrix used in the calculations
345  Matrix Identity = create_identity( matrix_size );
346 
347 
348 
349 
350 
351  // maximal number of outer iterations overriden by config
352  long long max_outer_iterations_loc;
353  if ( config.count("max_outer_iterations") > 0 ) {
354  config["max_outer_iterations"].get_property( max_outer_iterations_loc );
355 
356  }
357  else {
358  max_outer_iterations_loc =max_outer_iterations;
359  }
360 
361 
362  //measure the time for the decomposition
363  tbb::tick_count start_time = tbb::tick_count::now();
364 
365 
367  // Start the iterations
368  long long iter_idx;
369  for ( iter_idx=0; iter_idx<max_outer_iterations_loc; iter_idx++) {
370 
371  //determine the range of blocks to be optimalized togedther
372  block_idx_end = block_idx_start + optimization_block;
373  if (block_idx_end > gates_loc.size()) {
374  block_idx_end = gates_loc.size();
375  }
376 
377  // determine the number of free parameters to be optimized
378  block_parameter_num = 0;
379  for ( int block_idx=block_idx_start; block_idx < block_idx_end; block_idx++) {
380  block_parameter_num = block_parameter_num + gates_loc[block_idx]->get_parameter_num();
381  }
382 
383  // ***** get applied the fixed gates applied before the optimized gates *****
384  if (block_idx_start > 0 ) {
385 
386 
387  std::vector<Gate*> gates_save = gates;
388  gates.clear();
389  gates.reserve( gates_save.size()-1 );
390  for( std::vector<Gate*>::iterator gate_it = gates_save.begin(); gate_it != gates_save.end()-1; gate_it++ ) {
391  gates.push_back( *gate_it );
392  }
395 
396  gates = gates_save;
397  }
398  else {
399  Umtx = Umtx_loc.copy();
400  }
401 
402 
403  // clear the gate list used in the previous iterations
404  gates.clear();
405 
406  if (optimized_parameters_mtx.size() > 0 ) {
408  }
409 
410 
411 
412 
413  // create a list of gates for the optimization process
414  for ( int idx=block_idx_start; idx<block_idx_end; idx++ ) {
415  gates.push_back( gates_loc[idx] );
416  }
417 
418  // Create a general gate describing the cumulative effect of gates following the optimized gates
419  if (block_idx_end < gates_loc.size()) {
420 
421  int parameter_idx = 0;
422  for (int gate_idx=0; gate_idx<block_idx_end; gate_idx++) {
423  Gate* gate = gates_loc[gate_idx];
424  parameter_idx = parameter_idx + gate->get_parameter_num();
425  }
426 
427  Matrix_real optimized_parameters_partial = Matrix_real( optimized_parameters.get_data()+parameter_idx, 1, optimized_parameters.size()-parameter_idx );
428 
429  std::vector<Gate*> gates_save = gates;
430  gates.clear();
431  gates.reserve( gates_loc.size()-block_idx_end );
432  for( std::vector<Gate*>::iterator gate_it = gates_loc.begin() + block_idx_end; gate_it != gates_loc.end(); gate_it++ ) {
433  gates.push_back( *gate_it );
434  }
436 
437  Matrix post_mtx = Identity.copy();
438  apply_to( optimized_parameters_partial, post_mtx );
439 
440  gates = gates_save;
441 
442  fixed_gate_post->set_matrix( post_mtx );
443 
444  gates.push_back( fixed_gate_post );
446  }
447  else {
448  // release gate products
449  //gates_mtxs_post.clear();
450  fixed_gate_post->set_matrix( Identity );
451  }
452 
453 
454 
455  // constructing solution guess for the optimization
456  parameter_num = block_parameter_num;
457  Matrix_real solution_guess_tmp = Matrix_real(1, parameter_num);
458  memcpy( solution_guess_tmp.get_data(), optimized_parameters.get_data() + pre_gate_parameter_num, parameter_num*sizeof(double) );
459 
460 
461  // solve the optimization problem of the block
462  solve_layer_optimization_problem( parameter_num, solution_guess_tmp );
463 
464  // add the current minimum to the array of minimums and calculate the mean
465  double minvec_mean = 0;
466  for (int idx=min_vec_num-1; idx>0; idx--) {
467  minimum_vec[idx] = minimum_vec[idx-1];
468  minvec_mean = minvec_mean + minimum_vec[idx-1];
469  }
470  minimum_vec[0] = current_minimum;
471  minvec_mean = minvec_mean + current_minimum;
472  minvec_mean = minvec_mean/min_vec_num;
473 
474 
475 
476  // store the obtained optimalized parameters for the block
477  memcpy( optimized_parameters.get_data()+pre_gate_parameter_num, optimized_parameters_mtx.get_data(), parameter_num*sizeof(double) );
478 
479 
480  if (block_idx_end == gates_loc.size()) {
481  // restart the block-wise iteration again
482  block_idx_start = 0;
483  pre_gate_parameter_num = 0;
484  }
485  else {
486  // mode the block-wies optimization to the next block
487  block_idx_start = block_idx_start + optimization_block;
488  pre_gate_parameter_num = pre_gate_parameter_num + block_parameter_num;
489  }
490 
491 
492  // optimization result is displayed in each 500th iteration
493  if (iter_idx % 500 == 0) {
494  tbb::tick_count current_time = tbb::tick_count::now();
495  std::stringstream sstream;
496  sstream << "The minimum with " << layer_num << " layers after " << iter_idx << " outer iterations is " << current_minimum << " calculated in " << (current_time - start_time).seconds() << " seconds" << std::endl;
497  print(sstream, 2);
498  start_time = tbb::tick_count::now();
499  }
500 
501 
502  // calculate the variance of the last 10 minimums
503  double minvec_std = 0.0;
504  for ( int kdx=0; kdx<min_vec_num; kdx++ ) {
505  double tmp = minimum_vec[kdx] - minvec_mean;
506  minvec_std += tmp*tmp;
507  }
508  minvec_std = sqrt(minvec_std/(min_vec_num-1));
509 
510  // conditions to break the iteration cycles
511  if (std::abs(minvec_std/minimum_vec[min_vec_num-1]) < convergence_threshold ) {
512  std::stringstream sstream;
513  sstream << "The iterations converged to minimum " << current_minimum << " after " << iter_idx << " outer iterations with " << layer_num << " layers" << std::endl;
514  print(sstream, 1);
515  break;
516  }
517  else if (check_optimization_solution()) {
518  std::stringstream sstream;
519  sstream << "The minimum with " << layer_num << " layers after " << iter_idx << " outer iterations is " << current_minimum << std::endl;
520  print(sstream, 1);
521  break;
522  }
523 
524  }
525 
526 
527  if (iter_idx == max_outer_iterations_loc && max_outer_iterations_loc>1) {
528  std::stringstream sstream;
529  sstream << "Reached maximal number of outer iterations" << std::endl << std::endl;
530  print(sstream, 1);
531  }
532 
533  // restoring the parameters to originals
534  optimization_block = optimization_block_loc;
535 
536  // store the result of the optimization
537  gates.clear();
538  gates = gates_loc;
540 
541  parameter_num = parameter_num_loc;
542 
543 
545 
546  memcpy( optimized_parameters_mtx.get_data(), optimized_parameters.get_data(), parameter_num*sizeof(double) );
547 
548  delete(fixed_gate_post);
549 
550  // restore the original unitary
551  Umtx = Umtx_loc; // copy?
552 
553 
554 }
555 
556 
557 
558 
565  return;
566 }
567 
568 
569 
570 
576  return current_minimum;
577 }
578 
579 
580 
581 
582 
588 
589  double optimization_tolerance_loc;
590  if ( config.count("optimization_tolerance") > 0 ) {
591  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
592  }
593  else {
594  optimization_tolerance_loc = optimization_tolerance;
595  }
596 
597  return (std::abs(current_minimum - global_target_minimum) < optimization_tolerance_loc);
598 
599 }
600 
601 
607  return Umtx;
608 }
609 
610 
616  return matrix_size;
617 }
618 
624 
626 
627 }
628 
634  memcpy(ret, optimized_parameters_mtx.get_data(), parameter_num*sizeof(double));
635  return;
636 }
637 
638 
644 
645  if ( num_of_parameters == 0 ) {
646  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
647  return;
648  }
649 
650  if ( parameter_num != num_of_parameters ) {
651  std::string err("Decomposition_Base::set_optimized_parameters: The number of parameters does not match with the free parameters of the circuit.");
652  throw err;
653  }
654 
655  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
656  memcpy( optimized_parameters_mtx.get_data(), parameters, num_of_parameters*sizeof(double) );
657 
658  return;
659 }
660 
661 
667 
668  Matrix ret = Umtx.copy();
670 
671  return ret;
672 }
673 
674 
675 
682 Matrix
683 Decomposition_Base::apply_gate( Matrix& gate_mtx, Matrix& input_matrix ) {
684 
685  // Getting the transformed state upon the transformation given by gate
686  return dot( gate_mtx, input_matrix );
687 
688 }
689 
696 int Decomposition_Base::set_max_layer_num( int n, int max_layer_num_in ) {
697 
698  std::map<int,int>::iterator key_it = max_layer_num.find( n );
699 
700  if ( key_it != max_layer_num.end() ) {
701  max_layer_num.erase( key_it );
702  }
703 
704  max_layer_num.insert( std::pair<int, int>(n, max_layer_num_in) );
705 
706  return 0;
707 
708 }
709 
710 
716 int Decomposition_Base::set_max_layer_num( std::map<int, int> max_layer_num_in ) {
717 
718 
719  for ( std::map<int,int>::iterator it = max_layer_num_in.begin(); it!=max_layer_num_in.end(); it++) {
720  set_max_layer_num( it->first, it->second );
721  }
722 
723  return 0;
724 
725 }
726 
727 
732 void Decomposition_Base::reorder_qubits( std::vector<int> qbit_list) {
733 
734  Gates_block::reorder_qubits( qbit_list );
735 
736  // now reorder the unitary to be decomposed
737 
738  // obtain the permutation indices of the matrix rows/cols
739  std::vector<int> perm_indices;
740  perm_indices.reserve(matrix_size);
741 
742  for (int idx=0; idx<matrix_size; idx++) {
743  int row_idx=0;
744 
745  // get the binary representation of idx
746  std::vector<int> bin_rep;
747  bin_rep.reserve(qbit_num);
748  for (int i = 1 << (qbit_num-1); i > 0; i = i / 2) {
749  (idx & i) ? bin_rep.push_back(1) : bin_rep.push_back(0);
750  }
751 
752  // determine the permutation row index
753  for (int jdx=0; jdx<qbit_num; jdx++) {
754  row_idx = row_idx + bin_rep[qbit_num-1-qbit_list[jdx]]*Power_of_2(qbit_num-1-jdx);
755  }
756  perm_indices.push_back(row_idx);
757  }
758 
759 /*
760  for (auto it=qbit_list.begin(); it!=qbit_list.end(); it++) {
761  std::cout << *it;
762  }
763  std::cout << std::endl;
764 
765  for (auto it=perm_indices.begin(); it!=perm_indices.end(); it++) {
766  std::cout << *it << std::endl;
767  }
768 */
769 
770  // reordering the matrix elements
771  Matrix reordered_mtx = Matrix(matrix_size, matrix_size);
772  for (int row_idx = 0; row_idx<matrix_size; row_idx++) {
773  for (int col_idx = 0; col_idx<matrix_size; col_idx++) {
774  int index_reordered = perm_indices[row_idx]*Umtx.rows + perm_indices[col_idx];
775  int index_umtx = row_idx*Umtx.rows + col_idx;
776  reordered_mtx[index_reordered] = Umtx[index_umtx];
777  }
778  }
779 
780  Umtx = reordered_mtx;
781 }
782 
783 
784 
791 int Decomposition_Base::set_iteration_loops( int n, int iteration_loops_in ) {
792 
793  std::map<int,int>::iterator key_it = iteration_loops.find( n );
794 
795  if ( key_it != iteration_loops.end() ) {
796  iteration_loops.erase( key_it );
797  }
798 
799  iteration_loops.insert( std::pair<int, int>(n, iteration_loops_in) );
800 
801  return 0;
802 
803 }
804 
805 
811 int Decomposition_Base::set_iteration_loops( std::map<int, int> iteration_loops_in ) {
812 
813  for ( std::map<int,int>::iterator it=iteration_loops_in.begin(); it!= iteration_loops_in.end(); it++ ) {
814  set_iteration_loops( it->first, it->second );
815  }
816 
817  return 0;
818 
819 }
820 
821 
822 
827 
828  // default layer numbers
829  max_layer_num_def[2] = 3;
830  max_layer_num_def[3] = 14;
831  max_layer_num_def[4] = 60;
832  max_layer_num_def[5] = 240;
833  max_layer_num_def[6] = 1350;
834  max_layer_num_def[7] = 7000;//6180;
835 
836 }
837 
838 
839 
840 
841 
842 
843 
844 
850 
851  optimization_tolerance = tolerance_in;
852  return;
853 }
854 
855 
856 
861 void Decomposition_Base::set_convergence_threshold( double convergence_threshold_in ) {
862 
863  convergence_threshold = convergence_threshold_in;
864  return;
865 }
866 
872 
873  return decomposition_error;
874 
875 }
876 
877 
878 
879 
885 
886  return current_minimum;
887 
888 }
889 
895  return project_name;
896 }
897 
902 void Decomposition_Base::set_project_name(std::string& project_name_new){
903  project_name = project_name_new;
904  return;
905 }
906 
907 
913  global_phase_factor = mult(global_phase_factor, global_phase_factor_new);
914  return;
915 }
916 
922  return global_phase_factor;
923 }
924 
929 void Decomposition_Base::set_global_phase(double new_global_phase){
930  global_phase_factor.real = sqrt(2)*cos(new_global_phase);
931  global_phase_factor.imag = sqrt(2)*sin(new_global_phase);
932  return;
933 }
934 
940  mult(global_phase_factor, u3_gate);
941  return;
942 }
943 
950  set_global_phase(0);
951  return;
952 }
953 
954 
960  FILE* pFile;
961  if (project_name != ""){filename = project_name + "_" + filename;}
962 
963  const char* c_filename = filename.c_str();
964  pFile = fopen(c_filename, "wb");
965  if (pFile==NULL) {
966  fputs ("File error",stderr);
967  std::string error("Cannot open file.");
968  throw error;
969  }
970 
971 
972  fwrite(&Umtx.rows, sizeof(int), 1, pFile);
973  fwrite(&Umtx.cols, sizeof(int), 1, pFile);
974  fwrite(Umtx.get_data(), sizeof(QGD_Complex16), Umtx.size(), pFile);
975  fclose(pFile);
976  return;
977 }
978 
979 
980 
986  FILE* pFile;
987 
988  if (project_name != ""){filename = project_name + "_" + filename;}
989 
990  const char* c_filename = filename.c_str();
991  int cols;
992  int rows;
993  pFile = fopen(c_filename, "rb");
994  if (pFile==NULL) {
995  fputs ("File error",stderr);
996  exit (1);
997  }
998 
999  size_t fread_status;
1000  fread_status = fread(&rows, sizeof(int), 1, pFile);
1001  fread_status = fread(&cols, sizeof(int), 1, pFile);
1002 
1003  //TODO error handling for fread_status
1004 
1005  Matrix Umtx_ = Matrix(rows, cols);
1006 
1007  fread_status = fread(Umtx_.get_data(), sizeof(QGD_Complex16), rows*cols, pFile);
1008  fclose(pFile);
1009  return Umtx_;
1010 }
1011 
1012 
1018 
1019  int parallel;
1020  if ( config.count("parallel") > 0 ) {
1021  long long value;
1022  config["parallel"].get_property( value );
1023  parallel = (int) value;
1024  }
1025  else {
1026  parallel = 2;
1027  }
1028 
1029 
1030  return parallel;
1031 
1032 }
1033 
1034 
1039 void Decomposition_Base::set_qbit_num( int qbit_num_in ) {
1040 
1041  // check the size of the unitary
1042  int matrix_size_loc = 1 << qbit_num_in;
1043  if ( matrix_size_loc != matrix_size ) {
1044  std::string err("Decomposition_Base::set_qbit_num: The new number of qubits is not in line with the input unitary to be decomposed");
1045  throw err;
1046  }
1047 
1048  // setting the number of qubits
1049  Gates_block::set_qbit_num(qbit_num_in);
1050 
1051 
1052 }
void list_gates(int start_index)
Call to print the gates decomposing the initial unitary.
Matrix dot(Matrix &A, Matrix &B)
Call to calculate the product of two complex matrices by calling method zgemm3m from the CBLAS librar...
Definition: dot.cpp:38
Gate()
Default constructor of the class.
Definition: Gate.cpp:39
std::string get_project_name()
Call to get the current name of the project.
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 get_decomposed_matrix()
Calculate the decomposed matrix resulted by the effect of the optimized gates on the unitary Umtx...
void set_project_name(std::string &project_name_new)
Call to set the name of the project.
bool decomposition_finalized
The optimized parameters for the gates.
Matrix get_Umtx()
Call to retrive a pointer to the unitary to be transformed.
Matrix_real copy() const
Call to create a copy of the matrix.
double current_minimum
The current minimum of the optimization problem.
bool optimization_problem_solved
logical value describing whether the optimization problem was solved or not
int finalizing_gates_num
number of finalizing (deterministic) opertaions rotating the disentangled qubits into state |0>...
int set_max_layer_num(int n, int max_layer_num_in)
Set the maximal number of layers used in the subdecomposition of the n-th qubit.
double get_current_minimum()
Call to get the obtained minimum of the cost function.
virtual void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the gates (Obsolete function)
int layer_num
number of gate layers
Definition: Gates_block.h:48
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...
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
Definition: common.cpp:259
virtual void apply_to(Matrix_real &parameters_mtx, Matrix &input, int parallel=0)
Call to apply the gate on the input array/matrix Gates_block*input.
int matrix_size
The size N of the NxN matrix associated with the operations.
Definition: Gate.h:89
Matrix apply_gate(Matrix &gate_mtx, Matrix &input_matrix)
Apply an gates on the input matrix.
scalar * get_data() const
Call to get the pointer to the stored data.
guess_type initial_guess
type to guess the initial values for the optimization. Possible values: ZEROS=0, RANDOM=1, CLOSE_TO_ZERO=2
static void Init_max_layer_num()
Initializes default layer numbers.
void apply_global_phase_factor()
Call to apply the current global phase to the unitary matrix.
std::vector< Gate * > gates
The list of stored gates.
Definition: Gates_block.h:46
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.
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...
gate_type type
The type of the operation (see enumeration gate_type)
Definition: Gate.h:83
void set_global_phase(double new_global_phase)
Call to set global phase.
QGD_Complex16 get_global_phase_factor()
Get the global phase of the Unitary matrix.
int rows
The number of rows.
Definition: matrix_base.hpp:42
int cols
The number of columns.
Definition: matrix_base.hpp:44
double get_decomposition_error()
Call to get the error of the decomposition.
int get_Umtx_size()
Call to get the size of the unitary to be transformed.
double convergence_threshold
The convergence threshold in the optimization process.
Matrix_real get_optimized_parameters()
Call to get the optimized parameters.
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 set_optimized_parameters(double *parameters, int num_of_parameters)
Call to set the optimized parameters for initial optimization.
int num_threads
Store the number of OpenMP threads. (During the calculations OpenMP multithreading is turned off...
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
void set_optimization_tolerance(double tolerance_in)
Call to set the tolerance of the optimization processes.
void set_matrix(Matrix input)
Call to set the stored matrix in the operation.
Definition: Gate.cpp:299
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:117
Class to store data of complex arrays and its properties.
Definition: matrix.h:38
Header file for a class containing basic methods for the decomposition process.
virtual ~Decomposition_Base()
Destructor of the class.
QGD_Complex16 global_phase_factor
The global phase.
int size() const
Call to get the number of the allocated elements.
int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:486
void set_convergence_threshold(double convergence_threshold_in)
Call to set the threshold of convergence in the optimization processes.
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:41
void calculate_new_global_phase_factor(QGD_Complex16 global_phase_factor_new)
Calculate the new global phase of the Unitary matrix after removing a trivial U3 matrix.
void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the gate.
std::string name
A string labeling the gate operation.
Definition: Gate.h:79
guess_type
Type definition of the types of the initial guess.
std::map< std::string, Config_Element > config
config metadata utilized during the optimization
Base class for the representation of general gate operations.
Definition: Gate.h:73
Matrix create_identity(int matrix_size)
Call to create an identity matrix.
Definition: common.cpp:164
virtual void solve_layer_optimization_problem(int num_of_parameters, Matrix_real solution_guess_gsl)
Abstarct function to be used to solve a single sub-layer optimization problem.
int parameter_num
the number of free parameters of the operation
Definition: Gate.h:91
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.
Matrix copy()
Call to create a copy of the matrix.
Definition: matrix.cpp:105
static std::map< int, int > max_layer_num_def
A map of <int n: int num> indicating that how many layers should be used in the subdecomposition proc...
double real
the real part of a complex number
Definition: QGDTypes.h:40
void reset_parameter_start_indices()
Method to reset the parameter start indices of gate operations incorporated in the circuit...
void export_unitary(std::string &filename)
exports unitary matrix to binary file
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:81
int finalizing_parameter_num
the number of the finalizing (deterministic) parameters of gates rotating the disentangled qubits int...
void set_max_iteration(int max_outer_iterations_in)
Call to set the maximal number of the iterations in the optimization process.
bool check_optimization_solution()
check_optimization_solution
int optimization_block
number of gate blocks used in one shot of the optimization process
double decomposition_error
error of the final decomposition
int set_iteration_loops(int n, int iteration_loops_in)
Set the number of iteration loops during the subdecomposition of the n-th qubit.
void list_gates(const Matrix_real &parameters, int start_index)
Call to print the list of gates stored in the block of gates for a specific set of parameters...
Matrix import_unitary_from_binary(std::string &filename)
Import a Unitary matrix from a file.
Matrix_real optimized_parameters_mtx
The optimized parameters for the gates.
int get_parallel_configuration()
Get the parallel configuration from the config.
double global_target_minimum
The global target minimum of the optimization problem.
Header file for the paralleized calculation of the cost function of the subdecomposition (supporting ...
void set_optimization_blocks(int optimization_block_in)
Call to set the number of gate blocks to be optimized in one shot.
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:39
Decomposition_Base()
Nullary constructor of the class.
std::mt19937 gen
Standard mersenne_twister_engine seeded with rd()
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
virtual 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.
virtual double optimization_problem(const double *parameters)
This is an abstact definition of function giving the cost functions measuring the entaglement of the ...