Sequential Quantum Gate Decomposer  v1.9.7
Powerful decomposition of general unitarias into one- and two-qubit gates gates
N_Qubit_Decomposition_adaptive.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 */
26 #include "Random_Orthogonal.h"
27 #include "Random_Unitary.h"
28 
29 #include "X.h"
30 
31 #include <time.h>
32 #include <stdlib.h>
33 #include <numeric>
34 
35 
36 #ifdef __DFE__
37 #include "common_DFE.h"
38 #endif
39 
40 
41 
42 
43 
49 
50 
51  // set the level limit
52  level_limit = 0;
53 
54 
55 
56  // BFGS is better for smaller problems, while ADAM for larger ones
57  if ( qbit_num <= 5 ) {
59 
60  // Maximal number of iteartions in the optimization process
62  max_inner_iterations = 10000;
63  }
64  else {
66 
67  // Maximal number of iteartions in the optimization process
69  }
70 
71 
72  // Boolean variable to determine whether randomized adaptive layers are used or not
74 
75 
76 }
77 
87 N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive( Matrix Umtx_in, int qbit_num_in, int level_limit_in, int level_limit_min_in, std::map<std::string, Config_Element>& config, int accelerator_num ) : Optimization_Interface(Umtx_in, qbit_num_in, false, config, RANDOM, accelerator_num) {
88 
89 
90  // set the level limit
91  level_limit = level_limit_in;
92  level_limit_min = level_limit_min_in;
93 
94  // BFGS is better for smaller problems, while ADAM for larger ones
95  if ( qbit_num <= 5 ) {
97 
98  // Maximal number of iteartions in the optimization process
100 
101  max_inner_iterations = 10000;
102 
103  }
104  else {
105  set_optimizer( ADAM );
106 
107  // Maximal number of iteartions in the optimization process
109 
110  }
111 
112  // Boolean variable to determine whether randomized adaptive layers are used or not
114 
115 
116 }
117 
121 N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive( Matrix_float Umtx_in, int qbit_num_in, int level_limit_in, int level_limit_min_in, std::map<std::string, Config_Element>& config, int accelerator_num ) : Optimization_Interface(Umtx_in, qbit_num_in, false, config, RANDOM, accelerator_num) {
122 
123 
124  // set the level limit
125  level_limit = level_limit_in;
126  level_limit_min = level_limit_min_in;
127 
128  // BFGS is better for smaller problems, while ADAM for larger ones
129  if ( qbit_num <= 5 ) {
130  set_optimizer( BFGS );
131 
132  // Maximal number of iteartions in the optimization process
134 
135  max_inner_iterations = 10000;
136 
137  }
138  else {
139  set_optimizer( ADAM );
140 
141  // Maximal number of iteartions in the optimization process
143 
144  }
145 
146  // Boolean variable to determine whether randomized adaptive layers are used or not
148 
149 
150 }
151 
152 
153 
163 N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive( Matrix Umtx_in, int qbit_num_in, int level_limit_in, int level_limit_min_in, std::vector<matrix_base<int>> topology_in, std::map<std::string, Config_Element>& config, int accelerator_num ) : Optimization_Interface(Umtx_in, qbit_num_in, false, config, RANDOM, accelerator_num) {
164 
165 
166 
167  // set the level limit
168  level_limit = level_limit_in;
169  level_limit_min = level_limit_min_in;
170 
171  // Maximal number of iteartions in the optimization process
173 
174 
175  // setting the topology
176  topology = topology_in;
177 
178 
179 
180 
181  // BFGS is better for smaller problems, while ADAM for larger ones
182  if ( qbit_num <= 5 ) {
183  alg = BFGS;
184 
185  // Maximal number of iteartions in the optimization process
187  max_inner_iterations = 10000;
188  }
189  else {
190  alg = ADAM;
191 
192  // Maximal number of iteartions in the optimization process
194  }
195 
196  // Boolean variable to determine whether randomized adaptive layers are used or not
198 
199 }
200 
204 N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive( Matrix_float Umtx_in, int qbit_num_in, int level_limit_in, int level_limit_min_in, std::vector<matrix_base<int>> topology_in, std::map<std::string, Config_Element>& config, int accelerator_num ) : Optimization_Interface(Umtx_in, qbit_num_in, false, config, RANDOM, accelerator_num) {
205 
206 
207 
208  // set the level limit
209  level_limit = level_limit_in;
210  level_limit_min = level_limit_min_in;
211 
212  // Maximal number of iteartions in the optimization process
214 
215 
216  // setting the topology
217  topology = topology_in;
218 
219 
220 
221 
222  // BFGS is better for smaller problems, while ADAM for larger ones
223  if ( qbit_num <= 5 ) {
224  alg = BFGS;
225 
226  // Maximal number of iteartions in the optimization process
228  max_inner_iterations = 10000;
229  }
230  else {
231  alg = ADAM;
232 
233  // Maximal number of iteartions in the optimization process
235  }
236 
237  // Boolean variable to determine whether randomized adaptive layers are used or not
239 
240 }
241 
246 
247 }
248 
249 
250 
255 void
257 
258 
259  //The stringstream input to store the output messages.
260  std::stringstream sstream;
261  sstream << "***************************************************************" << std::endl;
262  sstream << "Starting to disentangle " << qbit_num << "-qubit matrix" << std::endl;
263  sstream << "***************************************************************" << std::endl << std::endl << std::endl;
264 
265  print(sstream, 1);
266 
267 
268 
269  // get the initial circuit including redundand 2-qbit blocks.
271 
272  // comppress the gate structure
274 
275 
276  // finalyzing the gate structure by turning CRY gates inti CNOT gates and do optimization cycles to correct approximation in this transformation
277  // (CRY gates with small rotation angles are expressed with a single CNOT gate
279 
280 }
281 
282 
283 
284 
285 
290 // temporarily turn off OpenMP parallelism
291 #if BLAS==0 // undefined BLAS
294 #elif BLAS==1 // MKL
295  num_threads = mkl_get_max_threads();
296  MKL_Set_Num_Threads(1);
297 #elif BLAS==2 //OpenBLAS
298  num_threads = openblas_get_num_threads();
299  openblas_set_num_threads(1);
300 #endif
301 
302  if (level_limit == 0 ) {
303  std::stringstream sstream;
304  sstream << "please increase level limit" << std::endl;
305  print(sstream, 0);
306  return;
307  }
308 
309 
310 
311 
312  Gates_block* gate_structure_loc = NULL;
313  if ( gates.size() > 0 ) {
314  std::stringstream sstream;
315  sstream << "Using imported gate structure for the decomposition." << std::endl;
316  print(sstream, 1);
318  }
319  else {
320  std::stringstream sstream;
321  sstream << "Construct initial gate structure for the decomposition." << std::endl;
322  print(sstream, 1);
324  }
325 
326 
327  long long export_circuit_2_binary_loc;
328  if ( config.count("export_circuit_2_binary") > 0 ) {
329  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
330  }
331  else {
332  export_circuit_2_binary_loc = 0;
333  }
334 
335 
336  if ( export_circuit_2_binary_loc > 0 ) {
337  std::string filename("circuit_squander.binary");
338  if (project_name != "") {
339  filename = project_name+ "_" +filename;
340  }
341  export_gate_list_to_binary(optimized_parameters_mtx, gate_structure_loc, filename, verbose);
342 
343  std::string unitaryname("unitary_squander.binary");
344  if (project_name != "") {
345  filename = project_name+ "_" +unitaryname;
346  }
347  export_unitary(unitaryname);
348 
349  }
350 
351  // store the created gate structure
352  release_gates();
353  combine( gate_structure_loc );
354  delete( gate_structure_loc );
355 
356 
357 #if BLAS==0 // undefined BLAS
359 #elif BLAS==1 //MKL
360  MKL_Set_Num_Threads(num_threads);
361 #elif BLAS==2 //OpenBLAS
362  openblas_set_num_threads(num_threads);
363 #endif
364 }
365 
366 
367 
368 
373 
374 // temporarily turn off OpenMP parallelism
375 #if BLAS==0 // undefined BLAS
378 #elif BLAS==1 // MKL
379  num_threads = mkl_get_max_threads();
380  MKL_Set_Num_Threads(1);
381 #elif BLAS==2 //OpenBLAS
382  num_threads = openblas_get_num_threads();
383  openblas_set_num_threads(1);
384 #endif
385 
386  std::stringstream sstream;
387  sstream.str("");
388  sstream << std::endl;
389  sstream << std::endl;
390  sstream << "**************************************************************" << std::endl;
391  sstream << "***************** Compressing Gate structure *****************" << std::endl;
392  sstream << "**************************************************************" << std::endl;
393  print(sstream, 1);
394  Gates_block* gate_structure_loc = NULL;
395  if ( gates.size() > 0 ) {
396  std::stringstream sstream;
397  sstream << "Using imported gate structure for the compression." << std::endl;
398  print(sstream, 1);
399 
400  gate_structure_loc = static_cast<Gates_block*>(this)->clone();
401  }
402  else {
403  std::stringstream sstream;
404  sstream << "No circuit initalised." << std::endl;
405  print(sstream, 1);
406  return;
407  }
408 
409  sstream.str("");
410  sstream << "Compressing gate structure consisting of " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
411  print(sstream, 1);
412  sstream.str("");
413 
414 
415  int iter = 0;
416  int uncompressed_iter_num = 0;
417 
418  long long export_circuit_2_binary_loc;
419  if ( config.count("export_circuit_2_binary") > 0 ) {
420  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
421  }
422  else {
423  export_circuit_2_binary_loc = 0;
424  }
425 
426 
427  while ( iter<25 || uncompressed_iter_num <= 5 ) {
428  std::stringstream sstream;
429  sstream.str("");
430  sstream << "iteration " << iter+1 << ": ";
431  print(sstream, 1);
432  Gates_block* gate_structure_compressed;
433 
434  gate_structure_compressed = compress_gate_structure( gate_structure_loc,uncompressed_iter_num );
435  if ( gate_structure_compressed->get_gate_num() < gate_structure_loc->get_gate_num() ) {
436  uncompressed_iter_num = 0;
437  }
438  else {
439  uncompressed_iter_num++;
440  }
441 
442  if ( gate_structure_compressed != gate_structure_loc ) {
443 
444  delete( gate_structure_loc );
445  gate_structure_loc = gate_structure_compressed;
446  gate_structure_compressed = NULL;
447 
448 
449 
450  if ( export_circuit_2_binary_loc > 0 ) {
451  std::string filename("circuit_compression.binary");
452  if (project_name != "") {
453  filename=project_name+ "_" +filename;
454  }
455  export_gate_list_to_binary(optimized_parameters_mtx, gate_structure_loc, filename, verbose);
456 
457 
458  std::string filename_unitary("unitary_compression.binary");
459  if (project_name != "") {
460  filename_unitary=project_name+ "_" +filename_unitary;
461  }
462  export_unitary(filename_unitary);
463 
464 
465  }
466  }
467 
468  iter++;
469 
470  if (uncompressed_iter_num>1) break;
471  // store the decomposing gate structure
472  }
473 
474  release_gates();
475 
476 
477  combine( gate_structure_loc );
478  delete( gate_structure_loc );
479 
480 #if BLAS==0 // undefined BLAS
482 #elif BLAS==1 //MKL
483  MKL_Set_Num_Threads(num_threads);
484 #elif BLAS==2 //OpenBLAS
485  openblas_set_num_threads(num_threads);
486 #endif
487 
488 }
489 
490 
491 
492 
493 
494 
495 
500 
501 
502 // temporarily turn off OpenMP parallelism
503 #if BLAS==0 // undefined BLAS
506 #elif BLAS==1 // MKL
507  num_threads = mkl_get_max_threads();
508  MKL_Set_Num_Threads(1);
509 #elif BLAS==2 //OpenBLAS
510  num_threads = openblas_get_num_threads();
511  openblas_set_num_threads(1);
512 #endif
513 
514 
515 
516 
517 
518  std::stringstream sstream;
519  sstream.str("");
520  sstream << "**************************************************************" << std::endl;
521  sstream << "************ Final tuning of the Gate structure **************" << std::endl;
522  sstream << "**************************************************************" << std::endl;
523  print(sstream, 1);
524 
525  // maximal number of inner iterations overriden by config
526  if ( config.count("optimization_tolerance") > 0 ) {
527  long long value;
528  config["optimization_tolerance"].get_property( value );
529  optimization_tolerance = (double)value;
530  }
531  else {optimization_tolerance = 1e-4;}
532 
533 
534 
536 
537 
538  sstream.str("");
539  sstream << "cost function value before replacing trivial CRY gates: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
540  print(sstream, 3);
541 
542  Gates_block* gate_structure_tmp = replace_CRY_gates();
543  Matrix_real optimized_parameters_save = optimized_parameters_mtx;
544 
545  release_gates();
546  combine( gate_structure_tmp );
547 
548  sstream.str("");
549  sstream << "cost function value before final optimization: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
550  print(sstream, 3);
551 
552  release_gates();
553  optimized_parameters_mtx = optimized_parameters_save;
554 
555  // solve the optimization problem
556  N_Qubit_Decomposition_custom cDecomp_custom;
557 
558 
559  std::map<std::string, Config_Element> config_copy;
560  config_copy.insert(config.begin(), config.end());
561  if ( config.count("max_inner_iterations_final") > 0 ) {
562  long long val;
563  config["max_inner_iterations_final"].get_property( val );
564  Config_Element element;
565  element.set_property( "max_inner_iterations", val );
566  config_copy["max_inner_iterations"] = element;
567  }
568 
569 
570  // solve the optimization problem in isolated optimization process
571  if ( use_float ) {
572  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx_float.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
573  }
574  else {
575  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
576  }
577  cDecomp_custom.set_custom_gate_structure( gate_structure_tmp );
579  cDecomp_custom.set_optimization_blocks( gate_structure_tmp->get_gate_num() );
580  cDecomp_custom.set_max_iteration( max_outer_iterations );
581  cDecomp_custom.set_verbose(verbose);
582  cDecomp_custom.set_cost_function_variant( cost_fnc );
583  cDecomp_custom.set_debugfile("");
584  cDecomp_custom.set_iteration_loops( iteration_loops );
586  cDecomp_custom.set_trace_offset( trace_offset );
587  cDecomp_custom.set_optimizer( alg );
588  if (alg==ADAM || alg==BFGS2) {
589  int param_num_loc = gate_structure_tmp->get_parameter_num();
590  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
591  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
592  cDecomp_custom.set_random_shift_count_max( 10000 );
593  }
594  else if ( alg==ADAM_BATCHED ) {
595  cDecomp_custom.set_optimizer( alg );
596  int max_inner_iterations_loc = 2500;
597  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
598  cDecomp_custom.set_random_shift_count_max( 5 );
599  }
600  else if ( alg==BFGS ) {
601  cDecomp_custom.set_optimizer( alg );
602  int max_inner_iterations_loc = 10000;
603  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
604  }
605  cDecomp_custom.start_decomposition();
606  increment_num_iters(cDecomp_custom.get_num_iters());
607 
608  current_minimum = cDecomp_custom.get_current_minimum();
611 
612 
613  combine( gate_structure_tmp );
614  delete( gate_structure_tmp );
615 
616  sstream.str("");
617  sstream << "cost function value after final optimization: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
618  print(sstream, 3);
619 
620 
621 
622  long long export_circuit_2_binary_loc;
623  if ( config.count("export_circuit_2_binary") > 0 ) {
624  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
625  }
626  else {
627  export_circuit_2_binary_loc = 0;
628  }
629 
630 
631  if ( export_circuit_2_binary_loc > 0 ) {
632  std::string filename2("circuit_final.binary");
633 
634  if (project_name != "") {
635  filename2=project_name+ "_" +filename2;
636  }
637 
639 
640  }
641 
643 
644 
645  sstream.str("");
646  sstream << "In the decomposition with error = " << decomposition_error << " were used " << layer_num << " gates with:" << std::endl;
647 
648  // get the number of gates used in the decomposition
649  std::map<std::string, int>&& gate_nums = get_gate_nums();
650 
651  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
652  sstream << it->second << " " << it->first << " gates" << std::endl;
653  }
654 
655  sstream << std::endl;
656  print(sstream, 1);
657 
658 #if BLAS==0 // undefined BLAS
660 #elif BLAS==1 //MKL
661  MKL_Set_Num_Threads(num_threads);
662 #elif BLAS==2 //OpenBLAS
663  openblas_set_num_threads(num_threads);
664 #endif
665 
666 }
667 
672 Gates_block*
674 
675  Gates_block* gate_structure_loc = (static_cast<Gates_block*>(this))->clone();
676 
677  //measure the time for the decompositin
678  tbb::tick_count start_time_loc = tbb::tick_count::now();
679 
680  std::stringstream sstream;
681  sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
682  print(sstream, 1);
683 
684  double optimization_tolerance_loc;
685  if ( config.count("optimization_tolerance") > 0 ) {
686  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
687  }
688  else {
689  optimization_tolerance_loc = optimization_tolerance;
690  }
691 
692  // solve the optimization problem
693  N_Qubit_Decomposition_custom cDecomp_custom;
694  // solve the optimization problem in isolated optimization process
695  if ( use_float ) {
697  }
698  else {
700  }
701  cDecomp_custom.set_custom_gate_structure( gate_structure_loc );
702  cDecomp_custom.set_optimized_parameters( optimized_parameters_mtx_loc.get_data(), optimized_parameters_mtx_loc.size() );
703  cDecomp_custom.set_optimization_blocks( gate_structure_loc->get_gate_num() );
704  cDecomp_custom.set_max_iteration( max_outer_iterations );
705  cDecomp_custom.set_verbose(verbose);
706  cDecomp_custom.set_cost_function_variant( cost_fnc );
707  cDecomp_custom.set_debugfile("");
708  cDecomp_custom.set_iteration_loops( iteration_loops );
709  cDecomp_custom.set_optimization_tolerance( optimization_tolerance_loc );
710  cDecomp_custom.set_trace_offset( trace_offset );
711  cDecomp_custom.set_optimizer( alg );
712  cDecomp_custom.set_project_name( project_name );
713  if (alg==ADAM || alg==BFGS2) {
714  int param_num_loc = gate_structure_loc->get_parameter_num();
715  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
716  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
717  cDecomp_custom.set_random_shift_count_max( 10000 );
718  }
719  else if ( alg==ADAM_BATCHED ) {
720  cDecomp_custom.set_optimizer( alg );
721  int max_inner_iterations_loc = 2500;
722  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
723  cDecomp_custom.set_random_shift_count_max( 5 );
724  }
725  else if ( alg==BFGS ) {
726  cDecomp_custom.set_optimizer( alg );
727  int max_inner_iterations_loc = 10000;
728  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
729  }
730  cDecomp_custom.start_decomposition();
731  increment_num_iters(cDecomp_custom.get_num_iters());
732  //cDecomp_custom.list_gates(0);
733 
734  tbb::tick_count end_time_loc = tbb::tick_count::now();
735 
736  current_minimum = cDecomp_custom.get_current_minimum();
737  optimized_parameters_mtx_loc = cDecomp_custom.get_optimized_parameters();
738 
739 
740 
741  if ( cDecomp_custom.get_current_minimum() < optimization_tolerance_loc ) {
742  std::stringstream sstream;
743  sstream << "Optimization problem solved with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
744  print(sstream, 1);
745  }
746  else {
747  std::stringstream sstream;
748  sstream << "Optimization problem converged to " << cDecomp_custom.get_current_minimum() << " with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
749  print(sstream, 1);
750  }
751 
752  if (current_minimum > optimization_tolerance_loc) {
753  std::stringstream sstream;
754  sstream << "Decomposition did not reached prescribed high numerical precision." << std::endl;
755  print(sstream, 1);
756  optimization_tolerance_loc = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
757  }
758 
759  sstream.str("");
760  sstream << "Continue with the compression of gate structure consisting of " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
761  print(sstream, 1);
762  return gate_structure_loc;
763 
764 
765 
766 }
767 
772 Gates_block*
774 
775  // strages to store the optimized minimums in case of different cirquit depths
776  std::vector<double> minimum_vec;
777  std::vector<Gates_block*> gate_structure_vec;
778  std::vector<Matrix_real> optimized_parameters_vec;
779 
780  double optimization_tolerance_loc;
781  if ( config.count("optimization_tolerance") > 0 ) {
782  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
783  }
784  else {
785  optimization_tolerance_loc = optimization_tolerance;
786  }
787 
788  int max_outer_iterations_loc;
789  double value_placeholder;
790  if ( config.count("max_outer_iterations") > 0 ) {
791  config["max_outer_iterations"].get_property( value_placeholder );
792  max_outer_iterations_loc = (int) value_placeholder;
793  }
794  else {
795  max_outer_iterations_loc = max_outer_iterations;
796  }
797 
798 
799  int level = level_limit_min;
800  while ( current_minimum > optimization_tolerance_loc && level <= level_limit) {
801 
802  // create gate structure to be optimized
803  Gates_block* gate_structure_loc = new Gates_block(qbit_num);
804 
805  optimized_parameters_mtx_loc = Matrix_real(0,0);
806 
807  for (int idx=0; idx<level; idx++) {
808 
809  // create the new decomposing layer and add to the gate staructure
810  add_adaptive_layers( gate_structure_loc );
811 
812  }
813 
814  // add finalyzing layer to the top of the gate structure
815  add_finalyzing_layer( gate_structure_loc );
816 
817 
818  //measure the time for the decompositin
819  tbb::tick_count start_time_loc = tbb::tick_count::now();
820 
821 
822  N_Qubit_Decomposition_custom cDecomp_custom_random, cDecomp_custom_close_to_zero;
823 
824  std::stringstream sstream;
825  sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
826  print(sstream, 1);
827 
828 
829 /*
830 #ifndef __DFE__
831  // try the decomposition withrandom and with close to zero initial values
832  tbb::parallel_invoke(
833  [&]{
834 #endif
835 */
836  // solve the optimization problem in isolated optimization process
837  if ( use_float ) {
838  cDecomp_custom_random = N_Qubit_Decomposition_custom( Umtx_float.copy(), qbit_num, false, config, RANDOM, accelerator_num);
839  }
840  else {
841  cDecomp_custom_random = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config, RANDOM, accelerator_num);
842  }
843  cDecomp_custom_random.set_custom_gate_structure( gate_structure_loc );
844  cDecomp_custom_random.set_optimization_blocks( gate_structure_loc->get_gate_num() );
845  cDecomp_custom_random.set_max_iteration( max_outer_iterations_loc );
846 #ifndef __DFE__
847  cDecomp_custom_random.set_verbose(verbose);
848 #else
849  cDecomp_custom_random.set_verbose(0);
850 #endif
851  cDecomp_custom_random.set_cost_function_variant( cost_fnc );
852  cDecomp_custom_random.set_debugfile("");
853  cDecomp_custom_random.set_optimization_tolerance( optimization_tolerance_loc );
854  cDecomp_custom_random.set_trace_offset( trace_offset );
855  cDecomp_custom_random.set_optimizer( alg );
856  cDecomp_custom_random.set_project_name( project_name );
857  if ( alg == ADAM || alg == BFGS2 ) {
858  int param_num_loc = gate_structure_loc->get_parameter_num();
859  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
860  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
861  cDecomp_custom_random.set_random_shift_count_max( 10000 );
862  }
863  else if ( alg==ADAM_BATCHED ) {
864  cDecomp_custom_random.set_optimizer( alg );
865  int max_inner_iterations_loc = 2000;
866  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
867  cDecomp_custom_random.set_random_shift_count_max( 5 );
868  }
869  else if ( alg==BFGS ) {
870  cDecomp_custom_random.set_optimizer( alg );
871  int max_inner_iterations_loc = 10000;
872  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
873  }
874 
875 
876  cDecomp_custom_random.start_decomposition();
877 
878 
879 
880  increment_num_iters(cDecomp_custom_random.get_num_iters()); // retrive the number of iterations spent on optimization
881 /*
882 #ifndef __DFE__
883  },
884  [&]{
885  // solve the optimization problem in isolated optimization process
886  cDecomp_custom_close_to_zero = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, CLOSE_TO_ZERO);
887  cDecomp_custom_close_to_zero.set_custom_gate_structure( gate_structure_loc );
888  cDecomp_custom_close_to_zero.set_optimization_blocks( gate_structure_loc->get_gate_num() );
889  cDecomp_custom_close_to_zero.set_max_iteration( max_outer_iterations );
890  cDecomp_custom_close_to_zero.set_verbose(0);
891  cDecomp_custom_close_to_zero.set_cost_function_variant( cost_fnc );
892  cDecomp_custom_close_to_zero.set_debugfile("");
893  cDecomp_custom_close_to_zero.set_optimization_tolerance( optimization_tolerance );
894  cDecomp_custom_close_to_zero.set_optimizer( alg );
895  if ( alg == ADAM || alg == BFGS2 ) {
896  int param_num_loc = gate_structure_loc->get_parameter_num();
897  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
898  cDecomp_custom_close_to_zero.set_max_inner_iterations( max_inner_iterations_loc );
899  cDecomp_custom_close_to_zero.set_random_shift_count_max( 10000 );
900  }
901  cDecomp_custom.close_to_zero.set_trace_offset( trace_offset );
902  cDecomp_custom_close_to_zero.start_decomposition(true);
903  increment_num_iters(cDecomp_custom_close_to_zero.get_num_iters());
904  }
905  );
906 #endif
907 */
908  tbb::tick_count end_time_loc = tbb::tick_count::now();
909 /*
910 #ifdef __DFE__
911 return NULL;
912 #endif
913 */
914  double current_minimum_random = cDecomp_custom_random.get_current_minimum();
915  double current_minimum_close_to_zero = cDecomp_custom_close_to_zero.get_current_minimum();
916  double current_minimum_loc;
917 
918 
919  // select between the results obtained for different initial value strategy
920  if ( current_minimum_random < optimization_tolerance_loc && current_minimum_close_to_zero > optimization_tolerance_loc ) {
921  current_minimum_loc = current_minimum_random;
922  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
924  }
925  else if ( current_minimum_random > optimization_tolerance_loc && current_minimum_close_to_zero < optimization_tolerance_loc ) {
926  current_minimum_loc = current_minimum_close_to_zero;
927  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
929  }
930  else if ( current_minimum_random < optimization_tolerance_loc && current_minimum_close_to_zero < optimization_tolerance_loc ) {
931  Matrix_real optimized_parameters_mtx_random = cDecomp_custom_random.get_optimized_parameters();
932  Matrix_real optimized_parameters_mtx_close_to_zero = cDecomp_custom_close_to_zero.get_optimized_parameters();
933 
934  int panelty_random = get_panelty(gate_structure_loc, optimized_parameters_mtx_random);
935  int panelty_close_to_zero = get_panelty(gate_structure_loc, optimized_parameters_mtx_close_to_zero );
936 
937  if ( panelty_random < panelty_close_to_zero ) {
938  current_minimum_loc = current_minimum_random;
939  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
941  }
942  else {
943  current_minimum_loc = current_minimum_close_to_zero;
944  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
946  }
947 
948  }
949  else {
950  if ( current_minimum_random < current_minimum_close_to_zero ) {
951  current_minimum_loc = current_minimum_random;
952  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
954  }
955  else {
956  current_minimum_loc = current_minimum_close_to_zero;
957  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
959  }
960 
961  }
962 
963  minimum_vec.push_back(current_minimum_loc);
964  gate_structure_vec.push_back(gate_structure_loc);
965  optimized_parameters_vec.push_back(optimized_parameters_mtx_loc);
966 
967 
968 
969  if ( current_minimum_loc < optimization_tolerance_loc ) {
970  std::stringstream sstream;
971  sstream << "Optimization problem solved with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
972  print(sstream, 1);
973  break;
974  }
975  else {
976  std::stringstream sstream;
977  sstream << "Optimization problem converged to " << current_minimum_loc << " with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
978  print(sstream, 1);
979  }
980 
981  level++;
982  }
983 
984 //exit(-1);
985 
986  // find the best decomposition
987  int idx_min = 0;
988  double current_minimum = minimum_vec[0];
989  for (int idx=1; idx<(int)minimum_vec.size(); idx++) {
990  if( current_minimum > minimum_vec[idx] ) {
991  idx_min = idx;
992  current_minimum = minimum_vec[idx];
993  }
994  }
995 
996 
997  Gates_block* gate_structure_loc = gate_structure_vec[idx_min];
998  optimized_parameters_mtx_loc = optimized_parameters_vec[idx_min];
999 
1000  // release unnecesarry data
1001  for (int idx=0; idx<(int)minimum_vec.size(); idx++) {
1002  if( idx == idx_min ) {
1003  continue;
1004  }
1005  delete( gate_structure_vec[idx] );
1006  }
1007  minimum_vec.clear();
1008  gate_structure_vec.clear();
1009  optimized_parameters_vec.clear();
1010 
1011 
1012 
1013  if (current_minimum > optimization_tolerance_loc) {
1014  std::stringstream sstream;
1015  sstream << "Decomposition did not reached prescribed high numerical precision." << std::endl;
1016  print(sstream, 1);
1017  optimization_tolerance_loc = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
1018  }
1019 
1020 
1021  return gate_structure_loc;
1022 
1023 }
1024 
1025 
1026 
1031 Gates_block*
1033 
1034 
1035 
1036  int layer_num_max;
1037  int layer_num_orig = gate_structure->get_gate_num()-1; // TODO: see line 1558 to explain the -1: the last finalyzing layer of U3 gates is not tested for removal
1038  if ( layer_num_orig < 50 ) layer_num_max = layer_num_orig;
1039  else if ( layer_num_orig < 60 ) layer_num_max = 4;
1040  else layer_num_max = 2;
1041  double optimization_tolerance_loc;
1042  if ( config.count("optimization_tolerance") > 0 ) {
1043  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
1044  }
1045  else {
1046  optimization_tolerance_loc = optimization_tolerance;
1047  }
1048 
1049  // random generator of integers
1050  std::uniform_int_distribution<> distrib_int(0, 5000);
1051  // create a list of layers to be tested for removal.
1052  std::vector<int> layers_to_remove;
1053  if (uncompressed_iter_num==0){
1054  layer_num_max = 5<layer_num_orig ? 5 : layer_num_orig;
1055  // create a list of layers to be tested for removal.
1056  std::vector<double> layers_parameters(layer_num_orig,15.0);
1057  std::vector<int> layers_idx_sorted(layer_num_orig,0);
1058  //layers_to_remove.reserve(layer_num_orig);
1059  for (int idx=0; idx<layer_num_orig;idx++){
1060  layers_parameters[idx] = extract_theta_from_layer(gate_structure,idx,optimized_parameters_mtx);
1061  layers_idx_sorted[idx] = idx;
1062  }
1063 
1064  std::iota(layers_idx_sorted.begin(),layers_idx_sorted.end(),0); //Initializing
1065  sort( layers_idx_sorted.begin(),layers_idx_sorted.end(), [&](int i,int j){return layers_parameters[i]<layers_parameters[j];} );
1066 
1067  for (int idx=0; idx<layer_num_max; idx++ ) { // TODO: see line 1558 to explain the -1
1068  layers_to_remove.push_back( layers_idx_sorted[idx]);
1069  }
1070  }
1071  else{
1072  layers_to_remove.reserve(layer_num_orig);
1073  for (int idx=0; idx<layer_num_orig; idx++ ) { // TODO: see line 1558 to explain the -1
1074  layers_to_remove.push_back(idx);
1075  }
1076 
1077 
1078  while ( (int)layers_to_remove.size() > layer_num_max ) {
1079  int remove_idx = distrib_int(gen) % layers_to_remove.size();
1080 
1081  layers_to_remove.erase( layers_to_remove.begin() + remove_idx );
1082  }
1083  }
1084 
1085 
1086 #ifdef __MPI__
1087  MPI_Bcast( &layers_to_remove[0], layers_to_remove.size(), MPI_INT, 0, MPI_COMM_WORLD);
1088 #endif
1089 
1090  // make a copy of the original unitary. (By removing trivial gates global phase might be added to the unitary)
1091  Matrix&& Umtx_orig = Umtx.copy();
1092 
1093  int panelties_num = layer_num_max < layer_num_orig ? layer_num_max : layer_num_orig;
1094 
1095  if ( panelties_num == 0 ) {
1096  return gate_structure;
1097  }
1098 
1099  // preallocate panelties associated with the number of remaining two-qubit controlled gates
1100  std::vector<unsigned int> panelties(panelties_num, 1<<31);
1101  std::vector<Gates_block*> gate_structures_vec(panelties_num, NULL);
1102  std::vector<double> current_minimum_vec(panelties_num, DBL_MAX);
1103  std::vector<int> iteration_num_vec(panelties_num, 0);
1104 
1105 
1106  std::vector<Matrix_real> optimized_parameters_vec(panelties_num, Matrix_real(0,0));
1107  std::vector<Matrix> Umtx_vec(panelties_num, Matrix(0,0));
1108 
1109 
1110 
1111  for (int idx=0; idx<panelties_num; idx++) {
1112 
1113  Umtx = Umtx_orig.copy();
1114 
1115  double current_minimum_loc = DBL_MAX;//current_minimum;
1116  int iteration_num = 0;
1117  Matrix_real optimized_parameters_loc = optimized_parameters_mtx.copy();
1118 
1119  Gates_block* gate_structure_reduced = compress_gate_structure( gate_structure, layers_to_remove[idx], optimized_parameters_loc, current_minimum_loc, iteration_num );
1120  if ( optimized_parameters_loc.size() == 0 ) {
1121  optimized_parameters_loc = optimized_parameters_mtx.copy();
1122  }
1123 
1124  // remove further adaptive gates if possible
1125  Gates_block* gate_structure_tmp;
1126  if ( gate_structure_reduced->get_gate_num() == gate_structure->get_gate_num() ) {
1127  gate_structure_tmp = gate_structure_reduced->clone();
1128  }
1129  else {
1130  gate_structure_tmp = remove_trivial_CRY_gates( gate_structure_reduced, optimized_parameters_loc, current_minimum_loc ); //TODO: reverse gate order
1131  }
1132 
1133  panelties[idx] = get_panelty(gate_structure_tmp, optimized_parameters_loc);
1134  gate_structures_vec[idx] = gate_structure_tmp;
1135  current_minimum_vec[idx] = current_minimum_loc;
1136  iteration_num_vec[idx] = iteration_num; // the accumulated number of optimization iterations
1137 
1138 
1139  optimized_parameters_vec[idx] = optimized_parameters_loc;
1140  Umtx_vec[idx] = Umtx;
1141 
1142 
1143  delete(gate_structure_reduced);
1144 
1145 #ifdef __DFE__
1146  if ( current_minimum_vec[idx] < optimization_tolerance_loc ) {
1147  break;
1148  }
1149 #endif
1150  }
1151 
1152 
1153 
1154  // determine the reduction with the lowest penalty
1155  unsigned int panelty_min = panelties[0];
1156  unsigned int idx_min = 0;
1157 
1158  for (size_t idx=0; idx<panelties.size(); idx++) {
1159  if ( panelty_min > panelties[idx] ) {
1160  panelty_min = panelties[idx];
1161  idx_min = static_cast<unsigned int>(idx);
1162  }
1163 
1164  else if ( panelty_min == panelties[idx] ) {
1165 
1166  if ( (distrib_int(gen) % 2) == 1 ) {
1167  idx_min = static_cast<unsigned int>(idx);
1168 
1169  panelty_min = panelties[idx];
1170  }
1171 
1172  }
1173 
1174  }
1175 
1176 #ifdef __MPI__
1177  MPI_Bcast( &idx_min, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);
1178 #endif
1179 
1180 
1181  // release gate structures other than the best one
1182  for (size_t idx=0; idx<panelties.size(); idx++) {
1183  if (idx==idx_min) {
1184  continue;
1185  }
1186 
1187 
1188  if ( gate_structures_vec[idx] == gate_structure) {
1189  continue;
1190  }
1191 
1192  if ( gate_structures_vec[idx] ) {
1193  delete( gate_structures_vec[idx] );
1194  gate_structures_vec[idx] = NULL;
1195  }
1196 
1197  }
1198 
1199 
1200  gate_structure = gate_structures_vec[idx_min];
1201  optimized_parameters_mtx = optimized_parameters_vec[idx_min];
1202  current_minimum = current_minimum_vec[idx_min];
1203  increment_num_iters(iteration_num_vec[idx_min]); // the total number of the accumulated optimization iterations
1204  Umtx = Umtx_vec[idx_min];
1205 
1206  int layer_num = gate_structure->get_gate_num();
1207 
1208  if ( layer_num < layer_num_orig+1 ) {
1209  std::stringstream sstream;
1210  sstream << "gate structure reduced from " << layer_num_orig+1 << " to " << layer_num << " decomposing layers" << std::endl;
1211  print(sstream, 1);
1212  }
1213  else {
1214  std::stringstream sstream;
1215  sstream << "gate structure kept at " << layer_num << " layers" << std::endl;
1216  print(sstream, 1);
1217  }
1218 
1219 
1220  return gate_structure;
1221 
1222 
1223 
1224 }
1225 
1234 Gates_block*
1235 N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_structure, int layer_idx, Matrix_real& optimized_parameters, double& current_minimum_loc, int& iteration_num ) {
1236 
1237  // create reduced gate structure without layer indexed by layer_idx
1238  Gates_block* gate_structure_reduced = gate_structure->clone();
1239  gate_structure_reduced->release_gate( layer_idx );
1240 
1241  Matrix_real parameters_reduced;
1242  if ( optimized_parameters.size() > 0 ) {
1243  parameters_reduced = create_reduced_parameters( gate_structure, optimized_parameters, layer_idx );
1244  }
1245  else {
1246  parameters_reduced = Matrix_real(0, 0);
1247  }
1248 
1249 
1250 
1251  N_Qubit_Decomposition_custom cDecomp_custom;
1252 
1253  std::map<std::string, Config_Element> config_copy;
1254  config_copy.insert(config.begin(), config.end());
1255  if ( config.count("max_inner_iterations_compression") > 0 ) {
1256  long long val;
1257  config["max_inner_iterations_compression"].get_property( val );
1258  Config_Element element;
1259  element.set_property( "max_inner_iterations", val );
1260  config_copy["max_inner_iterations"] = element;
1261  }
1262 
1263  double optimization_tolerance_loc;
1264  if ( config.count("optimization_tolerance") > 0 ) {
1265  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
1266  }
1267  else {
1268  optimization_tolerance_loc = optimization_tolerance;
1269  }
1270 
1271 
1272 
1273  // solve the optimization problem in isolated optimization process
1274  if ( use_float ) {
1275  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx_float.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
1276  }
1277  else {
1278  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
1279  }
1280  cDecomp_custom.set_custom_gate_structure( gate_structure_reduced );
1281  cDecomp_custom.set_optimized_parameters( parameters_reduced.get_data(), parameters_reduced.size() );
1282  cDecomp_custom.set_verbose(0);
1283  cDecomp_custom.set_cost_function_variant( cost_fnc );
1284  cDecomp_custom.set_debugfile("");
1285  cDecomp_custom.set_max_iteration( max_outer_iterations );
1286  cDecomp_custom.set_iteration_loops( iteration_loops );
1287  cDecomp_custom.set_optimization_blocks( gate_structure_reduced->get_gate_num() ) ;
1288  cDecomp_custom.set_optimization_tolerance( optimization_tolerance_loc );
1289  cDecomp_custom.set_trace_offset( trace_offset );
1290  cDecomp_custom.set_optimizer( alg );
1291  if ( alg == ADAM || alg==BFGS2) {
1292  cDecomp_custom.set_max_inner_iterations( 100000 );
1293  cDecomp_custom.set_random_shift_count_max( 1 );
1294  }
1295  else if ( alg==BFGS ) {
1296  cDecomp_custom.set_optimizer( alg );
1297  int max_inner_iterations_loc = 100;
1298  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
1299  }
1300  cDecomp_custom.start_decomposition();
1301  iteration_num = cDecomp_custom.get_num_iters();
1302  double current_minimum_tmp = cDecomp_custom.get_current_minimum();
1303 
1304  if ( current_minimum_tmp < optimization_tolerance_loc ) {
1305  //cDecomp_custom.list_gates(0);
1306  optimized_parameters = cDecomp_custom.get_optimized_parameters();
1307  current_minimum_loc = current_minimum_tmp;
1308  return gate_structure_reduced;
1309  }
1310 
1311 
1312  return gate_structure->clone();
1313 
1314 }
1315 
1316 
1322 unsigned int
1324 
1325 
1326  int panelty = 0;
1327 
1328  // iterate over the elements of the parameter array
1329  int parameter_idx = 0;
1330  int layer_num = gate_structure->get_gate_num();
1331  //for ( int layer_idx=layer_num-1; layer_idx>=0; layer_idx--) {
1332  for ( int layer_idx=0; layer_idx<layer_num; layer_idx++) {
1333 
1334  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate( layer_idx ) );
1335 
1336  int gate_num = layer->get_gate_num();
1337  //for( int gate_idx=gate_num-1; gate_idx>=0; gate_idx-- ) {
1338  for( int gate_idx=0; gate_idx<gate_num; gate_idx++ ) {
1339 
1340  Gate* gate = layer->get_gate( gate_idx );
1341 
1342  double parameter = optimized_parameters[parameter_idx];
1343  parameter_idx = parameter_idx + gate->get_parameter_num();
1344 
1345  if ( (gate->get_type() != ADAPTIVE_OPERATION) && (gate->get_type() != CROT_OPERATION)) {
1346  continue;
1347  }
1348 
1349  if (gate->get_type() == ADAPTIVE_OPERATION){
1350  if ( std::abs(std::sin(parameter)) < 0.999 && std::abs(std::cos(parameter)) < 1e-3 ) {
1351  // Condition of pure CNOT gate
1352  panelty += 1;
1353  }
1354  else if ( std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1355  // Condition of pure Identity gate
1356  //panelty++;
1357  }
1358  else {
1359  // Condition of controlled rotation gate
1360  panelty += 2;
1361  }
1362  }
1363  else{
1364  panelty +=1;
1365  }
1366 
1367  }
1368 
1369  }
1370 
1371 
1372  return panelty;
1373 
1374 
1375 }
1376 
1377 
1383 Gates_block*
1385 
1387 
1388  Gates_block* gate_structure = NULL;
1389  if ( gates.size() > 0 ) {
1390  gate_structure = static_cast<Gates_block*>(this)->clone();
1391  }
1392  else {
1393  std::stringstream sstream;
1394  sstream << "No circuit initalised." << std::endl;
1395  print(sstream, 1);
1396  throw( sstream.str() );
1397  }
1398 
1399  Matrix_real& optimized_parameters = optimized_parameters_mtx;
1400 
1401  Gates_block* gate_structure_ret = new Gates_block(qbit_num);
1402 
1403  int layer_num = gate_structure->get_gate_num();
1404 
1405 /*
1406  std::map<std::string, Config_Element> config_copy;
1407  config_copy.insert(config.begin(), config.end());
1408  N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1409  cDecomp_custom.set_custom_gate_structure( gate_structure );
1410  std::cout << std::endl << "before removing trivial gate: " << cDecomp_custom.optimization_problem( optimized_parameters ) << std::endl;
1411 */
1412  int parameter_idx = 0;
1413  //for (int idx=layer_num-1; idx>=0; idx-- ) {
1414  for (int idx=0; idx<layer_num; idx++ ) {
1415 
1416  Gate* gate = gate_structure->get_gate(idx);
1417 
1418  if ( gate->get_type() != BLOCK_OPERATION ) {
1419  std::string err = "N_Qubit_Decomposition_adaptive::replace_CRY_gates: Only block gates are accepted in this conversion.";
1420  throw( err );
1421  }
1422 
1423  Gates_block* block_op = static_cast<Gates_block*>(gate);
1424  //int param_num = gate->get_parameter_num();
1425 
1426 
1427  if ( true ) {//gate_structure->contains_adaptive_gate(idx) ) {
1428 
1429  Gates_block* layer = block_op->clone();
1430 
1431  //for ( int jdx=layer->get_gate_num()-1; jdx>=0; jdx-- ) {
1432  for ( int jdx=0; jdx<layer->get_gate_num(); jdx++ ) {
1433 
1434  Gate* gate_tmp = layer->get_gate(jdx);
1435  int param_num = gate_tmp->get_parameter_num();
1436 
1437 
1438  double parameter = optimized_parameters[parameter_idx];
1439  parameter = activation_function(parameter, 1);//limit_max);
1440 
1441 //std::cout << param[0] << " " << (gate_tmp->get_type() == ADAPTIVE_OPERATION) << " " << std::abs(std::sin(param[0])) << " " << 1+std::cos(param[0]) << std::endl;
1442 
1443 
1444  if ( gate_tmp->get_type() == ADAPTIVE_OPERATION && std::abs(std::sin(parameter)) > 0.999 && std::abs(std::cos(parameter)) < 1e-3) {
1445 
1446  // convert to CZ gate
1447  int target_qbit = gate_tmp->get_target_qbit();
1448  int control_qbit = gate_tmp->get_control_qbit();
1449  layer->release_gate( jdx );
1450 
1451  RX* rx_gate_1 = new RX(qbit_num, target_qbit);
1452  CZ* cz_gate = new CZ(qbit_num, target_qbit, control_qbit);
1453  RX* rx_gate_2 = new RX(qbit_num, target_qbit);
1454  RZ* rz_gate = new RZ(qbit_num, control_qbit);
1455 
1456  Gates_block* czr_gate = new Gates_block(qbit_num);
1457  czr_gate->add_gate(rx_gate_1);
1458  czr_gate->add_gate(cz_gate);
1459  czr_gate->add_gate(rx_gate_2);
1460  czr_gate->add_gate(rz_gate);
1461 
1462  layer->insert_gate( (Gate*)czr_gate, jdx);
1463 
1464  Matrix_real parameters_new(1, optimized_parameters.size()+2);
1465 
1466 
1467  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1468 
1469  memcpy(parameters_new.get_data()+parameter_idx+3, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1470 
1471  parameters_new[parameter_idx] = -M_PI/4; // rx_1 parameter
1472  parameters_new[parameter_idx+1] = M_PI/4; // rx_2 parameter
1473 
1474 
1475  if ( std::sin(parameter) < 0 ) {
1476 // parameters_new[parameter_idx+2] = -M_PI/2;
1477  parameters_new[parameter_idx+2] = -M_PI/4; // rz parameter
1478 
1479  QGD_Complex16 global_phase_factor_new;
1480  global_phase_factor_new.real = std::cos( -M_PI/4 );
1481  global_phase_factor_new.imag = std::sin( -M_PI/4 );
1482  apply_global_phase_factor(global_phase_factor_new, Umtx);
1483 
1484  }
1485  else{
1486 // parameters_new[parameter_idx+2] = M_PI/2;
1487  parameters_new[parameter_idx+2] = M_PI/4; // rz parameter
1488 
1489  QGD_Complex16 global_phase_factor_new;
1490  global_phase_factor_new.real = std::cos( M_PI/4 );
1491  global_phase_factor_new.imag = std::sin( M_PI/4 );
1492  apply_global_phase_factor(global_phase_factor_new, Umtx);
1493 
1494  }
1495 
1496 
1497  optimized_parameters = parameters_new;
1498  parameter_idx += 3;
1499 
1500 
1501 
1502  }
1503 
1504  else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION && std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1505  // release trivial gate
1506 
1507  layer->release_gate( jdx );
1508  //jdx--;
1509  Matrix_real parameters_new(1, optimized_parameters.size()-1);
1510  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1511  memcpy(parameters_new.get_data()+parameter_idx, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1512  optimized_parameters = parameters_new;
1513 
1514 
1515  }
1516 
1517  else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION ) {
1518 
1519  // controlled Y rotation decomposed into 2 CNOT gates
1520  int target_qbit = gate_tmp->get_target_qbit();
1521  int control_qbit = gate_tmp->get_control_qbit();
1522  layer->release_gate( jdx );
1523 
1524  RY* ry_gate_1 = new RY(qbit_num, target_qbit);
1525  CNOT* cnot_gate_1 = new CNOT(qbit_num, target_qbit, control_qbit);
1526  RY* ry_gate_2 = new RY(qbit_num, target_qbit);
1527  CNOT* cnot_gate_2 = new CNOT(qbit_num, target_qbit, control_qbit);
1528 
1529  Gates_block* czr_gate = new Gates_block(qbit_num);
1530  czr_gate->add_gate(ry_gate_1);
1531  czr_gate->add_gate(cnot_gate_1);
1532  czr_gate->add_gate(ry_gate_2);
1533  czr_gate->add_gate(cnot_gate_2);
1534 
1535  layer->insert_gate( (Gate*)czr_gate, jdx);
1536 
1537  Matrix_real parameters_new(1, optimized_parameters.size()+1);
1538  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1539  memcpy(parameters_new.get_data()+parameter_idx+2, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1540  optimized_parameters = parameters_new;
1541 
1542  optimized_parameters[parameter_idx] = parameter/2; // ry_1 parameter
1543  optimized_parameters[parameter_idx+1] = -parameter/2; // ry_2 parameter
1544 
1545 
1546  parameter_idx += 2;
1547 
1548  }
1549 
1550  else {
1551 
1552  parameter_idx += param_num;
1553 
1554  }
1555 
1556 
1557 
1558  }
1559 
1560  gate_structure_ret->add_gate((Gate*)layer);
1561 
1562 
1563  }
1564 
1565  }
1566 
1567 /*
1568  N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1569  cDecomp_custom_.set_custom_gate_structure( gate_structure_ret );
1570  std::cout << std::endl << "after removing trivial gate: " << cDecomp_custom_.optimization_problem( optimized_parameters ) << std::endl;
1571  exit(2);
1572 */
1573  delete( gate_structure );
1574  return gate_structure_ret;
1575 
1576 
1577 }
1578 
1579 
1585 
1586  if ( gates.size() == 0 ) {
1587  std::stringstream sstream;
1588  sstream << "No circuit initalised." << std::endl;
1589  print(sstream, 1);
1590  return;
1591  }
1592 
1593  int parameter_num_before = optimized_parameters_mtx.size();
1594  int gate_num_before = get_gate_num();
1595 
1596  Gates_block* gate_structure_loc = static_cast<Gates_block*>(this)->clone();
1597  double current_minimum_loc = current_minimum;
1598 
1599  while (true) {
1600  int layers_before = gate_structure_loc->get_gate_num();
1601  Gates_block* gate_structure_tmp = remove_trivial_CRY_gates( gate_structure_loc, optimized_parameters_mtx, current_minimum_loc );
1602  delete( gate_structure_loc );
1603  gate_structure_loc = gate_structure_tmp;
1604  if ( gate_structure_loc->get_gate_num() >= layers_before ) {
1605  break;
1606  }
1607  }
1608 
1609  release_gates();
1610  combine( gate_structure_loc );
1611  delete( gate_structure_loc );
1612 
1613  current_minimum = current_minimum_loc;
1614 
1615  std::stringstream sstream;
1616  sstream << "Circuit after removing trivial CRY gates: "
1617  << gate_num_before << " -> " << get_gate_num() << " layer(s), "
1618  << parameter_num_before << " -> " << optimized_parameters_mtx.size() << " parameter(s)." << std::endl;
1619  sstream << "Cost function after removing trivial CRY gates: " << optimization_problem( optimized_parameters_mtx ) << std::endl;
1620  print(sstream, 1);
1621 
1622 }
1623 
1630 Gates_block*
1632 
1633  int layer_num = gate_structure->get_gate_num();
1634 
1635  Matrix_real&& optimized_parameters_loc = optimized_parameters.copy();
1636  Gates_block* gate_structure_reduced = NULL;
1637 
1638  for (int idx=0; idx<layer_num; idx++ ) {
1639 
1640  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate(idx) );
1641 
1642  // create new layer if needed to add to the gate structure
1643  Gates_block* new_layer = NULL;
1644 
1645  if ( layer->get_gate_num() < 3 ) {
1646  continue;
1647  }
1648 
1649  Gate* gate_adaptive = layer->get_gate(2);
1650  double parameter = optimized_parameters_loc[layer->get_parameter_start_idx() + gate_adaptive->get_parameter_start_idx()]; // parameter for adaptive gate
1651  parameter = activation_function(parameter, 1);//limit_max);
1652 
1653  if ( (gate_adaptive->get_type() == ADAPTIVE_OPERATION || gate_adaptive->get_type() == CROT_OPERATION) && std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1654 
1655  // std::map<std::string, Config_Element> config_copy;
1656  // config_copy.insert(config.begin(), config.end());
1657  // N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1658  // cDecomp_custom.set_custom_gate_structure( gate_structure );
1659  // std::cout << std::endl << "before removing trivial gate: " << cDecomp_custom.optimization_problem( optimized_parameters_loc ) << std::endl;
1660 
1661  int parameter_idx_to_be_removed = layer->get_parameter_start_idx();
1662 
1663 
1664  // find matching U3 gates into which the U3 gates in the current layer are merged
1665  std::vector<int>&& involved_qbits = layer->get_involved_qubits();
1666  for( size_t rdx=0; rdx<involved_qbits.size(); rdx++ ) {
1667 
1668  U3* U_gate_to_be_removed = static_cast<U3*>(layer->get_gate(static_cast<int>(rdx)));
1669  int qbit_to_be_matched = U_gate_to_be_removed->get_target_qbit();
1670 
1671  int parameter_idx_loc = parameter_idx_to_be_removed + layer->get_parameter_num();
1672 
1673  bool found_match = false;
1674  U3* matching_gate = NULL;
1675 
1676  // iterate over subsequent layers to find the maching gate
1677  for ( int kdx=idx+1; kdx<layer_num; kdx++ ) {
1678 
1679  Gates_block* layer_test = static_cast<Gates_block*>( gate_structure->get_gate(kdx) );
1680 
1681  // iterate over the gates in the tested layer
1682  int gate_num = layer_test->get_gate_num();
1683  for ( int hdx=0; hdx<gate_num; hdx++ ) {
1684 
1685  Gate* gate_test = layer_test->get_gate(hdx);
1686 
1687  if ( gate_test->get_type() == U3_OPERATION ) {
1688  int target_qbit_loc = gate_test->get_target_qbit();
1689 
1690  if ( qbit_to_be_matched == target_qbit_loc ) {
1691  found_match = true;
1692  matching_gate = static_cast<U3*>(gate_test);
1693  parameter_idx_loc = layer_test->get_parameter_start_idx()+matching_gate->get_parameter_start_idx();
1694  break;
1695  }
1696 
1697 
1698  }
1699 
1700  }
1701 
1702  if ( found_match ) break;
1703 
1704 
1705  }
1706 
1707  if ( found_match == false ) {
1708 
1709  if ( new_layer == NULL ) {
1710  new_layer = new Gates_block(qbit_num);
1711  }
1712 
1713  new_layer->add_u3(U_gate_to_be_removed->get_target_qbit());
1714 
1715  Matrix_real optimized_parameters_loc_tmp = Matrix_real(1, optimized_parameters_loc.size() + U_gate_to_be_removed->get_parameter_num());
1716  memset(optimized_parameters_loc_tmp.get_data(), 0, optimized_parameters_loc_tmp.size()*sizeof(double));
1717  memcpy(optimized_parameters_loc_tmp.get_data(), optimized_parameters_loc.get_data(), optimized_parameters_loc.size()*sizeof(double));
1718 
1719  Matrix_real param1( &optimized_parameters_loc[parameter_idx_to_be_removed], 1, U_gate_to_be_removed->get_parameter_num() );
1720  memcpy(optimized_parameters_loc_tmp.get_data()+optimized_parameters_loc.size(), param1.get_data(), U_gate_to_be_removed->get_parameter_num()*sizeof(double));
1721 
1722  optimized_parameters_loc = optimized_parameters_loc_tmp;
1723 
1724  optimized_parameters_loc[parameter_idx_to_be_removed] = 0.0;
1725  optimized_parameters_loc[parameter_idx_to_be_removed+1] = 0.0;
1726  optimized_parameters_loc[parameter_idx_to_be_removed+2] = 0.0;
1727  parameter_idx_to_be_removed = parameter_idx_to_be_removed + U_gate_to_be_removed->get_parameter_num();
1728 
1729  }
1730  else {
1731 
1732  Matrix_real param1( &optimized_parameters_loc[parameter_idx_to_be_removed], 1, U_gate_to_be_removed->get_parameter_num() );
1733  Matrix U3_matrix1 = Gate::calc_one_qubit_u3(param1[0], param1[1], param1[2]);
1734 
1735  Matrix_real param2( &optimized_parameters_loc[parameter_idx_loc], 1, matching_gate->get_parameter_num() );
1736  Matrix U3_matrix2 = Gate::calc_one_qubit_u3(param2[0], param2[1], param2[2]);
1737 
1738  Matrix U3_prod = dot(U3_matrix2, U3_matrix1);
1739 
1740  optimized_parameters_loc[parameter_idx_to_be_removed] = 0.0;
1741  optimized_parameters_loc[parameter_idx_to_be_removed+1] = 0.0;
1742  optimized_parameters_loc[parameter_idx_to_be_removed+2] = 0.0;
1743  parameter_idx_to_be_removed = parameter_idx_to_be_removed + U_gate_to_be_removed->get_parameter_num();
1744 
1745  // calculate the new theta/2, phi, lambda parameters from U3_prod, and replace them in param2
1746  // global phase on Umtx
1747  double ctheta3_over2 = std::sqrt(U3_prod[0].real*U3_prod[0].real+U3_prod[0].imag*U3_prod[0].imag); // cos( theta/2 )
1748  double stheta3_over2 = std::sqrt(U3_prod[2].real*U3_prod[2].real+U3_prod[2].imag*U3_prod[2].imag); // sin( theta/2 )
1749  double theta3_over2 = std::atan2(stheta3_over2,ctheta3_over2); // theta/2
1750 
1751  double alpha = std::atan2(U3_prod[0].imag,U3_prod[0].real); // the global phase
1752 
1753  double lambda3;
1754  double phi3;
1755 
1756  if (std::abs(stheta3_over2)<4e-8){
1757  lambda3 = (std::atan2(U3_prod[3].imag,U3_prod[3].real)-alpha)/2;
1758  phi3 = lambda3;
1759  }
1760  else {
1761  lambda3 = std::atan2(-1*U3_prod[1].imag,-1*U3_prod[1].real)-alpha;
1762  phi3 = std::atan2(U3_prod[2].imag,U3_prod[2].real)-alpha;
1763  }
1764 
1765  // the product U3 matrix
1766  Matrix U3_new = Gate::calc_one_qubit_u3(theta3_over2, phi3, lambda3);
1767  QGD_Complex16 global_phase_factor_new;
1768  global_phase_factor_new.real = std::cos(alpha);
1769  global_phase_factor_new.imag = std::sin(alpha);
1770  apply_global_phase_factor(global_phase_factor_new, U3_new);
1771 
1772  // test for the product U3 matrix
1773  if (std::sqrt((U3_new[3].real-U3_prod[3].real)*(U3_new[3].real-U3_prod[3].real)) + std::sqrt((U3_new[3].imag-U3_prod[3].imag)*(U3_new[3].imag-U3_prod[3].imag)) < 1e-8 && (stheta3_over2*stheta3_over2+ctheta3_over2*ctheta3_over2) > 0.99) {
1774 
1775  // setting the resulting parameters if test passed
1776 
1777  param2[0] = theta3_over2;
1778  param2[1] = phi3;
1779  param2[2] = lambda3;
1780  apply_global_phase_factor(global_phase_factor_new, Umtx);
1781 
1782  }
1783  }
1784 
1785  // N_Qubit_Decomposition_custom cDecomp_custom__( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1786  // cDecomp_custom__.set_custom_gate_structure( gate_structure_loc );
1787  // std::cout << "right before removing a trivial gate: " << cDecomp_custom__.optimization_problem( optimized_parameters_loc ) << std::endl;
1788 
1789  }
1790 
1791 
1792  // std::stringstream sstream;
1793  // sstream << "N_Qubit_Decomposition_adaptive::remove_trivial_CRY_gates: Removing trivial gateblock" << std::endl;
1794  // print(sstream, 3);
1795 
1796  // remove gate from the structure
1797  // create reduced gate structure without layer indexed by idx
1798  gate_structure_reduced = gate_structure->clone();
1799  if ( new_layer != NULL ) {
1800  gate_structure_reduced->add_gate(new_layer);
1801  new_layer = NULL;
1802  }
1803 
1804 
1805 
1806  if ( optimized_parameters_loc.size() > 0 ) {
1807  optimized_parameters_loc = create_reduced_parameters( gate_structure_reduced, optimized_parameters_loc, idx );
1808  }
1809  else {
1810  optimized_parameters_loc = Matrix_real(0, 0);
1811  }
1812 
1813  gate_structure_reduced->release_gate( idx );
1814 
1815 
1816 
1817  // N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1818  // cDecomp_custom_.set_custom_gate_structure( gate_structure_reduced );
1819  // std::cout << "after removing a trivial gate: " << cDecomp_custom_.optimization_problem( optimized_parameters_loc ) << std::endl;
1820 
1821  optimized_parameters = optimized_parameters_loc;
1822  layer_num = gate_structure_reduced->get_gate_num();
1823  break;
1824 
1825  }
1826 
1827  }
1828 
1829  if ( gate_structure_reduced == NULL ) {
1830  gate_structure_reduced = gate_structure->clone();
1831  }
1832 
1833  return gate_structure_reduced;
1834 
1835 
1836 
1837 
1838 }
1839 
1840 
1847 Matrix_real
1849 
1850 
1851  // determine the index of the parameter that is about to delete
1852  int parameter_idx = 0;
1853  for ( int idx=0; idx<layer_idx; idx++) {
1854  //for ( int idx=gates_num-1; idx>layer_idx; idx--) {
1855  Gate* gate = gate_structure->get_gate( idx );
1856  parameter_idx += gate->get_parameter_num();
1857  }
1858 
1859 
1860  Gate* gate = gate_structure->get_gate( layer_idx );
1861  int param_num_removed = gate->get_parameter_num();
1862 
1863  Matrix_real reduced_parameters(1, optimized_parameters.size() - param_num_removed );
1864  memcpy( reduced_parameters.get_data(), optimized_parameters.get_data(), (parameter_idx)*sizeof(double));
1865  memcpy( reduced_parameters.get_data()+parameter_idx, optimized_parameters.get_data()+parameter_idx+param_num_removed, (optimized_parameters.size()-parameter_idx-param_num_removed)*sizeof(double));
1866 
1867 
1868  return reduced_parameters;
1869 }
1870 
1871 
1872 
1873 
1874 
1875 
1876 
1880 void
1882 
1883  add_adaptive_layers( this );
1884 
1885 }
1886 
1890 void
1892 
1893 
1894  // create the new decomposing layer and add to the gate staructure
1896  gate_structure->combine( layer );
1897 
1898 
1899 }
1900 
1901 
1902 
1903 
1907 Gates_block*
1909 
1910 
1911  //The stringstream input to store the output messages.
1912  std::stringstream sstream;
1913 
1914  // creating block of gates
1915  Gates_block* block = new Gates_block( qbit_num );
1916 
1917  std::vector<Gates_block* > layers;
1918 
1919 
1920  if ( topology.size() > 0 ) {
1921  for ( std::vector<matrix_base<int>>::iterator it=topology.begin(); it!=topology.end(); it++) {
1922 
1923  if ( it->size() != 2 ) {
1924  std::string err("The connectivity data should contains two qubits.");
1925  throw err;
1926  }
1927 
1928  int control_qbit_loc = (*it)[0];
1929  int target_qbit_loc = (*it)[1];
1930 
1931  if ( control_qbit_loc >= qbit_num || target_qbit_loc >= qbit_num ) {
1932  std::string err("Label of control/target qubit should be less than the number of qubits in the register.");
1933  throw err;
1934  }
1935 
1936  Gates_block* layer = new Gates_block( qbit_num );
1937 
1938  layer->add_u3(target_qbit_loc);
1939  layer->add_u3(control_qbit_loc);
1940  layer->add_adaptive(target_qbit_loc, control_qbit_loc);
1941 
1942  layers.push_back(layer);
1943 
1944 
1945  }
1946  }
1947  else {
1948 
1949  // sequ
1950  for (int target_qbit_loc = 0; target_qbit_loc<qbit_num; target_qbit_loc++) {
1951  for (int control_qbit_loc = target_qbit_loc+1; control_qbit_loc<qbit_num; control_qbit_loc++) {
1952 
1953  Gates_block* layer = new Gates_block( qbit_num );
1954 
1955  layer->add_u3(target_qbit_loc);
1956  layer->add_u3(control_qbit_loc);
1957  layer->add_adaptive(target_qbit_loc, control_qbit_loc);
1958 
1959  layers.push_back(layer);
1960  }
1961  }
1962 
1963  }
1964 
1965 /*
1966  for (int idx=0; idx<layers.size(); idx++) {
1967  Gates_block* layer = (Gates_block*)layers[idx];
1968  block->add_gate( layers[idx] );
1969 
1970  }
1971 */
1972 
1973  bool randomized_adaptive_layers_loc;
1974  if ( config.count("randomized_adaptive_layers") > 0 ) {
1975  config["randomized_adaptive_layers"].get_property( randomized_adaptive_layers_loc );
1976  }
1977  else {
1978  randomized_adaptive_layers_loc = randomized_adaptive_layers;
1979  }
1980 
1981 
1982  // make difference between randomized adaptive layers and deterministic one
1983  if (randomized_adaptive_layers_loc) {
1984 
1985  std::uniform_int_distribution<> distrib_int(0, 5000);
1986 
1987  while (layers.size()>0) {
1988  int idx = distrib_int(gen) % layers.size();
1989 
1990 #ifdef __MPI__
1991  MPI_Bcast( &idx, 1, MPI_INT, 0, MPI_COMM_WORLD);
1992 #endif
1993  block->add_gate( layers[idx] );
1994  layers.erase( layers.begin() + idx );
1995  }
1996 
1997  }
1998  else {
1999  while (layers.size()>0) {
2000  block->add_gate( layers[0] );
2001  layers.erase( layers.begin() );
2002  }
2003 
2004  }
2005 
2006 
2007  return block;
2008 
2009 
2010 }
2011 
2012 
2016 void
2018 
2019  add_finalyzing_layer( this );
2020 
2021 }
2022 
2026 void
2028 
2029  // creating block of gates
2030  Gates_block* block = new Gates_block( qbit_num );
2031 
2032  for (int idx=0; idx<qbit_num; idx++) {
2033  block->add_u3(idx);
2034 // block->add_ry(idx);
2035  }
2036 
2037 
2038  // adding the opeartion block to the gates
2039  if ( gate_structure == NULL ) {
2040  throw ("N_Qubit_Decomposition_adaptive::add_finalyzing_layer: gate_structure is null pointer");
2041  }
2042  else {
2043  gate_structure->add_gate( block );
2044  }
2045 
2046  if ( optimized_parameters_mtx.size() > 0 ) {
2047  Matrix_real optimized_parameters_mtx_tmp = Matrix_real(1, get_parameter_num());
2048  memset(optimized_parameters_mtx_tmp.get_data(), 0, optimized_parameters_mtx_tmp.size()*sizeof(double));
2049  memcpy(optimized_parameters_mtx_tmp.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double));
2050  optimized_parameters_mtx = optimized_parameters_mtx_tmp;
2051  }
2052 
2053 }
2054 
2055 
2056 
2057 
2062 void
2064 
2065  if ( gates.size() > 0 ) {
2066  release_gates();
2069  }
2070 
2072  combine( gate_structure );
2073  delete gate_structure;
2074 
2075 }
2076 
2077 
2082 void
2084 
2085  Umtx = import_unitary_from_binary(filename);
2086 
2087 #ifdef __DFE__
2088  if( qbit_num >= 5 ) {
2089  upload_Umtx_to_DFE();
2090  }
2091 #endif
2092 
2093 }
2098 void
2100 
2101  Umtx = Umtx_new;
2102  if ( use_float ) {
2103  Umtx_float = Umtx_new.to_float32();
2104  }
2105 
2106 #ifdef __DFE__
2107  if( qbit_num >= 5 ) {
2108  upload_Umtx_to_DFE();
2109  }
2110 #endif
2111 
2112 }
2113 
2114 void
2116 
2117  Umtx_float = Umtx_new;
2118  Umtx = Umtx_new.to_float64();
2119  use_float = true;
2120 
2121 #ifdef __DFE__
2122  if( qbit_num >= 5 ) {
2123  upload_Umtx_to_DFE();
2124  }
2125 #endif
2126 
2127 }
2128 
2133 void
2135 
2136 
2137 
2138  Matrix_real optimized_parameters_mtx_tmp;
2139  Gates_block* gate_structure_tmp = import_gate_list_from_binary(optimized_parameters_mtx_tmp, filename, verbose);
2140 
2141  if ( gates.size() > 0 ) {
2142  gate_structure_tmp->combine( static_cast<Gates_block*>(this) );
2143 
2144  release_gates();
2145  combine( gate_structure_tmp );
2146 
2147 
2148  Matrix_real optimized_parameters_mtx_tmp2( 1, optimized_parameters_mtx_tmp.size() + optimized_parameters_mtx.size() );
2149 
2150  memcpy( optimized_parameters_mtx_tmp2.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
2151  memcpy( optimized_parameters_mtx_tmp2.get_data()+optimized_parameters_mtx.size(), optimized_parameters_mtx_tmp.get_data(), optimized_parameters_mtx_tmp.size()*sizeof(double) );
2152 
2153  optimized_parameters_mtx = optimized_parameters_mtx_tmp2;
2154  }
2155  else {
2156  combine( gate_structure_tmp );
2157  optimized_parameters_mtx = optimized_parameters_mtx_tmp;
2158  }
2159 
2160 }
2161 
2162 
2163 
2167 void
2169 
2170  if ( gates.size() == 0 ) {
2171  return;
2172  }
2173 
2174 
2175  std::stringstream sstream;
2176  sstream << "The cost function before applying the imported gate structure is:" << optimization_problem( optimized_parameters_mtx ) << std::endl;
2177 
2178  if ( use_float ) {
2179  Matrix_real_float optimized_parameters_float(1, optimized_parameters_mtx.size());
2180  for (int pidx=0; pidx<optimized_parameters_mtx.size(); pidx++) {
2181  optimized_parameters_float[pidx] = static_cast<float>(optimized_parameters_mtx[pidx]);
2182  }
2183  apply_to( optimized_parameters_float, Umtx_float );
2185  }
2186  else {
2188  }
2189  release_gates();
2192 
2193 
2194  sstream << "The cost function after applying the imported gate structure is:" << optimization_problem( optimized_parameters_mtx ) << std::endl;
2195  print(sstream, 3);
2196 
2197 
2198 
2199 }
2200 
2201 
2206 void
2208 
2209 
2210  std::stringstream sstream;
2211  sstream << "Add new layer to the adaptive gate structure." << std::endl;
2212  print(sstream, 2);
2213 
2215 
2216 
2217  combine( layer );
2218 
2220  memset( tmp.get_data(), 0, tmp.size()*sizeof(double) );
2221  memcpy( tmp.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
2222 
2223  optimized_parameters_mtx = tmp;
2224 
2225 }
2226 
2227 double
2229 
2230  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate(layer_idx) );
2231  int layer_start_idx = layer->get_parameter_start_idx();
2232  int layer_gate_num = layer->get_gate_num();
2233  double ThetaOver2=0.;
2234  for (int gate_idx=0; gate_idx<layer_gate_num; gate_idx++){
2235  Gate* gate_tmp = layer->get_gate(gate_idx);
2236  double parameter = optimized_parameters[layer_start_idx+gate_tmp->get_parameter_start_idx()];
2237  if (gate_tmp->get_type() == ADAPTIVE_OPERATION || gate_tmp->get_type() == CROT_OPERATION){
2238  ThetaOver2 = std::sin(parameter)*std::sin(parameter);
2239  break;
2240  }
2241  }
2242  return ThetaOver2;
2243 }
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
optimization_aglorithms alg
The optimization algorithm to be used in the optimization.
Copyright (C) Miklos Maroti, 2021 SPDX-License-Identifier: Apache-2.0.
Definition: U3.h:19
void add_adaptive(int target_qbit, int control_qbit)
Append a Adaptive gate to the list of gates.
virtual unsigned int get_panelty(Gates_block *gate_structure, Matrix_real &optimized_parameters)
Call to get the panelty derived from the number of CRY and CNOT gates in the circuit.
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.
void release_gate(int idx)
Call to release one gate in the list.
Matrix_float to_float32() const
Convert to single precision.
Definition: matrix.cpp:32
int get_num_iters()
Get the number of processed iterations during the optimization process.
void set_optimizer(optimization_aglorithms alg_in)
Call to set the optimizer engine to be used in solving the optimization problem.
Gates_block * optimize_imported_gate_structure(Matrix_real &optimized_parameters_mtx_loc)
Call to optimize an imported gate structure.
void set_project_name(std::string &project_name_new)
Call to set the name of the project.
std::map< std::string, int > get_gate_nums()
Call to get the number of the individual gate types in the list of gates.
Gates_block * replace_CRY_gates()
Call to replace CRY gates in the circuit that are close to either an identity or to a CNOT gate...
void add_adaptive_layers()
Call to add adaptive layers to the gate structure stored by the class.
void set_custom_gate_structure(Gates_block *gate_structure_in)
Call to set custom layers to the gate structure that are intended to be used in the subdecomposition...
Matrix_real copy() const
Call to create a copy of the matrix.
int control_qbit
The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations...
Definition: Gate.h:100
void add_gate(Gate *gate)
Append a general gate to the list of gates.
Matrix to_float64() const
Convert to double precision.
Definition: matrix_float.cpp:8
bool use_float
Selects float32 circuit application for parameter/unitary/state data.
cost_function_type cost_fnc
The chosen variant of the cost function.
void add_finalyzing_layer()
Call to add finalyzing layer (single qubit rotations on all of the qubits) to the gate structure stor...
virtual void remove_trivial_CRY_gates()
Remove blocks containing a trivial CRY gate from the circuit stored by the class. ...
Gates_block * compress_gate_structure(Gates_block *gate_structure, int uncompressed_iter_num)
Call to run compression iterations on the circuit.
int target_qbit
The index of the qubit on which the operation acts (target_qbit >= 0)
Definition: Gate.h:98
double get_current_minimum()
Call to get the obtained minimum of the cost function.
void release_gates()
Call to release the stored gates.
bool randomized_adaptive_layers
Boolean variable to determine whether randomized adaptive layers are used or not. ...
int level_limit
The maximal number of adaptive layers used in the decomposition.
double optimization_problem(double *parameters)
Evaluate the optimization problem of the optimization.
void set_trace_offset(int trace_offset_in)
Set the trace offset used in the evaluation of the cost function.
int trace_offset
The offset in the first columns from which the "trace" is calculated. In this case Tr(A) = sum_(i-off...
virtual Gates_block * clone() override
Create a clone of the present class.
int layer_num
number of gate layers
Definition: Gates_block.h:51
void set_random_shift_count_max(int random_shift_count_max_in)
Call to set the maximal number of parameter randomization tries to escape a local minimum...
A class describing a universal configuration element.
void increment_num_iters(int delta=1)
Atomically increment the tracked number of optimization iterations.
std::vector< matrix_base< int > > topology
A vector of index pairs encoding the connectivity between the qubits.
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 Matrix calc_one_qubit_u3(double ThetaOver2=0.0, double Phi=0.0, double Lambda=0.0)
Build a 2x2 U3 kernel from angles (theta/2, phi, lambda).
Definition: Gate.cpp:2650
void sync_optimized_parameters_float()
Synchronize the float32 parameter mirror from the double optimizer storage.
Gates_block * import_gate_list_from_binary(Matrix_real &parameters, const std::string &filename, int verbosity)
Use to import a quantum circuit from a binary format.
void apply_global_phase_factor()
Call to apply the current global phase to the unitary matrix.
int get_gate_num()
Call to get the number of gates grouped in the class.
virtual ~N_Qubit_Decomposition_adaptive()
Destructor of the class.
Matrix_real create_reduced_parameters(Gates_block *gate_structure, Matrix_real &optimized_parameters, int layer_idx)
Call to remove those parameters from the array, which correspond to gates that are about to be remove...
std::vector< Gate * > gates
The list of stored gates.
Definition: Gates_block.h:49
int max_outer_iterations
Maximal number of iterations allowed in the optimization process.
N_Qubit_Decomposition_adaptive()
Nullary constructor of the class.
A class representing a CZ operation.
Definition: CZ.h:36
std::string project_name
the name of the project
void set_max_inner_iterations(int max_inner_iterations_in)
Call to set the maximal number of iterations for which an optimization engine tries to solve the opti...
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
double optimization_tolerance
The maximal allowed error of the optimization problem (The error of the decomposition would scale wit...
Definition: RZ.h:11
int accelerator_num
number of utilized accelerators
int level_limit_min
The minimal number of adaptive layers used in the decomposition.
void apply_imported_gate_structure()
Call to apply the imported gate structure on the unitary.
void set_debugfile(std::string debugfile)
Call to set the debugfile name.
Definition: logging.cpp:95
void set_unitary_from_file(std::string filename)
Set unitary matrix from file.
void set_adaptive_gate_structure(std::string filename)
Call to set custom layers to the gate structure that are intended to be used in the decomposition...
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.
gate_type get_type()
Call to get the type of the operation.
Definition: Gate.cpp:1333
void add_u3(int target_qbit)
Append a U3 gate to the list of gates.
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...
void combine(Gates_block *op_block)
Call to append the gates of an gate block to the current block.
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 get_parameter_start_idx()
Call to get the starting index of the parameters in the parameter array corresponding to the circuit ...
Definition: Gate.cpp:2535
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...
virtual void compress_circuit()
Compress the circuit.
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
A class representing a CNOT operation.
Definition: CNOT.h:35
void add_adaptive_gate_structure(std::string filename)
Call to append custom layers to the gate structure that are intended to be used in the decomposition...
virtual void get_initial_circuit()
get initial circuit
int verbose
Set the verbosity level of the output messages.
Definition: logging.h:50
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.
Gates_block * construct_adaptive_gate_layers()
Call to construct adaptive layers.
Double-precision complex matrix (float64).
Definition: matrix.h:38
Definition: RY.h:11
int size() const
Call to get the number of the allocated elements.
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:82
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
void omp_set_num_threads(int num_threads)
Set the number of threads on runtime in MKL.
virtual void finalize_circuit()
Finalize the circuit.
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
void set_verbose(int verbose_in)
Call to set the verbose attribute.
Definition: logging.cpp:85
void add_layer_to_imported_gate_structure()
Call to add an adaptive layer to the gate structure previously imported gate structure.
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
void set_property(std::string name_, double val_)
Call to set a double value.
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
virtual void start_decomposition()
Start the disentanglig process of the unitary.
double extract_theta_from_layer(Gates_block *gate_structure, int layer_idx, Matrix_real &optimized_parameters)
virtual void start_decomposition()
Start the disentanglig process of the unitary.
Header file for the paralleized calculation of the cost function of the final optimization problem (s...
volatile double current_minimum
The current minimum of the optimization problem.
Matrix Umtx
The unitary to be decomposed.
double activation_function(double Phi, int limit)
?????
Definition: common.cpp:35
void export_gate_list_to_binary(Matrix_real &parameters, Gates_block *gates_block, const std::string &filename, int verbosity)
Use to export a quantum circuit into binary format.
void insert_gate(Gate *gate, int idx)
Call to insert a gate at a given position.
void set_unitary(Matrix &Umtx_new)
Set unitary matrix.
double real
the real part of a complex number
Definition: QGDTypes.h:40
Header file for a class implementing the adaptive gate decomposition algorithm of arXiv:2203...
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
Header file for DFE support in unitary simulation.
void set_max_iteration(int max_outer_iterations_in)
Call to set the maximal number of the iterations in the optimization process.
int optimization_block
number of gate blocks used in one shot of the optimization process
double decomposition_error
error of the final decomposition
Gate * get_gate(int idx)
Call to get the gates stored in the class.
int set_iteration_loops(int n, int iteration_loops_in)
Set the number of iteration loops during the subdecomposition of the n-th qubit.
Definition: RX.h:11
int max_inner_iterations
the maximal number of iterations for which an optimization engine tries to solve the optimization pro...
Matrix import_unitary_from_binary(std::string &filename)
Import a Unitary matrix from a file.
Gates_block * determine_initial_gate_structure(Matrix_real &optimized_parameters_mtx)
Call determine the gate structrue of the decomposing circuit.
Matrix_real optimized_parameters_mtx
The optimized parameters for the gates.
std::vector< int > get_involved_qubits(bool only_target=false) override
Call to get the qubits involved in the gates stored in the block of gates.
void set_cost_function_variant(cost_function_type variant)
Call to set the variant of the cost function used in the calculations.
int get_parameter_num() override
Call to get the number of free parameters.
Matrix_float copy() const
Call to create a copy of the matrix.
Definition: matrix_float.h:60
void set_optimization_blocks(int optimization_block_in)
Call to set the number of gate blocks to be optimized in one shot.
int get_control_qbit()
Call to get the index of the control qubit.
Definition: Gate.cpp:1211
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41
std::mt19937 gen
Standard mersenne_twister_engine seeded with rd()
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
int omp_get_max_threads()
get the number of threads in MKL