Sequential Quantum Gate Decomposer  v1.9.7
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 namespace {
31 
32 std::mt19937 make_decomposition_generator(
33  std::map<std::string, Config_Element>* config = nullptr) {
34 
35  if (config != nullptr && config->count("random_seed") > 0) {
36  long long seed;
37  (*config)["random_seed"].get_property(seed);
38  return std::mt19937(static_cast<std::mt19937::result_type>(seed));
39  }
40 
41  return std::mt19937(std::random_device{}());
42 }
43 
44 }
45 
46 
51 
52 
53  // A string labeling the gate operation
54  name = "Decomposition_Interface";
55 
57 
58 
59  // logical value describing whether the decomposition was finalized or not
61 
62  // A string describing the type of the class
64 
65  // error of the unitarity of the final decomposition
66  decomposition_error = DBL_MAX;
67 
68  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
70 
71  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
73 
74  // The current minimum of the optimization problem
75  current_minimum = std::numeric_limits<double>::max();
76 
77  // The global minimum of the optimization problem
79 
80  // logical value describing whether the optimization problem was solved or not
82 
83  // number of iteratrion loops in the finale optimization
84  //iteration_loops = dict()
85 
86  // The maximal allowed error of the optimization problem
88 
89  // Maximal number of iteartions in the optimization process
91 
92  // number of operators in one sub-layer of the optimization process
93  optimization_block = -1;
94 
95  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
97 
98  // The convergence threshold in the optimization process
99  convergence_threshold = 1e-5;
100 
101  use_float = false;
102 
103  //global phase of the unitary matrix
106 
107  //the name of the SQUANDER project
108  std::string projectname = "";
109 
110 
111  gen = make_decomposition_generator();
112 
113 #if CBLAS==1
114  num_threads = mkl_get_max_threads();
115 #elif CBLAS==2
116  num_threads = openblas_get_num_threads();
117 #endif
118 
119 
120 
121 }
122 
123 
131 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) {
132 
133  // A string labeling the gate operation
134  name = "Decomposition_Interface";
135 
137 
138 
139  // the unitary operator to be decomposed
140  Umtx = Umtx_in;
141 
142  // logical value describing whether the decomposition was finalized or not
143  decomposition_finalized = false;
144 
145  // A string describing the type of the class
147 
148  // error of the unitarity of the final decomposition
149  decomposition_error = DBL_MAX;
150 
151  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
153 
154  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
156 
157  // The current minimum of the optimization problem
158  current_minimum = std::numeric_limits<double>::max();
159 
160  // The global minimum of the optimization problem
162 
163  // logical value describing whether the optimization problem was solved or not
165 
166  // number of iteratrion loops in the finale optimization
167  //iteration_loops = dict()
168 
169  // The maximal allowed error of the optimization problem
170  optimization_tolerance = 1e-7;
171 
172  // Maximal number of iteartions in the optimization process
173  max_outer_iterations = 1e8;
174 
175  // number of operators in one sub-layer of the optimization process
176  optimization_block = -1;
177 
178  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
179  initial_guess = initial_guess_in;
180 
181  // The convergence threshold in the optimization process
182  convergence_threshold = 1e-5;
183 
184  //global phase of the unitary matrix
187 
188  //name of the SQUANDER project
189  std::string projectname = "";
190 
191 
192  // config maps
193  config = config_in;
194 
195  use_float = false;
196  if ( config.count("use_float") > 0 ) {
197  config["use_float"].get_property( use_float );
198  }
199  if ( config.count("convergence_threshold") > 0 ) {
200  config["convergence_threshold"].get_property( convergence_threshold );
201  }
202 
203  if ( use_float ) {
205  }
206 
207  gen = make_decomposition_generator(&config);
208 
209 #if CBLAS==1
210  num_threads = mkl_get_max_threads();
211 #elif CBLAS==2
212  num_threads = openblas_get_num_threads();
213 #endif
214 
215 #ifdef __MPI__
216  // Get the number of processes
217  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
218 
219  // Get the rank of the process
220  MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
221 
222 #endif
223 
224 }
225 
229 Decomposition_Base::Decomposition_Base( Matrix_float Umtx_in, int qbit_num_in, std::map<std::string, Config_Element>& config_in, guess_type initial_guess_in ) : Gates_block(qbit_num_in) {
230 
231  // A string labeling the gate operation
232  name = "Decomposition_Interface";
233 
235 
236  // Keep the single precision input as the active unitary for the hot path.
237  Umtx_float = Umtx_in;
238  Umtx = Umtx_in.to_float64();
239 
240  // logical value describing whether the decomposition was finalized or not
241  decomposition_finalized = false;
242 
243  // A string describing the type of the class
245 
246  // error of the unitarity of the final decomposition
247  decomposition_error = DBL_MAX;
248 
249  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
251 
252  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
254 
255  // The current minimum of the optimization problem
256  current_minimum = std::numeric_limits<double>::max();
257 
258  // The global minimum of the optimization problem
260 
261  // logical value describing whether the optimization problem was solved or not
263 
264  // The maximal allowed error of the optimization problem
265  optimization_tolerance = 1e-7;
266 
267  // Maximal number of iteartions in the optimization process
268  max_outer_iterations = 1e8;
269 
270  // number of operators in one sub-layer of the optimization process
271  optimization_block = -1;
272 
273  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
274  initial_guess = initial_guess_in;
275 
276  // The convergence threshold in the optimization process
277  convergence_threshold = 1e-5;
278 
279  //global phase of the unitary matrix
282 
283  //name of the SQUANDER project
284  std::string projectname = "";
285 
286  // config maps
287  config = config_in;
288  use_float = true;
289  if ( config.count("convergence_threshold") > 0 ) {
290  config["convergence_threshold"].get_property( convergence_threshold );
291  }
292 
293  gen = make_decomposition_generator(&config);
294 
295 #if CBLAS==1
296  num_threads = mkl_get_max_threads();
297 #elif CBLAS==2
298  num_threads = openblas_get_num_threads();
299 #endif
300 
301 #ifdef __MPI__
302  // Get the number of processes
303  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
304 
305  // Get the rank of the process
306  MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
307 
308 #endif
309 
310 }
311 
316 /*
317  if (optimized_parameters != NULL ) {
318  qgd_free( optimized_parameters );
319  optimized_parameters = NULL;
320  }
321 */
322 }
323 
325 
326  if ( !use_float ) {
327  return;
328  }
329 
331  for (int pidx=0; pidx<optimized_parameters_mtx.size(); pidx++) {
332  optimized_parameters_mtx_float[pidx] = static_cast<float>(optimized_parameters_mtx[pidx]);
333  }
334 }
335 
336 
341 void Decomposition_Base::set_optimization_blocks( int optimization_block_in) {
342  optimization_block = optimization_block_in;
343 }
344 
349 void Decomposition_Base::set_max_iteration( int max_outer_iterations_in) {
350  max_outer_iterations = max_outer_iterations_in;
351 }
352 
353 
354 
355 
360 void Decomposition_Base::list_gates( int start_index ) {
361 
363 
364 }
365 
366 
367 
368 
369 
375 void Decomposition_Base::solve_optimization_problem( double* solution_guess, int solution_guess_num ) {
376 
377 
378  if ( gates.size() == 0 ) {
379  return;
380  }
381 
382  // array containing minimums to check convergence of the solution
383  const int min_vec_num = 20;
384  double minimum_vec[min_vec_num];
385  for ( int idx=0; idx<min_vec_num; idx++) {
386  minimum_vec[idx] = 0;
387  }
388 
389  // setting the initial value for the current minimum
390  current_minimum = std::numeric_limits<double>::max();
391 
392  // store the gates
393  std::vector<Gate*> gates_loc = gates;
394 
395 
396 
397  // store the number of parameters
398  int parameter_num_loc = parameter_num;
399 
400  // store the initial unitary to be decomposed
401  Matrix Umtx_loc = Umtx;
402  Matrix_float Umtx_float_loc;
403  if ( use_float ) {
404  Umtx_float_loc = Umtx_float.copy();
405  }
406 
407  // storing the initial computational parameters
408  int optimization_block_loc = optimization_block;
409 
410  if ( optimization_block == -1 ) {
411  optimization_block = gates.size();
412  }
413 
414  // random generator of real numbers
415  std::uniform_real_distribution<> distrib_real(0.0, 2*M_PI);
416 
417  // the array storing the optimized parameters
418  Matrix_real optimized_parameters(1, parameter_num_loc);
419 
420  // preparing solution guess for the iterations
421  if ( initial_guess == ZEROS ) {
422  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
423  optimized_parameters[idx] = 0;
424  }
425  }
426  else if ( initial_guess == RANDOM ) {
427  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
428  optimized_parameters[idx] = distrib_real(gen);
429  }
430 
431 #ifdef __MPI__
432  MPI_Bcast( (void*)optimized_parameters.get_data(), parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
433 #endif
434 
435 
436  }
437  else if ( initial_guess == CLOSE_TO_ZERO ) {
438  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
439  optimized_parameters[idx] = distrib_real(gen)/100;
440  }
441 
442 #ifdef __MPI__
443  MPI_Bcast( (void*)optimized_parameters.get_data(), parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
444 #endif
445 
446  }
447  else {
448  std::string err("bad value for initial guess");
449  }
450 
451  if ( solution_guess_num > 0) {
452  memcpy(optimized_parameters.get_data() + parameter_num-solution_guess_num, solution_guess, solution_guess_num*sizeof(double));
453  }
454 
455 
456  // starting number of gate block applied prior to the optimalized gate blocks
457  int pre_gate_parameter_num = 0;
458 
459  // defining temporary variables for iteration cycles
460  size_t block_idx_end;
461  size_t block_idx_start = 0;//gates.size();
462  gates.clear();
463  int block_parameter_num;
464  Gate* fixed_gate_post = new Gate( qbit_num );
465  std::vector<Matrix, tbb::cache_aligned_allocator<Matrix>> gates_mtxs_post;
466 
467  // the identity matrix used in the calculations
468  Matrix Identity = create_identity( matrix_size );
469 
470 
471 
472 
473 
474  // maximal number of outer iterations overriden by config
475  long long max_outer_iterations_loc;
476  if ( config.count("max_outer_iterations") > 0 ) {
477  config["max_outer_iterations"].get_property( max_outer_iterations_loc );
478 
479  }
480  else {
481  max_outer_iterations_loc =max_outer_iterations;
482  }
483 
484 
485  //measure the time for the decomposition
486  tbb::tick_count start_time = tbb::tick_count::now();
487 
488 
490  // Start the iterations
491  long long iter_idx;
492  for ( iter_idx=0; iter_idx<max_outer_iterations_loc; iter_idx++) {
493 
494  //determine the range of blocks to be optimalized togedther
495  block_idx_end = block_idx_start + optimization_block;
496  if (block_idx_end > gates_loc.size()) {
497  block_idx_end = gates_loc.size();
498  }
499 
500  // determine the number of free parameters to be optimized
501  block_parameter_num = 0;
502  for ( size_t block_idx=block_idx_start; block_idx < block_idx_end; block_idx++) {
503  block_parameter_num = block_parameter_num + gates_loc[block_idx]->get_parameter_num();
504  }
505 
506  // ***** get applied the fixed gates applied before the optimized gates *****
507  if (block_idx_start > 0 ) {
508 
509 
510  std::vector<Gate*> gates_save = gates;
511  gates.clear();
512  gates.reserve( gates_save.size()-1 );
513  for( std::vector<Gate*>::iterator gate_it = gates_save.begin(); gate_it != gates_save.end()-1; gate_it++ ) {
514  gates.push_back( *gate_it );
515  }
517  if ( use_float ) {
518  Matrix_real_float optimized_parameters_float(1, optimized_parameters_mtx.size());
519  for (int pidx=0; pidx<optimized_parameters_mtx.size(); pidx++) {
520  optimized_parameters_float[pidx] = static_cast<float>(optimized_parameters_mtx[pidx]);
521  }
522  Gates_block::apply_to( optimized_parameters_float, Umtx_float );
523  }
524  else {
526  }
527 
528  gates = gates_save;
529  }
530  else {
531  Umtx = Umtx_loc.copy();
532  if ( use_float ) {
533  Umtx_float = Umtx_float_loc.copy();
534  }
535  }
536 
537 
538  // clear the gate list used in the previous iterations
539  gates.clear();
540 
541  if (optimized_parameters_mtx.size() > 0 ) {
543  }
544 
545 
546 
547 
548  // create a list of gates for the optimization process
549  for ( size_t idx=block_idx_start; idx<block_idx_end; idx++ ) {
550  gates.push_back( gates_loc[idx] );
551  }
552 
553  // Create a general gate describing the cumulative effect of gates following the optimized gates
554  if (block_idx_end < gates_loc.size()) {
555 
556  int parameter_idx = 0;
557  for (size_t gate_idx=0; gate_idx<block_idx_end; gate_idx++) {
558  Gate* gate = gates_loc[gate_idx];
559  parameter_idx = parameter_idx + gate->get_parameter_num();
560  }
561 
562  Matrix_real optimized_parameters_partial = Matrix_real( optimized_parameters.get_data()+parameter_idx, 1, optimized_parameters.size()-parameter_idx );
563 
564  std::vector<Gate*> gates_save = gates;
565  gates.clear();
566  gates.reserve( gates_loc.size()-block_idx_end );
567  for( std::vector<Gate*>::iterator gate_it = gates_loc.begin() + block_idx_end; gate_it != gates_loc.end(); gate_it++ ) {
568  gates.push_back( *gate_it );
569  }
571 
572  if ( use_float ) {
573  Matrix_real_float optimized_parameters_partial_float(1, optimized_parameters_partial.size());
574  for (int pidx=0; pidx<optimized_parameters_partial.size(); pidx++) {
575  optimized_parameters_partial_float[pidx] = static_cast<float>(optimized_parameters_partial[pidx]);
576  }
577  Matrix_float post_mtx_float = create_identity_float( matrix_size );
578  Gates_block::apply_to( optimized_parameters_partial_float, post_mtx_float );
579 
580  gates = gates_save;
581 
582  fixed_gate_post->set_matrix( post_mtx_float.to_float64() );
583  }
584  else {
585  Matrix post_mtx = Identity.copy();
586  apply_to( optimized_parameters_partial, post_mtx );
587 
588  gates = gates_save;
589 
590  fixed_gate_post->set_matrix( post_mtx );
591  }
592 
593  gates.push_back( fixed_gate_post );
595  }
596  else {
597  // release gate products
598  //gates_mtxs_post.clear();
599  fixed_gate_post->set_matrix( Identity );
600  }
601 
602 
603 
604  // constructing solution guess for the optimization
605  parameter_num = block_parameter_num;
606  Matrix_real solution_guess_tmp = Matrix_real(1, parameter_num);
607  memcpy( solution_guess_tmp.get_data(), optimized_parameters.get_data() + pre_gate_parameter_num, parameter_num*sizeof(double) );
608 
609 
610  // solve the optimization problem of the block
611  solve_layer_optimization_problem( parameter_num, solution_guess_tmp );
612 
614 
615  // add the current minimum to the array of minimums and calculate the mean
616  double minvec_mean = 0;
617  for (int idx=min_vec_num-1; idx>0; idx--) {
618  minimum_vec[idx] = minimum_vec[idx-1];
619  minvec_mean = minvec_mean + minimum_vec[idx-1];
620  }
621  minimum_vec[0] = current_minimum;
622  minvec_mean = minvec_mean + current_minimum;
623  minvec_mean = minvec_mean/min_vec_num;
624 
625 
626 
627  // store the obtained optimalized parameters for the block
628  memcpy( optimized_parameters.get_data()+pre_gate_parameter_num, optimized_parameters_mtx.get_data(), parameter_num*sizeof(double) );
629 
630 
631  if (block_idx_end == gates_loc.size()) {
632  // restart the block-wise iteration again
633  block_idx_start = 0;
634  pre_gate_parameter_num = 0;
635  }
636  else {
637  // mode the block-wies optimization to the next block
638  block_idx_start = block_idx_start + optimization_block;
639  pre_gate_parameter_num = pre_gate_parameter_num + block_parameter_num;
640  }
641 
642 
643  // optimization result is displayed in each 500th iteration
644  if (iter_idx % 500 == 0) {
645  tbb::tick_count current_time = tbb::tick_count::now();
646  std::stringstream sstream;
647  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;
648  print(sstream, 2);
649  start_time = tbb::tick_count::now();
650  }
651 
652 
653  // calculate the variance of the last 10 minimums
654  double minvec_std = 0.0;
655  for ( int kdx=0; kdx<min_vec_num; kdx++ ) {
656  double tmp = minimum_vec[kdx] - minvec_mean;
657  minvec_std += tmp*tmp;
658  }
659  minvec_std = sqrt(minvec_std/(min_vec_num-1));
660 
661  // conditions to break the iteration cycles
662  if (std::abs(minvec_std/minimum_vec[min_vec_num-1]) < convergence_threshold ) {
663  std::stringstream sstream;
664  sstream << "The iterations converged to minimum " << current_minimum << " after " << iter_idx << " outer iterations with " << layer_num << " layers" << std::endl;
665  print(sstream, 1);
666  break;
667  }
668  else if (check_optimization_solution()) {
669  std::stringstream sstream;
670  sstream << "The minimum with " << layer_num << " layers after " << iter_idx << " outer iterations is " << current_minimum << std::endl;
671  print(sstream, 1);
672  break;
673  }
674 
675  }
676 
677 
678  if (iter_idx == max_outer_iterations_loc && max_outer_iterations_loc>1) {
679  std::stringstream sstream;
680  sstream << "Reached maximal number of outer iterations" << std::endl << std::endl;
681  print(sstream, 1);
682  }
683 
684  // restoring the parameters to originals
685  optimization_block = optimization_block_loc;
686 
687  // store the result of the optimization
688  gates.clear();
689  gates = gates_loc;
691 
692  parameter_num = parameter_num_loc;
693 
694 
696 
697  memcpy( optimized_parameters_mtx.get_data(), optimized_parameters.get_data(), parameter_num*sizeof(double) );
699 
700  delete(fixed_gate_post);
701 
702  // restore the original unitary
703  Umtx = Umtx_loc; // copy?
704  if ( use_float ) {
705  Umtx_float = Umtx_float_loc;
706  }
707 
708 
709 }
710 
711 
712 
713 
720  return;
721 }
722 
723 
724 
725 
731  return current_minimum;
732 }
733 
734 
735 
736 
737 
743 
744  double optimization_tolerance_loc;
745  if ( config.count("optimization_tolerance") > 0 ) {
746  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
747  }
748  else {
749  optimization_tolerance_loc = optimization_tolerance;
750  }
751 
752  return (std::abs(current_minimum - global_target_minimum) < optimization_tolerance_loc);
753 
754 }
755 
756 
762  return Umtx;
763 }
764 
766  return Umtx_float;
767 }
768 
770  return use_float;
771 }
772 
773 
779  return matrix_size;
780 }
781 
787 
789 
790 }
791 
793 
796 
797 }
798 
804  memcpy(ret, optimized_parameters_mtx.get_data(), parameter_num*sizeof(double));
805  return;
806 }
807 
808 
814 
815  if ( num_of_parameters == 0 ) {
816  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
818  return;
819  }
820 
821  if ( parameter_num != num_of_parameters ) {
822  std::string err("Decomposition_Base::set_optimized_parameters: The number of parameters does not match with the free parameters of the circuit.");
823  throw err;
824  }
825 
826  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
827  memcpy( optimized_parameters_mtx.get_data(), parameters, num_of_parameters*sizeof(double) );
829 
830  return;
831 }
832 
833 
839  if ( use_float ) {
841  Matrix_float ret = Umtx_float.copy();
843  return ret.to_float64();
844  }
845 
846  Matrix ret = Umtx.copy();
848 
849  return ret;
850 }
851 
852 
853 
860 Matrix
861 Decomposition_Base::apply_gate( Matrix& gate_mtx, Matrix& input_matrix ) {
862 
863  // Getting the transformed state upon the transformation given by gate
864  return dot( gate_mtx, input_matrix );
865 
866 }
867 
874 int Decomposition_Base::set_max_layer_num( int n, int max_layer_num_in ) {
875 
876  std::map<int,int>::iterator key_it = max_layer_num.find( n );
877 
878  if ( key_it != max_layer_num.end() ) {
879  max_layer_num.erase( key_it );
880  }
881 
882  max_layer_num.insert( std::pair<int, int>(n, max_layer_num_in) );
883 
884  return 0;
885 
886 }
887 
888 
894 int Decomposition_Base::set_max_layer_num( std::map<int, int> max_layer_num_in ) {
895 
896 
897  for ( std::map<int,int>::iterator it = max_layer_num_in.begin(); it!=max_layer_num_in.end(); it++) {
898  set_max_layer_num( it->first, it->second );
899  }
900 
901  return 0;
902 
903 }
904 
905 
910 void Decomposition_Base::reorder_qubits( std::vector<int> qbit_list) {
911 
912 
913  Gates_block::reorder_qubits( qbit_list);
914 
915  // now reorder the unitary to be decomposed
916 
917  // obtain the permutation indices of the matrix rows/cols
918  std::vector<int> perm_indices;
919  perm_indices.reserve(matrix_size);
920 
921  for (int idx=0; idx<matrix_size; idx++) {
922 
923  int row_idx=0;
924 
925  // contruct the index corresponding to the premuted qubits
926  for( int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++ ) {
927 
928  // state of qubit qbit_list[qbit_idx] in the index idx
929  int qbit_state = ( idx >> qbit_list[qbit_idx] ) & 1;
930 
931  row_idx = row_idx + qbit_state*Power_of_2(qbit_idx);
932  }
933 
934  perm_indices.push_back(row_idx);
935 
936  }
937 
938  // reordering the matrix elements
939  Matrix reordered_mtx = Matrix(matrix_size, matrix_size);
940  for (int row_idx = 0; row_idx<matrix_size; row_idx++) {
941  for (int col_idx = 0; col_idx<matrix_size; col_idx++) {
942  int index_reordered = perm_indices[row_idx]*Umtx.rows + perm_indices[col_idx];
943  int index_umtx = row_idx*Umtx.rows + col_idx;
944  reordered_mtx[index_reordered] = Umtx[index_umtx];
945  }
946  }
947 
948  Umtx = reordered_mtx;
949 
950 }
951 
952 
953 
960 int Decomposition_Base::set_iteration_loops( int n, int iteration_loops_in ) {
961 
962  std::map<int,int>::iterator key_it = iteration_loops.find( n );
963 
964  if ( key_it != iteration_loops.end() ) {
965  iteration_loops.erase( key_it );
966  }
967 
968  iteration_loops.insert( std::pair<int, int>(n, iteration_loops_in) );
969 
970  return 0;
971 
972 }
973 
974 
980 int Decomposition_Base::set_iteration_loops( std::map<int, int> iteration_loops_in ) {
981 
982  for ( std::map<int,int>::iterator it=iteration_loops_in.begin(); it!= iteration_loops_in.end(); it++ ) {
983  set_iteration_loops( it->first, it->second );
984  }
985 
986  return 0;
987 
988 }
989 
990 
991 
996 
997  // default layer numbers
998  max_layer_num_def[2] = 3;
999  max_layer_num_def[3] = 14;
1000  max_layer_num_def[4] = 60;
1001  max_layer_num_def[5] = 240;
1002  max_layer_num_def[6] = 1350;
1003  max_layer_num_def[7] = 7000;//6180;
1004 
1005 }
1006 
1007 
1008 
1009 
1010 
1011 
1012 
1013 
1019 
1020  optimization_tolerance = tolerance_in;
1021  return;
1022 }
1023 
1024 
1025 
1030 void Decomposition_Base::set_convergence_threshold( double convergence_threshold_in ) {
1031 
1032  convergence_threshold = convergence_threshold_in;
1033  return;
1034 }
1035 
1041 
1042  return decomposition_error;
1043 
1044 }
1045 
1046 
1047 
1048 
1054 
1055  return current_minimum;
1056 
1057 }
1058 
1064  return project_name;
1065 }
1066 
1071 void Decomposition_Base::set_project_name(std::string& project_name_new){
1072  project_name = project_name_new;
1073  return;
1074 }
1075 
1076 
1082  global_phase_factor = mult(global_phase_factor, global_phase_factor_new);
1083  return;
1084 }
1085 
1091  return global_phase_factor;
1092 }
1093 
1098 void Decomposition_Base::set_global_phase(double new_global_phase){
1099  global_phase_factor.real = sqrt(2)*cos(new_global_phase);
1100  global_phase_factor.imag = sqrt(2)*sin(new_global_phase);
1101  return;
1102 }
1103 
1109  mult(global_phase_factor, u3_gate);
1110  return;
1111 }
1112 
1119  set_global_phase(0);
1120  return;
1121 }
1122 
1123 
1129  FILE* pFile;
1130  if (project_name != ""){filename = project_name + "_" + filename;}
1131 
1132  const char* c_filename = filename.c_str();
1133  pFile = fopen(c_filename, "wb");
1134  if (pFile==NULL) {
1135  fputs ("File error",stderr);
1136  std::string error("Cannot open file.");
1137  throw error;
1138  }
1139 
1140 
1141  fwrite(&Umtx.rows, sizeof(int), 1, pFile);
1142  fwrite(&Umtx.cols, sizeof(int), 1, pFile);
1143  fwrite(Umtx.get_data(), sizeof(QGD_Complex16), Umtx.size(), pFile);
1144  fclose(pFile);
1145  return;
1146 }
1147 
1148 
1149 
1155  FILE* pFile;
1156 
1157  if (project_name != ""){filename = project_name + "_" + filename;}
1158 
1159  const char* c_filename = filename.c_str();
1160  int cols;
1161  int rows;
1162  pFile = fopen(c_filename, "rb");
1163  if (pFile==NULL) {
1164  fputs ("File error",stderr);
1165  exit (1);
1166  }
1167 
1168  fread_wrapper(&rows, sizeof(int), 1, pFile);
1169  fread_wrapper(&cols, sizeof(int), 1, pFile);
1170 
1171 
1172  Matrix Umtx_ = Matrix(rows, cols);
1173 
1174  fread_wrapper(Umtx_.get_data(), sizeof(QGD_Complex16), rows*cols, pFile);
1175  fclose(pFile);
1176  return Umtx_;
1177 }
1178 
1179 
1185 
1186  int parallel;
1187  if ( config.count("parallel") > 0 ) {
1188  long long value;
1189  config["parallel"].get_property( value );
1190  parallel = (int) value;
1191  }
1192  else {
1193  parallel = 2;
1194  }
1195 
1196 
1197  return parallel;
1198 
1199 }
1200 
1201 
1206 void Decomposition_Base::set_qbit_num( int qbit_num_in ) {
1207 
1208  // check the size of the unitary
1209  int matrix_size_loc = 1 << qbit_num_in;
1210  if ( matrix_size_loc != matrix_size ) {
1211  std::string err("Decomposition_Base::set_qbit_num: The new number of qubits is not in line with the input unitary to be decomposed");
1212  throw err;
1213  }
1214 
1215  // setting the number of qubits
1216  Gates_block::set_qbit_num(qbit_num_in);
1217 
1218 
1219 }
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:121
virtual void reorder_qubits(std::vector< int > qbit_list) override
Call to reorder the qubits in the matrix of the gates (Obsolete function)
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
Class to store single-precision real arrays and properties.
Matrix_float to_float32() const
Convert to single precision.
Definition: matrix.cpp:32
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.
bool optimization_problem_solved
logical value describing whether the optimization problem was solved or not
Matrix to_float64() const
Convert to double precision.
Definition: matrix_float.cpp:8
int finalizing_gates_num
number of finalizing (deterministic) opertaions rotating the disentangled qubits into state |0>...
bool use_float
Selects float32 circuit application for parameter/unitary/state data.
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.
Matrix_real_float optimized_parameters_mtx_float
Float32 optimized parameters used by the hot gate-application path.
double get_current_minimum()
Call to get the obtained minimum of the cost function.
Matrix_real_float get_optimized_parameters_float()
Return optimized parameters in float32.
int layer_num
number of gate layers
Definition: Gates_block.h:51
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:298
virtual void set_qbit_num(int qbit_num_in) override
Set the number of qubits spanning the matrix of the gates stored in the block of gates.
int matrix_size
The size N of the NxN matrix associated with the operations.
Definition: Gate.h:106
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 sync_optimized_parameters_float()
Synchronize the float32 parameter mirror from the double optimizer storage.
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:49
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.
Matrix_real_float copy() const
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:96
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_float Umtx_float
Float32 copy of the unitary used when config["use_float"] is true.
#define M_PI
Definition: qgd_math.h:42
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.
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.
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
Matrix copy() const
Call to create a copy of the matrix.
Definition: matrix.h:57
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:1036
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
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.
dictionary config
int size() const
Call to get the number of the allocated elements.
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
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:44
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:92
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
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:86
Matrix create_identity(int matrix_size)
Call to create an identity matrix.
Definition: common.cpp:182
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:108
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.
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...
Matrix_float get_Umtx_float()
Return the float32 unitary copy used by float execution.
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:94
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 fread_wrapper(void *buffer, size_t size, size_t count, FILE *stream)
Wrapper function aound fread with error handling.
Definition: common.cpp:75
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.
Matrix_float copy() const
Call to create a copy of the matrix.
Definition: matrix_float.h:60
bool get_use_float() const
True when config["use_float"] requests float32 circuit execution.
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:41
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
Matrix_float create_identity_float(int matrix_size)
Call to create a single-precision complex identity matrix.
Definition: common.cpp:203
virtual double optimization_problem(const double *parameters)
This is an abstact definition of function giving the cost functions measuring the entaglement of the ...