Sequential Quantum Gate Decomposer  v1.9.6
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  Gates_block* gate_structure_loc = NULL;
516  if ( gates.size() > 0 ) {
517  std::stringstream sstream;
518  sstream << "Using imported gate structure for the compression." << std::endl;
519  print(sstream, 1);
520 
521  gate_structure_loc = static_cast<Gates_block*>(this)->clone();
522  }
523  else {
524  std::stringstream sstream;
525  sstream << "No circuit initalised." << std::endl;
526  print(sstream, 1);
527  return;
528  }
529 
530 
531  std::stringstream sstream;
532  sstream.str("");
533  sstream << "**************************************************************" << std::endl;
534  sstream << "************ Final tuning of the Gate structure **************" << std::endl;
535  sstream << "**************************************************************" << std::endl;
536  print(sstream, 1);
537 
538  // maximal number of inner iterations overriden by config
539  if ( config.count("optimization_tolerance") > 0 ) {
540  long long value;
541  config["optimization_tolerance"].get_property( value );
542  optimization_tolerance = (double)value;
543  }
544  else {optimization_tolerance = 1e-4;}
545 
546 
547 
549 
550 
551  sstream.str("");
552  sstream << "cost function value before replacing trivial CRY gates: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
553  print(sstream, 3);
554 
555  Gates_block* gate_structure_tmp = replace_trivial_CRY_gates( gate_structure_loc, optimized_parameters_mtx );
556  Matrix_real optimized_parameters_save = optimized_parameters_mtx;
557 
558  release_gates();
559  combine( gate_structure_tmp );
560 
561  sstream.str("");
562  sstream << "cost function value before final optimization: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
563  print(sstream, 3);
564 
565  release_gates();
566  optimized_parameters_mtx = optimized_parameters_save;
567 
568  // solve the optimization problem
569  N_Qubit_Decomposition_custom cDecomp_custom;
570 
571 
572  std::map<std::string, Config_Element> config_copy;
573  config_copy.insert(config.begin(), config.end());
574  if ( config.count("max_inner_iterations_final") > 0 ) {
575  long long val;
576  config["max_inner_iterations_final"].get_property( val );
577  Config_Element element;
578  element.set_property( "max_inner_iterations", val );
579  config_copy["max_inner_iterations"] = element;
580  }
581 
582 
583  // solve the optimization problem in isolated optimization process
584  if ( use_float ) {
585  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx_float.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
586  }
587  else {
588  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
589  }
590  cDecomp_custom.set_custom_gate_structure( gate_structure_tmp );
592  cDecomp_custom.set_optimization_blocks( gate_structure_loc->get_gate_num() );
593  cDecomp_custom.set_max_iteration( max_outer_iterations );
594  cDecomp_custom.set_verbose(verbose);
595  cDecomp_custom.set_cost_function_variant( cost_fnc );
596  cDecomp_custom.set_debugfile("");
597  cDecomp_custom.set_iteration_loops( iteration_loops );
599  cDecomp_custom.set_trace_offset( trace_offset );
600  cDecomp_custom.set_optimizer( alg );
601  if (alg==ADAM || alg==BFGS2) {
602  int param_num_loc = gate_structure_loc->get_parameter_num();
603  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
604  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
605  cDecomp_custom.set_random_shift_count_max( 10000 );
606  }
607  else if ( alg==ADAM_BATCHED ) {
608  cDecomp_custom.set_optimizer( alg );
609  int max_inner_iterations_loc = 2500;
610  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
611  cDecomp_custom.set_random_shift_count_max( 5 );
612  }
613  else if ( alg==BFGS ) {
614  cDecomp_custom.set_optimizer( alg );
615  int max_inner_iterations_loc = 10000;
616  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
617  }
618  cDecomp_custom.start_decomposition();
619  increment_num_iters(cDecomp_custom.get_num_iters());
620 
621  current_minimum = cDecomp_custom.get_current_minimum();
624 
625 
626  combine( gate_structure_tmp );
627  delete( gate_structure_tmp );
628  delete( gate_structure_loc );
629 
630  sstream.str("");
631  sstream << "cost function value after final optimization: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
632  print(sstream, 3);
633 
634 
635 
636  long long export_circuit_2_binary_loc;
637  if ( config.count("export_circuit_2_binary") > 0 ) {
638  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
639  }
640  else {
641  export_circuit_2_binary_loc = 0;
642  }
643 
644 
645  if ( export_circuit_2_binary_loc > 0 ) {
646  std::string filename2("circuit_final.binary");
647 
648  if (project_name != "") {
649  filename2=project_name+ "_" +filename2;
650  }
651 
653 
654  }
655 
657 
658 
659  sstream.str("");
660  sstream << "In the decomposition with error = " << decomposition_error << " were used " << layer_num << " gates with:" << std::endl;
661 
662  // get the number of gates used in the decomposition
663  std::map<std::string, int>&& gate_nums = get_gate_nums();
664 
665  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
666  sstream << it->second << " " << it->first << " gates" << std::endl;
667  }
668 
669  sstream << std::endl;
670  print(sstream, 1);
671 
672 #if BLAS==0 // undefined BLAS
674 #elif BLAS==1 //MKL
675  MKL_Set_Num_Threads(num_threads);
676 #elif BLAS==2 //OpenBLAS
677  openblas_set_num_threads(num_threads);
678 #endif
679 
680 }
681 
686 Gates_block*
688 
689  Gates_block* gate_structure_loc = (static_cast<Gates_block*>(this))->clone();
690 
691  //measure the time for the decompositin
692  tbb::tick_count start_time_loc = tbb::tick_count::now();
693 
694  std::stringstream sstream;
695  sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
696  print(sstream, 1);
697 
698  double optimization_tolerance_loc;
699  if ( config.count("optimization_tolerance") > 0 ) {
700  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
701  }
702  else {
703  optimization_tolerance_loc = optimization_tolerance;
704  }
705 
706  // solve the optimization problem
707  N_Qubit_Decomposition_custom cDecomp_custom;
708  // solve the optimization problem in isolated optimization process
709  if ( use_float ) {
711  }
712  else {
714  }
715  cDecomp_custom.set_custom_gate_structure( gate_structure_loc );
716  cDecomp_custom.set_optimized_parameters( optimized_parameters_mtx_loc.get_data(), optimized_parameters_mtx_loc.size() );
717  cDecomp_custom.set_optimization_blocks( gate_structure_loc->get_gate_num() );
718  cDecomp_custom.set_max_iteration( max_outer_iterations );
719  cDecomp_custom.set_verbose(verbose);
720  cDecomp_custom.set_cost_function_variant( cost_fnc );
721  cDecomp_custom.set_debugfile("");
722  cDecomp_custom.set_iteration_loops( iteration_loops );
723  cDecomp_custom.set_optimization_tolerance( optimization_tolerance_loc );
724  cDecomp_custom.set_trace_offset( trace_offset );
725  cDecomp_custom.set_optimizer( alg );
726  cDecomp_custom.set_project_name( project_name );
727  if (alg==ADAM || alg==BFGS2) {
728  int param_num_loc = gate_structure_loc->get_parameter_num();
729  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
730  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
731  cDecomp_custom.set_random_shift_count_max( 10000 );
732  }
733  else if ( alg==ADAM_BATCHED ) {
734  cDecomp_custom.set_optimizer( alg );
735  int max_inner_iterations_loc = 2500;
736  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
737  cDecomp_custom.set_random_shift_count_max( 5 );
738  }
739  else if ( alg==BFGS ) {
740  cDecomp_custom.set_optimizer( alg );
741  int max_inner_iterations_loc = 10000;
742  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
743  }
744  cDecomp_custom.start_decomposition();
745  increment_num_iters(cDecomp_custom.get_num_iters());
746  //cDecomp_custom.list_gates(0);
747 
748  tbb::tick_count end_time_loc = tbb::tick_count::now();
749 
750  current_minimum = cDecomp_custom.get_current_minimum();
751  optimized_parameters_mtx_loc = cDecomp_custom.get_optimized_parameters();
752 
753 
754 
755  if ( cDecomp_custom.get_current_minimum() < optimization_tolerance_loc ) {
756  std::stringstream sstream;
757  sstream << "Optimization problem solved with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
758  print(sstream, 1);
759  }
760  else {
761  std::stringstream sstream;
762  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;
763  print(sstream, 1);
764  }
765 
766  if (current_minimum > optimization_tolerance_loc) {
767  std::stringstream sstream;
768  sstream << "Decomposition did not reached prescribed high numerical precision." << std::endl;
769  print(sstream, 1);
770  optimization_tolerance_loc = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
771  }
772 
773  sstream.str("");
774  sstream << "Continue with the compression of gate structure consisting of " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
775  print(sstream, 1);
776  return gate_structure_loc;
777 
778 
779 
780 }
781 
786 Gates_block*
788 
789  // strages to store the optimized minimums in case of different cirquit depths
790  std::vector<double> minimum_vec;
791  std::vector<Gates_block*> gate_structure_vec;
792  std::vector<Matrix_real> optimized_parameters_vec;
793 
794  double optimization_tolerance_loc;
795  if ( config.count("optimization_tolerance") > 0 ) {
796  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
797  }
798  else {
799  optimization_tolerance_loc = optimization_tolerance;
800  }
801 
802  int max_outer_iterations_loc;
803  double value_placeholder;
804  if ( config.count("max_outer_iterations") > 0 ) {
805  config["max_outer_iterations"].get_property( value_placeholder );
806  max_outer_iterations_loc = (int) value_placeholder;
807  }
808  else {
809  max_outer_iterations_loc = max_outer_iterations;
810  }
811 
812 
813  int level = level_limit_min;
814  while ( current_minimum > optimization_tolerance_loc && level <= level_limit) {
815 
816  // create gate structure to be optimized
817  Gates_block* gate_structure_loc = new Gates_block(qbit_num);
818 
819  optimized_parameters_mtx_loc = Matrix_real(0,0);
820 
821  for (int idx=0; idx<level; idx++) {
822 
823  // create the new decomposing layer and add to the gate staructure
824  add_adaptive_layers( gate_structure_loc );
825 
826  }
827 
828  // add finalyzing layer to the top of the gate structure
829  add_finalyzing_layer( gate_structure_loc );
830 
831 
832  //measure the time for the decompositin
833  tbb::tick_count start_time_loc = tbb::tick_count::now();
834 
835 
836  N_Qubit_Decomposition_custom cDecomp_custom_random, cDecomp_custom_close_to_zero;
837 
838  std::stringstream sstream;
839  sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
840  print(sstream, 1);
841 
842 
843 /*
844 #ifndef __DFE__
845  // try the decomposition withrandom and with close to zero initial values
846  tbb::parallel_invoke(
847  [&]{
848 #endif
849 */
850  // solve the optimization problem in isolated optimization process
851  if ( use_float ) {
852  cDecomp_custom_random = N_Qubit_Decomposition_custom( Umtx_float.copy(), qbit_num, false, config, RANDOM, accelerator_num);
853  }
854  else {
855  cDecomp_custom_random = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config, RANDOM, accelerator_num);
856  }
857  cDecomp_custom_random.set_custom_gate_structure( gate_structure_loc );
858  cDecomp_custom_random.set_optimization_blocks( gate_structure_loc->get_gate_num() );
859  cDecomp_custom_random.set_max_iteration( max_outer_iterations_loc );
860 #ifndef __DFE__
861  cDecomp_custom_random.set_verbose(verbose);
862 #else
863  cDecomp_custom_random.set_verbose(0);
864 #endif
865  cDecomp_custom_random.set_cost_function_variant( cost_fnc );
866  cDecomp_custom_random.set_debugfile("");
867  cDecomp_custom_random.set_optimization_tolerance( optimization_tolerance_loc );
868  cDecomp_custom_random.set_trace_offset( trace_offset );
869  cDecomp_custom_random.set_optimizer( alg );
870  cDecomp_custom_random.set_project_name( project_name );
871  if ( alg == ADAM || alg == BFGS2 ) {
872  int param_num_loc = gate_structure_loc->get_parameter_num();
873  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
874  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
875  cDecomp_custom_random.set_random_shift_count_max( 10000 );
876  }
877  else if ( alg==ADAM_BATCHED ) {
878  cDecomp_custom_random.set_optimizer( alg );
879  int max_inner_iterations_loc = 2000;
880  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
881  cDecomp_custom_random.set_random_shift_count_max( 5 );
882  }
883  else if ( alg==BFGS ) {
884  cDecomp_custom_random.set_optimizer( alg );
885  int max_inner_iterations_loc = 10000;
886  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
887  }
888 
889 
890  cDecomp_custom_random.start_decomposition();
891 
892 
893 
894  increment_num_iters(cDecomp_custom_random.get_num_iters()); // retrive the number of iterations spent on optimization
895 /*
896 #ifndef __DFE__
897  },
898  [&]{
899  // solve the optimization problem in isolated optimization process
900  cDecomp_custom_close_to_zero = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, CLOSE_TO_ZERO);
901  cDecomp_custom_close_to_zero.set_custom_gate_structure( gate_structure_loc );
902  cDecomp_custom_close_to_zero.set_optimization_blocks( gate_structure_loc->get_gate_num() );
903  cDecomp_custom_close_to_zero.set_max_iteration( max_outer_iterations );
904  cDecomp_custom_close_to_zero.set_verbose(0);
905  cDecomp_custom_close_to_zero.set_cost_function_variant( cost_fnc );
906  cDecomp_custom_close_to_zero.set_debugfile("");
907  cDecomp_custom_close_to_zero.set_optimization_tolerance( optimization_tolerance );
908  cDecomp_custom_close_to_zero.set_optimizer( alg );
909  if ( alg == ADAM || alg == BFGS2 ) {
910  int param_num_loc = gate_structure_loc->get_parameter_num();
911  int max_inner_iterations_loc = static_cast<int>((double)param_num_loc/852 * 10000000.0);
912  cDecomp_custom_close_to_zero.set_max_inner_iterations( max_inner_iterations_loc );
913  cDecomp_custom_close_to_zero.set_random_shift_count_max( 10000 );
914  }
915  cDecomp_custom.close_to_zero.set_trace_offset( trace_offset );
916  cDecomp_custom_close_to_zero.start_decomposition(true);
917  increment_num_iters(cDecomp_custom_close_to_zero.get_num_iters());
918  }
919  );
920 #endif
921 */
922  tbb::tick_count end_time_loc = tbb::tick_count::now();
923 /*
924 #ifdef __DFE__
925 return NULL;
926 #endif
927 */
928  double current_minimum_random = cDecomp_custom_random.get_current_minimum();
929  double current_minimum_close_to_zero = cDecomp_custom_close_to_zero.get_current_minimum();
930  double current_minimum_loc;
931 
932 
933  // select between the results obtained for different initial value strategy
934  if ( current_minimum_random < optimization_tolerance_loc && current_minimum_close_to_zero > optimization_tolerance_loc ) {
935  current_minimum_loc = current_minimum_random;
936  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
938  }
939  else if ( current_minimum_random > optimization_tolerance_loc && current_minimum_close_to_zero < optimization_tolerance_loc ) {
940  current_minimum_loc = current_minimum_close_to_zero;
941  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
943  }
944  else if ( current_minimum_random < optimization_tolerance_loc && current_minimum_close_to_zero < optimization_tolerance_loc ) {
945  Matrix_real optimized_parameters_mtx_random = cDecomp_custom_random.get_optimized_parameters();
946  Matrix_real optimized_parameters_mtx_close_to_zero = cDecomp_custom_close_to_zero.get_optimized_parameters();
947 
948  int panelty_random = get_panelty(gate_structure_loc, optimized_parameters_mtx_random);
949  int panelty_close_to_zero = get_panelty(gate_structure_loc, optimized_parameters_mtx_close_to_zero );
950 
951  if ( panelty_random < panelty_close_to_zero ) {
952  current_minimum_loc = current_minimum_random;
953  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
955  }
956  else {
957  current_minimum_loc = current_minimum_close_to_zero;
958  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
960  }
961 
962  }
963  else {
964  if ( current_minimum_random < current_minimum_close_to_zero ) {
965  current_minimum_loc = current_minimum_random;
966  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
968  }
969  else {
970  current_minimum_loc = current_minimum_close_to_zero;
971  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
973  }
974 
975  }
976 
977  minimum_vec.push_back(current_minimum_loc);
978  gate_structure_vec.push_back(gate_structure_loc);
979  optimized_parameters_vec.push_back(optimized_parameters_mtx_loc);
980 
981 
982 
983  if ( current_minimum_loc < optimization_tolerance_loc ) {
984  std::stringstream sstream;
985  sstream << "Optimization problem solved with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
986  print(sstream, 1);
987  break;
988  }
989  else {
990  std::stringstream sstream;
991  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;
992  print(sstream, 1);
993  }
994 
995  level++;
996  }
997 
998 //exit(-1);
999 
1000  // find the best decomposition
1001  int idx_min = 0;
1002  double current_minimum = minimum_vec[0];
1003  for (int idx=1; idx<(int)minimum_vec.size(); idx++) {
1004  if( current_minimum > minimum_vec[idx] ) {
1005  idx_min = idx;
1006  current_minimum = minimum_vec[idx];
1007  }
1008  }
1009 
1010 
1011  Gates_block* gate_structure_loc = gate_structure_vec[idx_min];
1012  optimized_parameters_mtx_loc = optimized_parameters_vec[idx_min];
1013 
1014  // release unnecesarry data
1015  for (int idx=0; idx<(int)minimum_vec.size(); idx++) {
1016  if( idx == idx_min ) {
1017  continue;
1018  }
1019  delete( gate_structure_vec[idx] );
1020  }
1021  minimum_vec.clear();
1022  gate_structure_vec.clear();
1023  optimized_parameters_vec.clear();
1024 
1025 
1026 
1027  if (current_minimum > optimization_tolerance_loc) {
1028  std::stringstream sstream;
1029  sstream << "Decomposition did not reached prescribed high numerical precision." << std::endl;
1030  print(sstream, 1);
1031  optimization_tolerance_loc = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
1032  }
1033 
1034 
1035  return gate_structure_loc;
1036 
1037 }
1038 
1039 
1040 
1045 Gates_block*
1047 
1048 
1049 
1050  int layer_num_max;
1051  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
1052  if ( layer_num_orig < 50 ) layer_num_max = layer_num_orig;
1053  else if ( layer_num_orig < 60 ) layer_num_max = 4;
1054  else layer_num_max = 2;
1055  double optimization_tolerance_loc;
1056  if ( config.count("optimization_tolerance") > 0 ) {
1057  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
1058  }
1059  else {
1060  optimization_tolerance_loc = optimization_tolerance;
1061  }
1062 
1063  // random generator of integers
1064  std::uniform_int_distribution<> distrib_int(0, 5000);
1065  // create a list of layers to be tested for removal.
1066  std::vector<int> layers_to_remove;
1067  if (uncompressed_iter_num==0){
1068  layer_num_max = 5<layer_num_orig ? 5 : layer_num_orig;
1069  // create a list of layers to be tested for removal.
1070  std::vector<double> layers_parameters(layer_num_orig,15.0);
1071  std::vector<int> layers_idx_sorted(layer_num_orig,0);
1072  //layers_to_remove.reserve(layer_num_orig);
1073  for (int idx=0; idx<layer_num_orig;idx++){
1074  layers_parameters[idx] = extract_theta_from_layer(gate_structure,idx,optimized_parameters_mtx);
1075  layers_idx_sorted[idx] = idx;
1076  }
1077 
1078  std::iota(layers_idx_sorted.begin(),layers_idx_sorted.end(),0); //Initializing
1079  sort( layers_idx_sorted.begin(),layers_idx_sorted.end(), [&](int i,int j){return layers_parameters[i]<layers_parameters[j];} );
1080 
1081  for (int idx=0; idx<layer_num_max; idx++ ) { // TODO: see line 1558 to explain the -1
1082  layers_to_remove.push_back( layers_idx_sorted[idx]);
1083  }
1084  }
1085  else{
1086  layers_to_remove.reserve(layer_num_orig);
1087  for (int idx=0; idx<layer_num_orig; idx++ ) { // TODO: see line 1558 to explain the -1
1088  layers_to_remove.push_back(idx);
1089  }
1090 
1091 
1092  while ( (int)layers_to_remove.size() > layer_num_max ) {
1093  int remove_idx = distrib_int(gen) % layers_to_remove.size();
1094 
1095  layers_to_remove.erase( layers_to_remove.begin() + remove_idx );
1096  }
1097  }
1098 
1099 
1100 #ifdef __MPI__
1101  MPI_Bcast( &layers_to_remove[0], layers_to_remove.size(), MPI_INT, 0, MPI_COMM_WORLD);
1102 #endif
1103 
1104  // make a copy of the original unitary. (By removing trivial gates global phase might be added to the unitary)
1105  Matrix&& Umtx_orig = Umtx.copy();
1106 
1107  int panelties_num = layer_num_max < layer_num_orig ? layer_num_max : layer_num_orig;
1108 
1109  if ( panelties_num == 0 ) {
1110  return gate_structure;
1111  }
1112 
1113  // preallocate panelties associated with the number of remaining two-qubit controlled gates
1114  std::vector<unsigned int> panelties(panelties_num, 1<<31);
1115  std::vector<Gates_block*> gate_structures_vec(panelties_num, NULL);
1116  std::vector<double> current_minimum_vec(panelties_num, DBL_MAX);
1117  std::vector<int> iteration_num_vec(panelties_num, 0);
1118 
1119 
1120  std::vector<Matrix_real> optimized_parameters_vec(panelties_num, Matrix_real(0,0));
1121  std::vector<Matrix> Umtx_vec(panelties_num, Matrix(0,0));
1122 
1123 
1124 
1125  for (int idx=0; idx<panelties_num; idx++) {
1126 
1127  Umtx = Umtx_orig.copy();
1128 
1129  double current_minimum_loc = DBL_MAX;//current_minimum;
1130  int iteration_num = 0;
1131  Matrix_real optimized_parameters_loc = optimized_parameters_mtx.copy();
1132 
1133  Gates_block* gate_structure_reduced = compress_gate_structure( gate_structure, layers_to_remove[idx], optimized_parameters_loc, current_minimum_loc, iteration_num );
1134  if ( optimized_parameters_loc.size() == 0 ) {
1135  optimized_parameters_loc = optimized_parameters_mtx.copy();
1136  }
1137 
1138  // remove further adaptive gates if possible
1139  Gates_block* gate_structure_tmp;
1140  if ( gate_structure_reduced->get_gate_num() == gate_structure->get_gate_num() ) {
1141  gate_structure_tmp = gate_structure_reduced->clone();
1142  }
1143  else {
1144  gate_structure_tmp = remove_trivial_gates( gate_structure_reduced, optimized_parameters_loc, current_minimum_loc ); //TODO: reverse gate order
1145  }
1146 
1147  panelties[idx] = get_panelty(gate_structure_tmp, optimized_parameters_loc);
1148  gate_structures_vec[idx] = gate_structure_tmp;
1149  current_minimum_vec[idx] = current_minimum_loc;
1150  iteration_num_vec[idx] = iteration_num; // the accumulated number of optimization iterations
1151 
1152 
1153  optimized_parameters_vec[idx] = optimized_parameters_loc;
1154  Umtx_vec[idx] = Umtx;
1155 
1156 
1157  delete(gate_structure_reduced);
1158 
1159 #ifdef __DFE__
1160  if ( current_minimum_vec[idx] < optimization_tolerance_loc ) {
1161  break;
1162  }
1163 #endif
1164  }
1165 
1166 
1167 
1168  // determine the reduction with the lowest penalty
1169  unsigned int panelty_min = panelties[0];
1170  unsigned int idx_min = 0;
1171 
1172  for (size_t idx=0; idx<panelties.size(); idx++) {
1173  if ( panelty_min > panelties[idx] ) {
1174  panelty_min = panelties[idx];
1175  idx_min = static_cast<unsigned int>(idx);
1176  }
1177 
1178  else if ( panelty_min == panelties[idx] ) {
1179 
1180  if ( (distrib_int(gen) % 2) == 1 ) {
1181  idx_min = static_cast<unsigned int>(idx);
1182 
1183  panelty_min = panelties[idx];
1184  }
1185 
1186  }
1187 
1188  }
1189 
1190 #ifdef __MPI__
1191  MPI_Bcast( &idx_min, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);
1192 #endif
1193 
1194 
1195  // release gate structures other than the best one
1196  for (size_t idx=0; idx<panelties.size(); idx++) {
1197  if (idx==idx_min) {
1198  continue;
1199  }
1200 
1201 
1202  if ( gate_structures_vec[idx] == gate_structure) {
1203  continue;
1204  }
1205 
1206  if ( gate_structures_vec[idx] ) {
1207  delete( gate_structures_vec[idx] );
1208  gate_structures_vec[idx] = NULL;
1209  }
1210 
1211  }
1212 
1213 
1214  gate_structure = gate_structures_vec[idx_min];
1215  optimized_parameters_mtx = optimized_parameters_vec[idx_min];
1216  current_minimum = current_minimum_vec[idx_min];
1217  increment_num_iters(iteration_num_vec[idx_min]); // the total number of the accumulated optimization iterations
1218  Umtx = Umtx_vec[idx_min];
1219 
1220  int layer_num = gate_structure->get_gate_num();
1221 
1222  if ( layer_num < layer_num_orig+1 ) {
1223  std::stringstream sstream;
1224  sstream << "gate structure reduced from " << layer_num_orig+1 << " to " << layer_num << " decomposing layers" << std::endl;
1225  print(sstream, 1);
1226  }
1227  else {
1228  std::stringstream sstream;
1229  sstream << "gate structure kept at " << layer_num << " layers" << std::endl;
1230  print(sstream, 1);
1231  }
1232 
1233 
1234  return gate_structure;
1235 
1236 
1237 
1238 }
1239 
1248 Gates_block*
1249 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 ) {
1250 
1251  // create reduced gate structure without layer indexed by layer_idx
1252  Gates_block* gate_structure_reduced = gate_structure->clone();
1253  gate_structure_reduced->release_gate( layer_idx );
1254 
1255  Matrix_real parameters_reduced;
1256  if ( optimized_parameters.size() > 0 ) {
1257  parameters_reduced = create_reduced_parameters( gate_structure, optimized_parameters, layer_idx );
1258  }
1259  else {
1260  parameters_reduced = Matrix_real(0, 0);
1261  }
1262 
1263 
1264 
1265  N_Qubit_Decomposition_custom cDecomp_custom;
1266 
1267  std::map<std::string, Config_Element> config_copy;
1268  config_copy.insert(config.begin(), config.end());
1269  if ( config.count("max_inner_iterations_compression") > 0 ) {
1270  long long val;
1271  config["max_inner_iterations_compression"].get_property( val );
1272  Config_Element element;
1273  element.set_property( "max_inner_iterations", val );
1274  config_copy["max_inner_iterations"] = element;
1275  }
1276 
1277  double optimization_tolerance_loc;
1278  if ( config.count("optimization_tolerance") > 0 ) {
1279  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
1280  }
1281  else {
1282  optimization_tolerance_loc = optimization_tolerance;
1283  }
1284 
1285 
1286 
1287  // solve the optimization problem in isolated optimization process
1288  if ( use_float ) {
1289  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx_float.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
1290  }
1291  else {
1292  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
1293  }
1294  cDecomp_custom.set_custom_gate_structure( gate_structure_reduced );
1295  cDecomp_custom.set_optimized_parameters( parameters_reduced.get_data(), parameters_reduced.size() );
1296  cDecomp_custom.set_verbose(0);
1297  cDecomp_custom.set_cost_function_variant( cost_fnc );
1298  cDecomp_custom.set_debugfile("");
1299  cDecomp_custom.set_max_iteration( max_outer_iterations );
1300  cDecomp_custom.set_iteration_loops( iteration_loops );
1301  cDecomp_custom.set_optimization_blocks( gate_structure_reduced->get_gate_num() ) ;
1302  cDecomp_custom.set_optimization_tolerance( optimization_tolerance_loc );
1303  cDecomp_custom.set_trace_offset( trace_offset );
1304  cDecomp_custom.set_optimizer( alg );
1305  if ( alg == ADAM || alg==BFGS2) {
1306  cDecomp_custom.set_max_inner_iterations( 100000 );
1307  cDecomp_custom.set_random_shift_count_max( 1 );
1308  }
1309  else if ( alg==BFGS ) {
1310  cDecomp_custom.set_optimizer( alg );
1311  int max_inner_iterations_loc = 100;
1312  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
1313  }
1314  cDecomp_custom.start_decomposition();
1315  iteration_num = cDecomp_custom.get_num_iters();
1316  double current_minimum_tmp = cDecomp_custom.get_current_minimum();
1317 
1318  if ( current_minimum_tmp < optimization_tolerance_loc ) {
1319  //cDecomp_custom.list_gates(0);
1320  optimized_parameters = cDecomp_custom.get_optimized_parameters();
1321  current_minimum_loc = current_minimum_tmp;
1322  return gate_structure_reduced;
1323  }
1324 
1325 
1326  return gate_structure->clone();
1327 
1328 }
1329 
1330 
1336 unsigned int
1338 
1339 
1340  int panelty = 0;
1341 
1342  // iterate over the elements of the parameter array
1343  int parameter_idx = 0;
1344  int layer_num = gate_structure->get_gate_num();
1345  //for ( int layer_idx=layer_num-1; layer_idx>=0; layer_idx--) {
1346  for ( int layer_idx=0; layer_idx<layer_num; layer_idx++) {
1347 
1348  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate( layer_idx ) );
1349 
1350  int gate_num = layer->get_gate_num();
1351  //for( int gate_idx=gate_num-1; gate_idx>=0; gate_idx-- ) {
1352  for( int gate_idx=0; gate_idx<gate_num; gate_idx++ ) {
1353 
1354  Gate* gate = layer->get_gate( gate_idx );
1355 
1356  double parameter = optimized_parameters[parameter_idx];
1357  parameter_idx = parameter_idx + gate->get_parameter_num();
1358 
1359  if ( (gate->get_type() != ADAPTIVE_OPERATION) && (gate->get_type() != CROT_OPERATION)) {
1360  continue;
1361  }
1362 
1363  if (gate->get_type() == ADAPTIVE_OPERATION){
1364  if ( std::abs(std::sin(parameter)) < 0.999 && std::abs(std::cos(parameter)) < 1e-3 ) {
1365  // Condition of pure CNOT gate
1366  panelty += 1;
1367  }
1368  else if ( std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1369  // Condition of pure Identity gate
1370  //panelty++;
1371  }
1372  else {
1373  // Condition of controlled rotation gate
1374  panelty += 2;
1375  }
1376  }
1377  else{
1378  panelty +=1;
1379  }
1380 
1381  }
1382 
1383  }
1384 
1385 
1386  return panelty;
1387 
1388 
1389 }
1390 
1391 
1397 Gates_block*
1399 
1400  Gates_block* gate_structure_ret = new Gates_block(qbit_num);
1401 
1402  int layer_num = gate_structure->get_gate_num();
1403 
1404 /*
1405  std::map<std::string, Config_Element> config_copy;
1406  config_copy.insert(config.begin(), config.end());
1407  N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1408  cDecomp_custom.set_custom_gate_structure( gate_structure );
1409  std::cout << std::endl << "before removing trivial gate: " << cDecomp_custom.optimization_problem( optimized_parameters ) << std::endl;
1410 */
1411  int parameter_idx = 0;
1412  //for (int idx=layer_num-1; idx>=0; idx-- ) {
1413  for (int idx=0; idx<layer_num; idx++ ) {
1414 
1415  Gate* gate = gate_structure->get_gate(idx);
1416 
1417  if ( gate->get_type() != BLOCK_OPERATION ) {
1418  std::string err = "N_Qubit_Decomposition_adaptive::replace_trivial_adaptive_gates: Only block gates are accepted in this conversion.";
1419  throw( err );
1420  }
1421 
1422  Gates_block* block_op = static_cast<Gates_block*>(gate);
1423  //int param_num = gate->get_parameter_num();
1424 
1425 
1426  if ( true ) {//gate_structure->contains_adaptive_gate(idx) ) {
1427 
1428  Gates_block* layer = block_op->clone();
1429 
1430  //for ( int jdx=layer->get_gate_num()-1; jdx>=0; jdx-- ) {
1431  for ( int jdx=0; jdx<layer->get_gate_num(); jdx++ ) {
1432 
1433  Gate* gate_tmp = layer->get_gate(jdx);
1434  int param_num = gate_tmp->get_parameter_num();
1435 
1436 
1437  double parameter = optimized_parameters[parameter_idx];
1438  parameter = activation_function(parameter, 1);//limit_max);
1439 
1440 //std::cout << param[0] << " " << (gate_tmp->get_type() == ADAPTIVE_OPERATION) << " " << std::abs(std::sin(param[0])) << " " << 1+std::cos(param[0]) << std::endl;
1441 
1442 
1443  if ( gate_tmp->get_type() == ADAPTIVE_OPERATION && std::abs(std::sin(parameter)) > 0.999 && std::abs(std::cos(parameter)) < 1e-3) {
1444 
1445  // convert to CZ gate
1446  int target_qbit = gate_tmp->get_target_qbit();
1447  int control_qbit = gate_tmp->get_control_qbit();
1448  layer->release_gate( jdx );
1449 
1450  RX* rx_gate_1 = new RX(qbit_num, target_qbit);
1451  CZ* cz_gate = new CZ(qbit_num, target_qbit, control_qbit);
1452  RX* rx_gate_2 = new RX(qbit_num, target_qbit);
1453  RZ* rz_gate = new RZ(qbit_num, control_qbit);
1454 
1455  Gates_block* czr_gate = new Gates_block(qbit_num);
1456  czr_gate->add_gate(rx_gate_1);
1457  czr_gate->add_gate(cz_gate);
1458  czr_gate->add_gate(rx_gate_2);
1459  czr_gate->add_gate(rz_gate);
1460 
1461  layer->insert_gate( (Gate*)czr_gate, jdx);
1462 
1463  Matrix_real parameters_new(1, optimized_parameters.size()+2);
1464 
1465 
1466  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1467 
1468  memcpy(parameters_new.get_data()+parameter_idx+3, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1469 
1470  parameters_new[parameter_idx] = -M_PI/4; // rx_1 parameter
1471  parameters_new[parameter_idx+1] = M_PI/4; // rx_2 parameter
1472 
1473 
1474  if ( std::sin(parameter) < 0 ) {
1475 // parameters_new[parameter_idx+2] = -M_PI/2;
1476  parameters_new[parameter_idx+2] = -M_PI/4; // rz parameter
1477 
1478  QGD_Complex16 global_phase_factor_new;
1479  global_phase_factor_new.real = std::cos( -M_PI/4 );
1480  global_phase_factor_new.imag = std::sin( -M_PI/4 );
1481  apply_global_phase_factor(global_phase_factor_new, Umtx);
1482 
1483  }
1484  else{
1485 // parameters_new[parameter_idx+2] = M_PI/2;
1486  parameters_new[parameter_idx+2] = M_PI/4; // rz parameter
1487 
1488  QGD_Complex16 global_phase_factor_new;
1489  global_phase_factor_new.real = std::cos( M_PI/4 );
1490  global_phase_factor_new.imag = std::sin( M_PI/4 );
1491  apply_global_phase_factor(global_phase_factor_new, Umtx);
1492 
1493  }
1494 
1495 
1496  optimized_parameters = parameters_new;
1497  parameter_idx += 3;
1498 
1499 
1500 
1501  }
1502 
1503  else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION && std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1504  // release trivial gate
1505 
1506  layer->release_gate( jdx );
1507  //jdx--;
1508  Matrix_real parameters_new(1, optimized_parameters.size()-1);
1509  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1510  memcpy(parameters_new.get_data()+parameter_idx, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1511  optimized_parameters = parameters_new;
1512 
1513 
1514  }
1515 
1516  else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION ) {
1517 
1518  // controlled Y rotation decomposed into 2 CNOT gates
1519  int target_qbit = gate_tmp->get_target_qbit();
1520  int control_qbit = gate_tmp->get_control_qbit();
1521  layer->release_gate( jdx );
1522 
1523  RY* ry_gate_1 = new RY(qbit_num, target_qbit);
1524  CNOT* cnot_gate_1 = new CNOT(qbit_num, target_qbit, control_qbit);
1525  RY* ry_gate_2 = new RY(qbit_num, target_qbit);
1526  CNOT* cnot_gate_2 = new CNOT(qbit_num, target_qbit, control_qbit);
1527 
1528  Gates_block* czr_gate = new Gates_block(qbit_num);
1529  czr_gate->add_gate(ry_gate_1);
1530  czr_gate->add_gate(cnot_gate_1);
1531  czr_gate->add_gate(ry_gate_2);
1532  czr_gate->add_gate(cnot_gate_2);
1533 
1534  layer->insert_gate( (Gate*)czr_gate, jdx);
1535 
1536  Matrix_real parameters_new(1, optimized_parameters.size()+1);
1537  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1538  memcpy(parameters_new.get_data()+parameter_idx+2, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1539  optimized_parameters = parameters_new;
1540 
1541  optimized_parameters[parameter_idx] = parameter/2; // ry_1 parameter
1542  optimized_parameters[parameter_idx+1] = -parameter/2; // ry_2 parameter
1543 
1544 
1545  parameter_idx += 2;
1546 
1547  }
1548 
1549  else {
1550 
1551  parameter_idx += param_num;
1552 
1553  }
1554 
1555 
1556 
1557  }
1558 
1559  gate_structure_ret->add_gate((Gate*)layer);
1560 
1561 
1562  }
1563 
1564  }
1565 
1566 /*
1567  N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1568  cDecomp_custom_.set_custom_gate_structure( gate_structure_ret );
1569  std::cout << std::endl << "after removing trivial gate: " << cDecomp_custom_.optimization_problem( optimized_parameters ) << std::endl;
1570  exit(2);
1571 */
1572  return gate_structure_ret;
1573 
1574 
1575 }
1576 
1583 Gates_block*
1584 N_Qubit_Decomposition_adaptive::remove_trivial_gates( Gates_block* gate_structure, Matrix_real& optimized_parameters, double& current_minimum_loc ) {
1585 
1586  int layer_num = gate_structure->get_gate_num();
1587 
1588  Matrix_real&& optimized_parameters_loc = optimized_parameters.copy();
1589 
1590  Gates_block* gate_structure_loc = gate_structure->clone();
1591 
1592 
1593  for (int idx=0; idx<layer_num; idx++ ) {
1594 
1595  Gates_block* layer = static_cast<Gates_block*>( gate_structure_loc->get_gate(idx) );
1596 
1597 
1598  Gate* gate_adaptive = layer->get_gate(2);
1599  double parameter = optimized_parameters_loc[layer->get_parameter_start_idx() + gate_adaptive->get_parameter_start_idx()]; // parameter for adaptive gate
1600  parameter = activation_function(parameter, 1);//limit_max);
1601 
1602  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 ) {
1603  /*
1604  optimized_parameters_loc[parameter_idx+6] = 0.0;
1605  std::map<std::string, Config_Element> config_copy;
1606  config_copy.insert(config.begin(), config.end());
1607  N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1608  cDecomp_custom.set_custom_gate_structure( gate_structure_loc );
1609  std::cout << std::endl << "before removing trivial gate: " << cDecomp_custom.optimization_problem( optimized_parameters_loc ) << std::endl;
1610  */
1611 
1612  int parameter_idx_to_be_removed = layer->get_parameter_start_idx();
1613 
1614 
1615  // find matching U3 gates into which the U3 gates in the current layer are merged
1616  std::vector<int>&& involved_qbits = layer->get_involved_qubits();
1617  for( size_t rdx=0; rdx<involved_qbits.size(); rdx++ ) {
1618 
1619  U3* U_gate_to_be_removed = static_cast<U3*>(layer->get_gate(static_cast<int>(rdx)));
1620  int qbit_to_be_matched = U_gate_to_be_removed->get_target_qbit();
1621 
1622  int parameter_idx_loc = parameter_idx_to_be_removed + layer->get_parameter_num();
1623 
1624  bool found_match = false;
1625  U3* matching_gate = NULL;
1626 
1627  // iterate over subsequent layers to find the maching gate
1628  for ( int kdx=idx+1; kdx<layer_num; kdx++ ) {
1629 
1630  Gates_block* layer_test = static_cast<Gates_block*>( gate_structure_loc->get_gate(kdx) );
1631 
1632  // iterate over the gates in the tested layer
1633  int gate_num = layer_test->get_gate_num();
1634  for ( int hdx=0; hdx<gate_num; hdx++ ) {
1635 
1636  Gate* gate_test = layer_test->get_gate(hdx);
1637 
1638  if ( gate_test->get_type() == U3_OPERATION ) {
1639  int target_qbit_loc = gate_test->get_target_qbit();
1640 
1641  if ( qbit_to_be_matched == target_qbit_loc ) {
1642  found_match = true;
1643  matching_gate = static_cast<U3*>(gate_test);
1644  parameter_idx_loc = layer_test->get_parameter_start_idx()+matching_gate->get_parameter_start_idx();
1645  break;
1646  }
1647 
1648 
1649  }
1650 
1651  }
1652 
1653  if ( found_match ) break;
1654 
1655 
1656  }
1657 
1658  if ( found_match == false ) {
1659  // TODO: append a matching U3 gate to the very end of the circuit
1660  std::string err("N_Qubit_Decomposition_adaptive::remove_trivial_gates: No matching U3 gate was found. Need to append a U3 gate to the end, but this functionality is not developed yet.");
1661  throw err;
1662  }
1663 
1664  Matrix_real param1( &optimized_parameters_loc[parameter_idx_to_be_removed], 1, U_gate_to_be_removed->get_parameter_num() );
1665  Matrix U3_matrix1 = Gate::calc_one_qubit_u3(param1[0], param1[1], param1[2]);
1666 
1667  Matrix_real param2( &optimized_parameters_loc[parameter_idx_loc], 1, matching_gate->get_parameter_num() );
1668  Matrix U3_matrix2 = Gate::calc_one_qubit_u3(param2[0], param2[1], param2[2]);
1669 
1670  Matrix U3_prod = dot(U3_matrix2, U3_matrix1);
1671 
1672  optimized_parameters_loc[parameter_idx_to_be_removed] = 0.0;
1673  optimized_parameters_loc[parameter_idx_to_be_removed+1] = 0.0;
1674  optimized_parameters_loc[parameter_idx_to_be_removed+2] = 0.0;
1675  parameter_idx_to_be_removed = parameter_idx_to_be_removed + U_gate_to_be_removed->get_parameter_num();
1676 
1677  // calculate the new theta/2, phi, lambda parameters from U3_prod, and replace them in param2
1678  // global phase on Umtx
1679  double ctheta3_over2 = std::sqrt(U3_prod[0].real*U3_prod[0].real+U3_prod[0].imag*U3_prod[0].imag); // cos( theta/2 )
1680  double stheta3_over2 = std::sqrt(U3_prod[2].real*U3_prod[2].real+U3_prod[2].imag*U3_prod[2].imag); // sin( theta/2 )
1681  double theta3_over2 = std::atan2(stheta3_over2,ctheta3_over2); // theta/2
1682 
1683  double alpha = std::atan2(U3_prod[0].imag,U3_prod[0].real); // the global phase
1684 
1685  double lambda3;
1686  double phi3;
1687 
1688  if (std::abs(stheta3_over2)<4e-8){
1689  lambda3 = (std::atan2(U3_prod[3].imag,U3_prod[3].real)-alpha)/2;
1690  phi3 = lambda3;
1691  }
1692  else {
1693  lambda3 = std::atan2(-1*U3_prod[1].imag,-1*U3_prod[1].real)-alpha;
1694  phi3 = std::atan2(U3_prod[2].imag,U3_prod[2].real)-alpha;
1695  }
1696 
1697  // the product U3 matrix
1698  Matrix U3_new = Gate::calc_one_qubit_u3(theta3_over2, phi3, lambda3);
1699  QGD_Complex16 global_phase_factor_new;
1700  global_phase_factor_new.real = std::cos(alpha);
1701  global_phase_factor_new.imag = std::sin(alpha);
1702  apply_global_phase_factor(global_phase_factor_new, U3_new);
1703 
1704  // test for the product U3 matrix
1705  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) {
1706 
1707  // setting the resulting parameters if test passed
1708 
1709  param2[0] = theta3_over2;
1710  param2[1] = phi3;
1711  param2[2] = lambda3;
1712  apply_global_phase_factor(global_phase_factor_new, Umtx);
1713 
1714  }
1715  /*
1716  N_Qubit_Decomposition_custom cDecomp_custom__( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1717  cDecomp_custom__.set_custom_gate_structure( gate_structure_loc );
1718  std::cout << "right before removing a trivial gate: " << cDecomp_custom__.optimization_problem( optimized_parameters_loc ) << std::endl;
1719  */
1720  }
1721 
1722 
1723  std::stringstream sstream;
1724  sstream << "N_Qubit_Decomposition_adaptive::remove_trivial_gates: Removing trivial gateblock" << std::endl;
1725  print(sstream, 3);
1726 
1727 
1728  // remove gate from the structure
1729  int iteration_num_loc = 0;
1730  Gates_block* gate_structure_tmp = compress_gate_structure( gate_structure_loc, idx, optimized_parameters_loc, current_minimum_loc, iteration_num_loc );
1731  increment_num_iters(iteration_num_loc);
1732 
1733  /*
1734  N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1735  cDecomp_custom_.set_custom_gate_structure( gate_structure_tmp );
1736  std::cout << "after removing a trivial gate: " << cDecomp_custom_.optimization_problem( optimized_parameters_loc ) << std::endl;
1737  */
1738  optimized_parameters = optimized_parameters_loc;
1739  delete( gate_structure_loc );
1740  gate_structure_loc = gate_structure_tmp;
1741  layer_num = gate_structure_loc->get_gate_num();
1742  break;
1743 
1744 
1745 
1746 
1747  }
1748 
1749 
1750 
1751  }
1752 
1753 //std::cout << "N_Qubit_Decomposition_adaptive::remove_trivial_gates :" << gate_structure->get_gate_num() << " reduced to " << gate_structure_loc->get_gate_num() << std::endl;
1754  return gate_structure_loc;
1755 
1756 
1757 
1758 
1759 }
1760 
1761 
1768 Matrix_real
1770 
1771 
1772  // determine the index of the parameter that is about to delete
1773  int parameter_idx = 0;
1774  for ( int idx=0; idx<layer_idx; idx++) {
1775  //for ( int idx=gates_num-1; idx>layer_idx; idx--) {
1776  Gate* gate = gate_structure->get_gate( idx );
1777  parameter_idx += gate->get_parameter_num();
1778  }
1779 
1780 
1781  Gate* gate = gate_structure->get_gate( layer_idx );
1782  int param_num_removed = gate->get_parameter_num();
1783 
1784  Matrix_real reduced_parameters(1, optimized_parameters.size() - param_num_removed );
1785  memcpy( reduced_parameters.get_data(), optimized_parameters.get_data(), (parameter_idx)*sizeof(double));
1786  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));
1787 
1788 
1789  return reduced_parameters;
1790 }
1791 
1792 
1793 
1794 
1795 
1796 
1797 
1801 void
1803 
1804  add_adaptive_layers( this );
1805 
1806 }
1807 
1811 void
1813 
1814 
1815  // create the new decomposing layer and add to the gate staructure
1817  gate_structure->combine( layer );
1818 
1819 
1820 }
1821 
1822 
1823 
1824 
1828 Gates_block*
1830 
1831 
1832  //The stringstream input to store the output messages.
1833  std::stringstream sstream;
1834 
1835  // creating block of gates
1836  Gates_block* block = new Gates_block( qbit_num );
1837 
1838  std::vector<Gates_block* > layers;
1839 
1840 
1841  if ( topology.size() > 0 ) {
1842  for ( std::vector<matrix_base<int>>::iterator it=topology.begin(); it!=topology.end(); it++) {
1843 
1844  if ( it->size() != 2 ) {
1845  std::string err("The connectivity data should contains two qubits.");
1846  throw err;
1847  }
1848 
1849  int control_qbit_loc = (*it)[0];
1850  int target_qbit_loc = (*it)[1];
1851 
1852  if ( control_qbit_loc >= qbit_num || target_qbit_loc >= qbit_num ) {
1853  std::string err("Label of control/target qubit should be less than the number of qubits in the register.");
1854  throw err;
1855  }
1856 
1857  Gates_block* layer = new Gates_block( qbit_num );
1858 
1859  layer->add_u3(target_qbit_loc);
1860  layer->add_u3(control_qbit_loc);
1861  layer->add_adaptive(target_qbit_loc, control_qbit_loc);
1862 
1863  layers.push_back(layer);
1864 
1865 
1866  }
1867  }
1868  else {
1869 
1870  // sequ
1871  for (int target_qbit_loc = 0; target_qbit_loc<qbit_num; target_qbit_loc++) {
1872  for (int control_qbit_loc = target_qbit_loc+1; control_qbit_loc<qbit_num; control_qbit_loc++) {
1873 
1874  Gates_block* layer = new Gates_block( qbit_num );
1875 
1876  layer->add_u3(target_qbit_loc);
1877  layer->add_u3(control_qbit_loc);
1878  layer->add_adaptive(target_qbit_loc, control_qbit_loc);
1879 
1880  layers.push_back(layer);
1881  }
1882  }
1883 
1884  }
1885 
1886 /*
1887  for (int idx=0; idx<layers.size(); idx++) {
1888  Gates_block* layer = (Gates_block*)layers[idx];
1889  block->add_gate( layers[idx] );
1890 
1891  }
1892 */
1893 
1894  bool randomized_adaptive_layers_loc;
1895  if ( config.count("randomized_adaptive_layers") > 0 ) {
1896  config["randomized_adaptive_layers"].get_property( randomized_adaptive_layers_loc );
1897  }
1898  else {
1899  randomized_adaptive_layers_loc = randomized_adaptive_layers;
1900  }
1901 
1902 
1903  // make difference between randomized adaptive layers and deterministic one
1904  if (randomized_adaptive_layers_loc) {
1905 
1906  std::uniform_int_distribution<> distrib_int(0, 5000);
1907 
1908  while (layers.size()>0) {
1909  int idx = distrib_int(gen) % layers.size();
1910 
1911 #ifdef __MPI__
1912  MPI_Bcast( &idx, 1, MPI_INT, 0, MPI_COMM_WORLD);
1913 #endif
1914  block->add_gate( layers[idx] );
1915  layers.erase( layers.begin() + idx );
1916  }
1917 
1918  }
1919  else {
1920  while (layers.size()>0) {
1921  block->add_gate( layers[0] );
1922  layers.erase( layers.begin() );
1923  }
1924 
1925  }
1926 
1927 
1928  return block;
1929 
1930 
1931 }
1932 
1933 
1937 void
1939 
1940  add_finalyzing_layer( this );
1941 
1942 }
1943 
1947 void
1949 
1950 
1951  // creating block of gates
1952  Gates_block* block = new Gates_block( qbit_num );
1953 
1954  for (int idx=0; idx<qbit_num; idx++) {
1955  block->add_u3(idx);
1956 // block->add_ry(idx);
1957  }
1958 
1959 
1960  // adding the opeartion block to the gates
1961  if ( gate_structure == NULL ) {
1962  throw ("N_Qubit_Decomposition_adaptive::add_finalyzing_layer: gate_structure is null pointer");
1963  }
1964  else {
1965  gate_structure->add_gate( block );
1966  }
1967 
1968 
1969 }
1970 
1971 
1972 
1973 
1978 void
1980 
1981  if ( gates.size() > 0 ) {
1982  release_gates();
1985  }
1986 
1988  combine( gate_structure );
1989  delete gate_structure;
1990 
1991 }
1992 
1997 void
1999 
2000  Umtx = import_unitary_from_binary(filename);
2001 
2002 #ifdef __DFE__
2003  if( qbit_num >= 5 ) {
2004  upload_Umtx_to_DFE();
2005  }
2006 #endif
2007 
2008 }
2013 void
2015 
2016  Umtx = Umtx_new;
2017  if ( use_float ) {
2018  Umtx_float = Umtx_new.to_float32();
2019  }
2020 
2021 #ifdef __DFE__
2022  if( qbit_num >= 5 ) {
2023  upload_Umtx_to_DFE();
2024  }
2025 #endif
2026 
2027 }
2028 
2029 void
2031 
2032  Umtx_float = Umtx_new;
2033  Umtx = Umtx_new.to_float64();
2034  use_float = true;
2035 
2036 #ifdef __DFE__
2037  if( qbit_num >= 5 ) {
2038  upload_Umtx_to_DFE();
2039  }
2040 #endif
2041 
2042 }
2043 
2048 void
2050 
2051 
2052 
2053  Matrix_real optimized_parameters_mtx_tmp;
2054  Gates_block* gate_structure_tmp = import_gate_list_from_binary(optimized_parameters_mtx_tmp, filename, verbose);
2055 
2056  if ( gates.size() > 0 ) {
2057  gate_structure_tmp->combine( static_cast<Gates_block*>(this) );
2058 
2059  release_gates();
2060  combine( gate_structure_tmp );
2061 
2062 
2063  Matrix_real optimized_parameters_mtx_tmp2( 1, optimized_parameters_mtx_tmp.size() + optimized_parameters_mtx.size() );
2064 
2065  memcpy( optimized_parameters_mtx_tmp2.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
2066  memcpy( optimized_parameters_mtx_tmp2.get_data()+optimized_parameters_mtx.size(), optimized_parameters_mtx_tmp.get_data(), optimized_parameters_mtx_tmp.size()*sizeof(double) );
2067 
2068  optimized_parameters_mtx = optimized_parameters_mtx_tmp2;
2069  }
2070  else {
2071  combine( gate_structure_tmp );
2072  optimized_parameters_mtx = optimized_parameters_mtx_tmp;
2073  }
2074 
2075 }
2076 
2077 
2078 
2082 void
2084 
2085  if ( gates.size() == 0 ) {
2086  return;
2087  }
2088 
2089 
2090  std::stringstream sstream;
2091  sstream << "The cost function before applying the imported gate structure is:" << optimization_problem( optimized_parameters_mtx ) << std::endl;
2092 
2093  if ( use_float ) {
2094  Matrix_real_float optimized_parameters_float(1, optimized_parameters_mtx.size());
2095  for (int pidx=0; pidx<optimized_parameters_mtx.size(); pidx++) {
2096  optimized_parameters_float[pidx] = static_cast<float>(optimized_parameters_mtx[pidx]);
2097  }
2098  apply_to( optimized_parameters_float, Umtx_float );
2100  }
2101  else {
2103  }
2104  release_gates();
2107 
2108 
2109  sstream << "The cost function after applying the imported gate structure is:" << optimization_problem( optimized_parameters_mtx ) << std::endl;
2110  print(sstream, 3);
2111 
2112 
2113 
2114 }
2115 
2116 
2121 void
2123 
2124 
2125  std::stringstream sstream;
2126  sstream << "Add new layer to the adaptive gate structure." << std::endl;
2127  print(sstream, 2);
2128 
2130 
2131 
2132  combine( layer );
2133 
2135  memset( tmp.get_data(), 0, tmp.size()*sizeof(double) );
2136  memcpy( tmp.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
2137 
2138  optimized_parameters_mtx = tmp;
2139 
2140 }
2141 
2142 double
2144 
2145  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate(layer_idx) );
2146  int layer_start_idx = layer->get_parameter_start_idx();
2147  int layer_gate_num = layer->get_gate_num();
2148  double ThetaOver2=0.;
2149  for (int gate_idx=0; gate_idx<layer_gate_num; gate_idx++){
2150  Gate* gate_tmp = layer->get_gate(gate_idx);
2151  double parameter = optimized_parameters[layer_start_idx+gate_tmp->get_parameter_start_idx()];
2152  if (gate_tmp->get_type() == ADAPTIVE_OPERATION || gate_tmp->get_type() == CROT_OPERATION){
2153  ThetaOver2 = std::sin(parameter)*std::sin(parameter);
2154  break;
2155  }
2156  }
2157  return ThetaOver2;
2158 }
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.
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...
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.
virtual Gates_block * remove_trivial_gates(Gates_block *gate_structure, Matrix_real &optimized_parameters, double &currnt_minimum_loc)
Call to remove those blocks from the circuit that contain a trivial CRY gate (i.e.
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
Gates_block * replace_trivial_CRY_gates(Gates_block *gate_structure, Matrix_real &optimized_parameters)
Call to replace CRY gates in the circuit that are close to either an identity or to a CNOT gate...
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