Sequential Quantum Gate Decomposer  v1.9.6
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  use_float = false;
86 
87  //global phase of the unitary matrix
90 
91  //the name of the SQUANDER project
92  std::string projectname = "";
93 
94 
95  // Will be used to obtain a seed for the random number engine
96  std::random_device rd;
97 
98  // seedign the random generator
99  gen = std::mt19937(rd());
100 
101 #if CBLAS==1
102  num_threads = mkl_get_max_threads();
103 #elif CBLAS==2
104  num_threads = openblas_get_num_threads();
105 #endif
106 
107 
108 
109 }
110 
111 
119 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) {
120 
121  // A string labeling the gate operation
122  name = "Decomposition_Interface";
123 
125 
126 
127  // the unitary operator to be decomposed
128  Umtx = Umtx_in;
129 
130  // logical value describing whether the decomposition was finalized or not
131  decomposition_finalized = false;
132 
133  // A string describing the type of the class
135 
136  // error of the unitarity of the final decomposition
137  decomposition_error = DBL_MAX;
138 
139  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
141 
142  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
144 
145  // The current minimum of the optimization problem
146  current_minimum = std::numeric_limits<double>::max();
147 
148  // The global minimum of the optimization problem
150 
151  // logical value describing whether the optimization problem was solved or not
153 
154  // number of iteratrion loops in the finale optimization
155  //iteration_loops = dict()
156 
157  // The maximal allowed error of the optimization problem
158  optimization_tolerance = 1e-7;
159 
160  // Maximal number of iteartions in the optimization process
161  max_outer_iterations = 1e8;
162 
163  // number of operators in one sub-layer of the optimization process
164  optimization_block = -1;
165 
166  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
167  initial_guess = initial_guess_in;
168 
169  // The convergence threshold in the optimization process
170  convergence_threshold = 1e-5;
171 
172  //global phase of the unitary matrix
175 
176  //name of the SQUANDER project
177  std::string projectname = "";
178 
179 
180  // config maps
181  config = config_in;
182 
183  use_float = false;
184  if ( config.count("use_float") > 0 ) {
185  config["use_float"].get_property( use_float );
186  }
187 
188  if ( use_float ) {
190  }
191 
192  // Will be used to obtain a seed for the random number engine
193  std::random_device rd;
194 
195  // seedign the random generator
196  gen = std::mt19937(rd());
197 
198 #if CBLAS==1
199  num_threads = mkl_get_max_threads();
200 #elif CBLAS==2
201  num_threads = openblas_get_num_threads();
202 #endif
203 
204 #ifdef __MPI__
205  // Get the number of processes
206  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
207 
208  // Get the rank of the process
209  MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
210 
211 #endif
212 
213 }
214 
218 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) {
219 
220  // A string labeling the gate operation
221  name = "Decomposition_Interface";
222 
224 
225  // Keep the single precision input as the active unitary for the hot path.
226  Umtx_float = Umtx_in;
227  Umtx = Umtx_in.to_float64();
228 
229  // logical value describing whether the decomposition was finalized or not
230  decomposition_finalized = false;
231 
232  // A string describing the type of the class
234 
235  // error of the unitarity of the final decomposition
236  decomposition_error = DBL_MAX;
237 
238  // number of finalizing (deterministic) opertaions counted from the top of the array of gates
240 
241  // the number of the finalizing (deterministic) parameters counted from the top of the optimized_parameters list
243 
244  // The current minimum of the optimization problem
245  current_minimum = std::numeric_limits<double>::max();
246 
247  // The global minimum of the optimization problem
249 
250  // logical value describing whether the optimization problem was solved or not
252 
253  // The maximal allowed error of the optimization problem
254  optimization_tolerance = 1e-7;
255 
256  // Maximal number of iteartions in the optimization process
257  max_outer_iterations = 1e8;
258 
259  // number of operators in one sub-layer of the optimization process
260  optimization_block = -1;
261 
262  // method to guess initial values for the optimization. Possible values: ZEROS, RANDOM, CLOSE_TO_ZERO (default)
263  initial_guess = initial_guess_in;
264 
265  // The convergence threshold in the optimization process
266  convergence_threshold = 1e-5;
267 
268  //global phase of the unitary matrix
271 
272  //name of the SQUANDER project
273  std::string projectname = "";
274 
275  // config maps
276  config = config_in;
277  use_float = true;
278 
279  // Will be used to obtain a seed for the random number engine
280  std::random_device rd;
281 
282  // seedign the random generator
283  gen = std::mt19937(rd());
284 
285 #if CBLAS==1
286  num_threads = mkl_get_max_threads();
287 #elif CBLAS==2
288  num_threads = openblas_get_num_threads();
289 #endif
290 
291 #ifdef __MPI__
292  // Get the number of processes
293  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
294 
295  // Get the rank of the process
296  MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
297 
298 #endif
299 
300 }
301 
306 /*
307  if (optimized_parameters != NULL ) {
308  qgd_free( optimized_parameters );
309  optimized_parameters = NULL;
310  }
311 */
312 }
313 
315 
316  if ( !use_float ) {
317  return;
318  }
319 
321  for (int pidx=0; pidx<optimized_parameters_mtx.size(); pidx++) {
322  optimized_parameters_mtx_float[pidx] = static_cast<float>(optimized_parameters_mtx[pidx]);
323  }
324 }
325 
326 
331 void Decomposition_Base::set_optimization_blocks( int optimization_block_in) {
332  optimization_block = optimization_block_in;
333 }
334 
339 void Decomposition_Base::set_max_iteration( int max_outer_iterations_in) {
340  max_outer_iterations = max_outer_iterations_in;
341 }
342 
343 
344 
345 
350 void Decomposition_Base::list_gates( int start_index ) {
351 
353 
354 }
355 
356 
357 
358 
359 
365 void Decomposition_Base::solve_optimization_problem( double* solution_guess, int solution_guess_num ) {
366 
367 
368  if ( gates.size() == 0 ) {
369  return;
370  }
371 
372  // array containing minimums to check convergence of the solution
373  const int min_vec_num = 20;
374  double minimum_vec[min_vec_num];
375  for ( int idx=0; idx<min_vec_num; idx++) {
376  minimum_vec[idx] = 0;
377  }
378 
379  // setting the initial value for the current minimum
380  current_minimum = std::numeric_limits<double>::max();
381 
382  // store the gates
383  std::vector<Gate*> gates_loc = gates;
384 
385 
386 
387  // store the number of parameters
388  int parameter_num_loc = parameter_num;
389 
390  // store the initial unitary to be decomposed
391  Matrix Umtx_loc = Umtx;
392  Matrix_float Umtx_float_loc;
393  if ( use_float ) {
394  Umtx_float_loc = Umtx_float.copy();
395  }
396 
397  // storing the initial computational parameters
398  int optimization_block_loc = optimization_block;
399 
400  if ( optimization_block == -1 ) {
401  optimization_block = gates.size();
402  }
403 
404  // random generator of real numbers
405  std::uniform_real_distribution<> distrib_real(0.0, 2*M_PI);
406 
407  // the array storing the optimized parameters
408  Matrix_real optimized_parameters(1, parameter_num_loc);
409 
410  // preparing solution guess for the iterations
411  if ( initial_guess == ZEROS ) {
412  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
413  optimized_parameters[idx] = 0;
414  }
415  }
416  else if ( initial_guess == RANDOM ) {
417  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
418  optimized_parameters[idx] = distrib_real(gen);
419  }
420 
421 #ifdef __MPI__
422  MPI_Bcast( (void*)optimized_parameters.get_data(), parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
423 #endif
424 
425 
426  }
427  else if ( initial_guess == CLOSE_TO_ZERO ) {
428  for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
429  optimized_parameters[idx] = distrib_real(gen)/100;
430  }
431 
432 #ifdef __MPI__
433  MPI_Bcast( (void*)optimized_parameters.get_data(), parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
434 #endif
435 
436  }
437  else {
438  std::string err("bad value for initial guess");
439  }
440 
441  if ( solution_guess_num > 0) {
442  memcpy(optimized_parameters.get_data() + parameter_num-solution_guess_num, solution_guess, solution_guess_num*sizeof(double));
443  }
444 
445 
446  // starting number of gate block applied prior to the optimalized gate blocks
447  int pre_gate_parameter_num = 0;
448 
449  // defining temporary variables for iteration cycles
450  size_t block_idx_end;
451  size_t block_idx_start = 0;//gates.size();
452  gates.clear();
453  int block_parameter_num;
454  Gate* fixed_gate_post = new Gate( qbit_num );
455  std::vector<Matrix, tbb::cache_aligned_allocator<Matrix>> gates_mtxs_post;
456 
457  // the identity matrix used in the calculations
458  Matrix Identity = create_identity( matrix_size );
459 
460 
461 
462 
463 
464  // maximal number of outer iterations overriden by config
465  long long max_outer_iterations_loc;
466  if ( config.count("max_outer_iterations") > 0 ) {
467  config["max_outer_iterations"].get_property( max_outer_iterations_loc );
468 
469  }
470  else {
471  max_outer_iterations_loc =max_outer_iterations;
472  }
473 
474 
475  //measure the time for the decomposition
476  tbb::tick_count start_time = tbb::tick_count::now();
477 
478 
480  // Start the iterations
481  long long iter_idx;
482  for ( iter_idx=0; iter_idx<max_outer_iterations_loc; iter_idx++) {
483 
484  //determine the range of blocks to be optimalized togedther
485  block_idx_end = block_idx_start + optimization_block;
486  if (block_idx_end > gates_loc.size()) {
487  block_idx_end = gates_loc.size();
488  }
489 
490  // determine the number of free parameters to be optimized
491  block_parameter_num = 0;
492  for ( size_t block_idx=block_idx_start; block_idx < block_idx_end; block_idx++) {
493  block_parameter_num = block_parameter_num + gates_loc[block_idx]->get_parameter_num();
494  }
495 
496  // ***** get applied the fixed gates applied before the optimized gates *****
497  if (block_idx_start > 0 ) {
498 
499 
500  std::vector<Gate*> gates_save = gates;
501  gates.clear();
502  gates.reserve( gates_save.size()-1 );
503  for( std::vector<Gate*>::iterator gate_it = gates_save.begin(); gate_it != gates_save.end()-1; gate_it++ ) {
504  gates.push_back( *gate_it );
505  }
507  if ( use_float ) {
508  Matrix_real_float optimized_parameters_float(1, optimized_parameters_mtx.size());
509  for (int pidx=0; pidx<optimized_parameters_mtx.size(); pidx++) {
510  optimized_parameters_float[pidx] = static_cast<float>(optimized_parameters_mtx[pidx]);
511  }
512  Gates_block::apply_to( optimized_parameters_float, Umtx_float );
513  }
514  else {
516  }
517 
518  gates = gates_save;
519  }
520  else {
521  Umtx = Umtx_loc.copy();
522  if ( use_float ) {
523  Umtx_float = Umtx_float_loc.copy();
524  }
525  }
526 
527 
528  // clear the gate list used in the previous iterations
529  gates.clear();
530 
531  if (optimized_parameters_mtx.size() > 0 ) {
533  }
534 
535 
536 
537 
538  // create a list of gates for the optimization process
539  for ( size_t idx=block_idx_start; idx<block_idx_end; idx++ ) {
540  gates.push_back( gates_loc[idx] );
541  }
542 
543  // Create a general gate describing the cumulative effect of gates following the optimized gates
544  if (block_idx_end < gates_loc.size()) {
545 
546  int parameter_idx = 0;
547  for (size_t gate_idx=0; gate_idx<block_idx_end; gate_idx++) {
548  Gate* gate = gates_loc[gate_idx];
549  parameter_idx = parameter_idx + gate->get_parameter_num();
550  }
551 
552  Matrix_real optimized_parameters_partial = Matrix_real( optimized_parameters.get_data()+parameter_idx, 1, optimized_parameters.size()-parameter_idx );
553 
554  std::vector<Gate*> gates_save = gates;
555  gates.clear();
556  gates.reserve( gates_loc.size()-block_idx_end );
557  for( std::vector<Gate*>::iterator gate_it = gates_loc.begin() + block_idx_end; gate_it != gates_loc.end(); gate_it++ ) {
558  gates.push_back( *gate_it );
559  }
561 
562  if ( use_float ) {
563  Matrix_real_float optimized_parameters_partial_float(1, optimized_parameters_partial.size());
564  for (int pidx=0; pidx<optimized_parameters_partial.size(); pidx++) {
565  optimized_parameters_partial_float[pidx] = static_cast<float>(optimized_parameters_partial[pidx]);
566  }
567  Matrix_float post_mtx_float = create_identity_float( matrix_size );
568  Gates_block::apply_to( optimized_parameters_partial_float, post_mtx_float );
569 
570  gates = gates_save;
571 
572  fixed_gate_post->set_matrix( post_mtx_float.to_float64() );
573  }
574  else {
575  Matrix post_mtx = Identity.copy();
576  apply_to( optimized_parameters_partial, post_mtx );
577 
578  gates = gates_save;
579 
580  fixed_gate_post->set_matrix( post_mtx );
581  }
582 
583  gates.push_back( fixed_gate_post );
585  }
586  else {
587  // release gate products
588  //gates_mtxs_post.clear();
589  fixed_gate_post->set_matrix( Identity );
590  }
591 
592 
593 
594  // constructing solution guess for the optimization
595  parameter_num = block_parameter_num;
596  Matrix_real solution_guess_tmp = Matrix_real(1, parameter_num);
597  memcpy( solution_guess_tmp.get_data(), optimized_parameters.get_data() + pre_gate_parameter_num, parameter_num*sizeof(double) );
598 
599 
600  // solve the optimization problem of the block
601  solve_layer_optimization_problem( parameter_num, solution_guess_tmp );
602 
604 
605  // add the current minimum to the array of minimums and calculate the mean
606  double minvec_mean = 0;
607  for (int idx=min_vec_num-1; idx>0; idx--) {
608  minimum_vec[idx] = minimum_vec[idx-1];
609  minvec_mean = minvec_mean + minimum_vec[idx-1];
610  }
611  minimum_vec[0] = current_minimum;
612  minvec_mean = minvec_mean + current_minimum;
613  minvec_mean = minvec_mean/min_vec_num;
614 
615 
616 
617  // store the obtained optimalized parameters for the block
618  memcpy( optimized_parameters.get_data()+pre_gate_parameter_num, optimized_parameters_mtx.get_data(), parameter_num*sizeof(double) );
619 
620 
621  if (block_idx_end == gates_loc.size()) {
622  // restart the block-wise iteration again
623  block_idx_start = 0;
624  pre_gate_parameter_num = 0;
625  }
626  else {
627  // mode the block-wies optimization to the next block
628  block_idx_start = block_idx_start + optimization_block;
629  pre_gate_parameter_num = pre_gate_parameter_num + block_parameter_num;
630  }
631 
632 
633  // optimization result is displayed in each 500th iteration
634  if (iter_idx % 500 == 0) {
635  tbb::tick_count current_time = tbb::tick_count::now();
636  std::stringstream sstream;
637  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;
638  print(sstream, 2);
639  start_time = tbb::tick_count::now();
640  }
641 
642 
643  // calculate the variance of the last 10 minimums
644  double minvec_std = 0.0;
645  for ( int kdx=0; kdx<min_vec_num; kdx++ ) {
646  double tmp = minimum_vec[kdx] - minvec_mean;
647  minvec_std += tmp*tmp;
648  }
649  minvec_std = sqrt(minvec_std/(min_vec_num-1));
650 
651  // conditions to break the iteration cycles
652  if (std::abs(minvec_std/minimum_vec[min_vec_num-1]) < convergence_threshold ) {
653  std::stringstream sstream;
654  sstream << "The iterations converged to minimum " << current_minimum << " after " << iter_idx << " outer iterations with " << layer_num << " layers" << std::endl;
655  print(sstream, 1);
656  break;
657  }
658  else if (check_optimization_solution()) {
659  std::stringstream sstream;
660  sstream << "The minimum with " << layer_num << " layers after " << iter_idx << " outer iterations is " << current_minimum << std::endl;
661  print(sstream, 1);
662  break;
663  }
664 
665  }
666 
667 
668  if (iter_idx == max_outer_iterations_loc && max_outer_iterations_loc>1) {
669  std::stringstream sstream;
670  sstream << "Reached maximal number of outer iterations" << std::endl << std::endl;
671  print(sstream, 1);
672  }
673 
674  // restoring the parameters to originals
675  optimization_block = optimization_block_loc;
676 
677  // store the result of the optimization
678  gates.clear();
679  gates = gates_loc;
681 
682  parameter_num = parameter_num_loc;
683 
684 
686 
687  memcpy( optimized_parameters_mtx.get_data(), optimized_parameters.get_data(), parameter_num*sizeof(double) );
689 
690  delete(fixed_gate_post);
691 
692  // restore the original unitary
693  Umtx = Umtx_loc; // copy?
694  if ( use_float ) {
695  Umtx_float = Umtx_float_loc;
696  }
697 
698 
699 }
700 
701 
702 
703 
710  return;
711 }
712 
713 
714 
715 
721  return current_minimum;
722 }
723 
724 
725 
726 
727 
733 
734  double optimization_tolerance_loc;
735  if ( config.count("optimization_tolerance") > 0 ) {
736  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
737  }
738  else {
739  optimization_tolerance_loc = optimization_tolerance;
740  }
741 
742  return (std::abs(current_minimum - global_target_minimum) < optimization_tolerance_loc);
743 
744 }
745 
746 
752  return Umtx;
753 }
754 
756  return Umtx_float;
757 }
758 
760  return use_float;
761 }
762 
763 
769  return matrix_size;
770 }
771 
777 
779 
780 }
781 
783 
786 
787 }
788 
794  memcpy(ret, optimized_parameters_mtx.get_data(), parameter_num*sizeof(double));
795  return;
796 }
797 
798 
804 
805  if ( num_of_parameters == 0 ) {
806  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
808  return;
809  }
810 
811  if ( parameter_num != num_of_parameters ) {
812  std::string err("Decomposition_Base::set_optimized_parameters: The number of parameters does not match with the free parameters of the circuit.");
813  throw err;
814  }
815 
816  optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
817  memcpy( optimized_parameters_mtx.get_data(), parameters, num_of_parameters*sizeof(double) );
819 
820  return;
821 }
822 
823 
829  if ( use_float ) {
831  Matrix_float ret = Umtx_float.copy();
833  return ret.to_float64();
834  }
835 
836  Matrix ret = Umtx.copy();
838 
839  return ret;
840 }
841 
842 
843 
850 Matrix
851 Decomposition_Base::apply_gate( Matrix& gate_mtx, Matrix& input_matrix ) {
852 
853  // Getting the transformed state upon the transformation given by gate
854  return dot( gate_mtx, input_matrix );
855 
856 }
857 
864 int Decomposition_Base::set_max_layer_num( int n, int max_layer_num_in ) {
865 
866  std::map<int,int>::iterator key_it = max_layer_num.find( n );
867 
868  if ( key_it != max_layer_num.end() ) {
869  max_layer_num.erase( key_it );
870  }
871 
872  max_layer_num.insert( std::pair<int, int>(n, max_layer_num_in) );
873 
874  return 0;
875 
876 }
877 
878 
884 int Decomposition_Base::set_max_layer_num( std::map<int, int> max_layer_num_in ) {
885 
886 
887  for ( std::map<int,int>::iterator it = max_layer_num_in.begin(); it!=max_layer_num_in.end(); it++) {
888  set_max_layer_num( it->first, it->second );
889  }
890 
891  return 0;
892 
893 }
894 
895 
900 void Decomposition_Base::reorder_qubits( std::vector<int> qbit_list) {
901 
902 
903  Gates_block::reorder_qubits( qbit_list);
904 
905  // now reorder the unitary to be decomposed
906 
907  // obtain the permutation indices of the matrix rows/cols
908  std::vector<int> perm_indices;
909  perm_indices.reserve(matrix_size);
910 
911  for (int idx=0; idx<matrix_size; idx++) {
912 
913  int row_idx=0;
914 
915  // contruct the index corresponding to the premuted qubits
916  for( int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++ ) {
917 
918  // state of qubit qbit_list[qbit_idx] in the index idx
919  int qbit_state = ( idx >> qbit_list[qbit_idx] ) & 1;
920 
921  row_idx = row_idx + qbit_state*Power_of_2(qbit_idx);
922  }
923 
924  perm_indices.push_back(row_idx);
925 
926  }
927 
928  // reordering the matrix elements
929  Matrix reordered_mtx = Matrix(matrix_size, matrix_size);
930  for (int row_idx = 0; row_idx<matrix_size; row_idx++) {
931  for (int col_idx = 0; col_idx<matrix_size; col_idx++) {
932  int index_reordered = perm_indices[row_idx]*Umtx.rows + perm_indices[col_idx];
933  int index_umtx = row_idx*Umtx.rows + col_idx;
934  reordered_mtx[index_reordered] = Umtx[index_umtx];
935  }
936  }
937 
938  Umtx = reordered_mtx;
939 
940 }
941 
942 
943 
950 int Decomposition_Base::set_iteration_loops( int n, int iteration_loops_in ) {
951 
952  std::map<int,int>::iterator key_it = iteration_loops.find( n );
953 
954  if ( key_it != iteration_loops.end() ) {
955  iteration_loops.erase( key_it );
956  }
957 
958  iteration_loops.insert( std::pair<int, int>(n, iteration_loops_in) );
959 
960  return 0;
961 
962 }
963 
964 
970 int Decomposition_Base::set_iteration_loops( std::map<int, int> iteration_loops_in ) {
971 
972  for ( std::map<int,int>::iterator it=iteration_loops_in.begin(); it!= iteration_loops_in.end(); it++ ) {
973  set_iteration_loops( it->first, it->second );
974  }
975 
976  return 0;
977 
978 }
979 
980 
981 
986 
987  // default layer numbers
988  max_layer_num_def[2] = 3;
989  max_layer_num_def[3] = 14;
990  max_layer_num_def[4] = 60;
991  max_layer_num_def[5] = 240;
992  max_layer_num_def[6] = 1350;
993  max_layer_num_def[7] = 7000;//6180;
994 
995 }
996 
997 
998 
999 
1000 
1001 
1002 
1003 
1009 
1010  optimization_tolerance = tolerance_in;
1011  return;
1012 }
1013 
1014 
1015 
1020 void Decomposition_Base::set_convergence_threshold( double convergence_threshold_in ) {
1021 
1022  convergence_threshold = convergence_threshold_in;
1023  return;
1024 }
1025 
1031 
1032  return decomposition_error;
1033 
1034 }
1035 
1036 
1037 
1038 
1044 
1045  return current_minimum;
1046 
1047 }
1048 
1054  return project_name;
1055 }
1056 
1061 void Decomposition_Base::set_project_name(std::string& project_name_new){
1062  project_name = project_name_new;
1063  return;
1064 }
1065 
1066 
1072  global_phase_factor = mult(global_phase_factor, global_phase_factor_new);
1073  return;
1074 }
1075 
1081  return global_phase_factor;
1082 }
1083 
1088 void Decomposition_Base::set_global_phase(double new_global_phase){
1089  global_phase_factor.real = sqrt(2)*cos(new_global_phase);
1090  global_phase_factor.imag = sqrt(2)*sin(new_global_phase);
1091  return;
1092 }
1093 
1099  mult(global_phase_factor, u3_gate);
1100  return;
1101 }
1102 
1109  set_global_phase(0);
1110  return;
1111 }
1112 
1113 
1119  FILE* pFile;
1120  if (project_name != ""){filename = project_name + "_" + filename;}
1121 
1122  const char* c_filename = filename.c_str();
1123  pFile = fopen(c_filename, "wb");
1124  if (pFile==NULL) {
1125  fputs ("File error",stderr);
1126  std::string error("Cannot open file.");
1127  throw error;
1128  }
1129 
1130 
1131  fwrite(&Umtx.rows, sizeof(int), 1, pFile);
1132  fwrite(&Umtx.cols, sizeof(int), 1, pFile);
1133  fwrite(Umtx.get_data(), sizeof(QGD_Complex16), Umtx.size(), pFile);
1134  fclose(pFile);
1135  return;
1136 }
1137 
1138 
1139 
1145  FILE* pFile;
1146 
1147  if (project_name != ""){filename = project_name + "_" + filename;}
1148 
1149  const char* c_filename = filename.c_str();
1150  int cols;
1151  int rows;
1152  pFile = fopen(c_filename, "rb");
1153  if (pFile==NULL) {
1154  fputs ("File error",stderr);
1155  exit (1);
1156  }
1157 
1158  fread_wrapper(&rows, sizeof(int), 1, pFile);
1159  fread_wrapper(&cols, sizeof(int), 1, pFile);
1160 
1161 
1162  Matrix Umtx_ = Matrix(rows, cols);
1163 
1164  fread_wrapper(Umtx_.get_data(), sizeof(QGD_Complex16), rows*cols, pFile);
1165  fclose(pFile);
1166  return Umtx_;
1167 }
1168 
1169 
1175 
1176  int parallel;
1177  if ( config.count("parallel") > 0 ) {
1178  long long value;
1179  config["parallel"].get_property( value );
1180  parallel = (int) value;
1181  }
1182  else {
1183  parallel = 2;
1184  }
1185 
1186 
1187  return parallel;
1188 
1189 }
1190 
1191 
1196 void Decomposition_Base::set_qbit_num( int qbit_num_in ) {
1197 
1198  // check the size of the unitary
1199  int matrix_size_loc = 1 << qbit_num_in;
1200  if ( matrix_size_loc != matrix_size ) {
1201  std::string err("Decomposition_Base::set_qbit_num: The new number of qubits is not in line with the input unitary to be decomposed");
1202  throw err;
1203  }
1204 
1205  // setting the number of qubits
1206  Gates_block::set_qbit_num(qbit_num_in);
1207 
1208 
1209 }
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.
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 ...