Sequential Quantum Gate Decomposer  v1.9.3
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 
34 
35 #ifdef __DFE__
36 #include "common_DFE.h"
37 #endif
38 
39 
40 
41 
42 
48 
49 
50  // set the level limit
51  level_limit = 0;
52 
53 
54 
55  // BFGS is better for smaller problems, while ADAM for larger ones
56  if ( qbit_num <= 5 ) {
58 
59  // Maximal number of iteartions in the optimization process
61  max_inner_iterations = 10000;
62  }
63  else {
65 
66  // Maximal number of iteartions in the optimization process
68  }
69 
70 
71  // Boolean variable to determine whether randomized adaptive layers are used or not
73 
74 
75 }
76 
86 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) {
87 
88 
89  // set the level limit
90  level_limit = level_limit_in;
91  level_limit_min = level_limit_min_in;
92 
93  // BFGS is better for smaller problems, while ADAM for larger ones
94  if ( qbit_num <= 5 ) {
96 
97  // Maximal number of iteartions in the optimization process
99 
100  max_inner_iterations = 10000;
101 
102  }
103  else {
104  set_optimizer( ADAM );
105 
106  // Maximal number of iteartions in the optimization process
108 
109  }
110 
111  // Boolean variable to determine whether randomized adaptive layers are used or not
113 
114 
115 }
116 
117 
118 
128 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) {
129 
130 
131 
132  // set the level limit
133  level_limit = level_limit_in;
134  level_limit_min = level_limit_min_in;
135 
136  // Maximal number of iteartions in the optimization process
138 
139 
140  // setting the topology
141  topology = topology_in;
142 
143 
144 
145 
146  // BFGS is better for smaller problems, while ADAM for larger ones
147  if ( qbit_num <= 5 ) {
148  alg = BFGS;
149 
150  // Maximal number of iteartions in the optimization process
152  max_inner_iterations = 10000;
153  }
154  else {
155  alg = ADAM;
156 
157  // Maximal number of iteartions in the optimization process
159  }
160 
161  // Boolean variable to determine whether randomized adaptive layers are used or not
163 
164 }
165 
170 
171 }
172 
173 
174 
179 void
181 
182 
183  //The stringstream input to store the output messages.
184  std::stringstream sstream;
185  sstream << "***************************************************************" << std::endl;
186  sstream << "Starting to disentangle " << qbit_num << "-qubit matrix" << std::endl;
187  sstream << "***************************************************************" << std::endl << std::endl << std::endl;
188 
189  print(sstream, 1);
190 
191 
192 
193  // get the initial circuit including redundand 2-qbit blocks.
195 
196  // comppress the gate structure
198 
199 
200  // finalyzing the gate structure by turning CRY gates inti CNOT gates and do optimization cycles to correct approximation in this transformation
201  // (CRY gates with small rotation angles are expressed with a single CNOT gate
203 
204 }
205 
206 
207 
208 
209 
214 // temporarily turn off OpenMP parallelism
215 #if BLAS==0 // undefined BLAS
218 #elif BLAS==1 // MKL
219  num_threads = mkl_get_max_threads();
220  MKL_Set_Num_Threads(1);
221 #elif BLAS==2 //OpenBLAS
222  num_threads = openblas_get_num_threads();
223  openblas_set_num_threads(1);
224 #endif
225  //measure the time for the decompositin
226  tbb::tick_count start_time = tbb::tick_count::now();
227 
228  if (level_limit == 0 ) {
229  std::stringstream sstream;
230  sstream << "please increase level limit" << std::endl;
231  print(sstream, 0);
232  return;
233  }
234 
235 
236 
237 
238  Gates_block* gate_structure_loc = NULL;
239  if ( gates.size() > 0 ) {
240  std::stringstream sstream;
241  sstream << "Using imported gate structure for the decomposition." << std::endl;
242  print(sstream, 1);
244  }
245  else {
246  std::stringstream sstream;
247  sstream << "Construct initial gate structure for the decomposition." << std::endl;
248  print(sstream, 1);
250  }
251 
252 
253  long long export_circuit_2_binary_loc;
254  if ( config.count("export_circuit_2_binary") > 0 ) {
255  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
256  }
257  else {
258  export_circuit_2_binary_loc = 0;
259  }
260 
261 
262  if ( export_circuit_2_binary_loc > 0 ) {
263  std::string filename("circuit_squander.binary");
264  if (project_name != "") {
265  filename = project_name+ "_" +filename;
266  }
267  export_gate_list_to_binary(optimized_parameters_mtx, gate_structure_loc, filename, verbose);
268 
269  std::string unitaryname("unitary_squander.binary");
270  if (project_name != "") {
271  filename = project_name+ "_" +unitaryname;
272  }
273  export_unitary(unitaryname);
274 
275  }
276 
277  // store the created gate structure
278  release_gates();
279  combine( gate_structure_loc );
280  delete( gate_structure_loc );
281 
282 
283 #if BLAS==0 // undefined BLAS
285 #elif BLAS==1 //MKL
286  MKL_Set_Num_Threads(num_threads);
287 #elif BLAS==2 //OpenBLAS
288  openblas_set_num_threads(num_threads);
289 #endif
290 }
291 
292 
293 
294 
299 
300 // temporarily turn off OpenMP parallelism
301 #if BLAS==0 // undefined BLAS
304 #elif BLAS==1 // MKL
305  num_threads = mkl_get_max_threads();
306  MKL_Set_Num_Threads(1);
307 #elif BLAS==2 //OpenBLAS
308  num_threads = openblas_get_num_threads();
309  openblas_set_num_threads(1);
310 #endif
311 
312  std::stringstream sstream;
313  sstream.str("");
314  sstream << std::endl;
315  sstream << std::endl;
316  sstream << "**************************************************************" << std::endl;
317  sstream << "***************** Compressing Gate structure *****************" << std::endl;
318  sstream << "**************************************************************" << std::endl;
319  print(sstream, 1);
320  Gates_block* gate_structure_loc = NULL;
321  if ( gates.size() > 0 ) {
322  std::stringstream sstream;
323  sstream << "Using imported gate structure for the compression." << std::endl;
324  print(sstream, 1);
325 
326  gate_structure_loc = static_cast<Gates_block*>(this)->clone();
327  }
328  else {
329  std::stringstream sstream;
330  sstream << "No circuit initalised." << std::endl;
331  print(sstream, 1);
332  return;
333  }
334 
335  sstream.str("");
336  sstream << "Compressing gate structure consisting of " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
337  print(sstream, 1);
338  sstream.str("");
339 
340 
341  int iter = 0;
342  int uncompressed_iter_num = 0;
343 
344  long long export_circuit_2_binary_loc;
345  if ( config.count("export_circuit_2_binary") > 0 ) {
346  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
347  }
348  else {
349  export_circuit_2_binary_loc = 0;
350  }
351 
352 
353  while ( iter<25 || uncompressed_iter_num <= 5 ) {
354  std::stringstream sstream;
355  sstream.str("");
356  sstream << "iteration " << iter+1 << ": ";
357  print(sstream, 1);
358  Gates_block* gate_structure_compressed;
359 
360  gate_structure_compressed = compress_gate_structure( gate_structure_loc,uncompressed_iter_num );
361  if ( gate_structure_compressed->get_gate_num() < gate_structure_loc->get_gate_num() ) {
362  uncompressed_iter_num = 0;
363  }
364  else {
365  uncompressed_iter_num++;
366  }
367 
368  if ( gate_structure_compressed != gate_structure_loc ) {
369 
370  delete( gate_structure_loc );
371  gate_structure_loc = gate_structure_compressed;
372  gate_structure_compressed = NULL;
373 
374 
375 
376  if ( export_circuit_2_binary_loc > 0 ) {
377  std::string filename("circuit_compression.binary");
378  if (project_name != "") {
379  filename=project_name+ "_" +filename;
380  }
381  export_gate_list_to_binary(optimized_parameters_mtx, gate_structure_loc, filename, verbose);
382 
383 
384  std::string filename_unitary("unitary_compression.binary");
385  if (project_name != "") {
386  filename_unitary=project_name+ "_" +filename_unitary;
387  }
388  export_unitary(filename_unitary);
389 
390 
391  }
392  }
393 
394  iter++;
395 
396  if (uncompressed_iter_num>1) break;
397  // store the decomposing gate structure
398  }
399 
400  release_gates();
401 
402 
403  combine( gate_structure_loc );
404  delete( gate_structure_loc );
405 
406 #if BLAS==0 // undefined BLAS
408 #elif BLAS==1 //MKL
409  MKL_Set_Num_Threads(num_threads);
410 #elif BLAS==2 //OpenBLAS
411  openblas_set_num_threads(num_threads);
412 #endif
413 
414 }
415 
416 
417 
418 
419 
420 
421 
426 
427 
428 // temporarily turn off OpenMP parallelism
429 #if BLAS==0 // undefined BLAS
432 #elif BLAS==1 // MKL
433  num_threads = mkl_get_max_threads();
434  MKL_Set_Num_Threads(1);
435 #elif BLAS==2 //OpenBLAS
436  num_threads = openblas_get_num_threads();
437  openblas_set_num_threads(1);
438 #endif
439 
440 
441  Gates_block* gate_structure_loc = NULL;
442  if ( gates.size() > 0 ) {
443  std::stringstream sstream;
444  sstream << "Using imported gate structure for the compression." << std::endl;
445  print(sstream, 1);
446 
447  gate_structure_loc = static_cast<Gates_block*>(this)->clone();
448  }
449  else {
450  std::stringstream sstream;
451  sstream << "No circuit initalised." << std::endl;
452  print(sstream, 1);
453  return;
454  }
455 
456 
457  std::stringstream sstream;
458  sstream.str("");
459  sstream << "**************************************************************" << std::endl;
460  sstream << "************ Final tuning of the Gate structure **************" << std::endl;
461  sstream << "**************************************************************" << std::endl;
462  print(sstream, 1);
463 
464  // maximal number of inner iterations overriden by config
465  if ( config.count("optimization_tolerance") > 0 ) {
466  long long value;
467  config["optimization_tolerance"].get_property( value );
468  optimization_tolerance = (double)value;
469  }
470  else {optimization_tolerance = 1e-4;}
471 
472 
473 
475 
476 
477  sstream.str("");
478  sstream << "cost function value before replacing trivial CRY gates: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
479  print(sstream, 3);
480 
481  Gates_block* gate_structure_tmp = replace_trivial_CRY_gates( gate_structure_loc, optimized_parameters_mtx );
482  Matrix_real optimized_parameters_save = optimized_parameters_mtx;
483 
484  release_gates();
485  combine( gate_structure_tmp );
486 
487  sstream.str("");
488  sstream << "cost function value before final optimization: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
489  print(sstream, 3);
490 
491  release_gates();
492  optimized_parameters_mtx = optimized_parameters_save;
493 
494  // solve the optimization problem
495  N_Qubit_Decomposition_custom cDecomp_custom;
496 
497 
498  std::map<std::string, Config_Element> config_copy;
499  config_copy.insert(config.begin(), config.end());
500  if ( config.count("max_inner_iterations_final") > 0 ) {
501  long long val;
502  config["max_inner_iterations_final"].get_property( val );
503  Config_Element element;
504  element.set_property( "max_inner_iterations", val );
505  config_copy["max_inner_iterations"] = element;
506  }
507 
508 
509  // solve the optimization problem in isolated optimization process
510  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
511  cDecomp_custom.set_custom_gate_structure( gate_structure_tmp );
513  cDecomp_custom.set_optimization_blocks( gate_structure_loc->get_gate_num() );
514  cDecomp_custom.set_max_iteration( max_outer_iterations );
515  cDecomp_custom.set_verbose(verbose);
516  cDecomp_custom.set_cost_function_variant( cost_fnc );
517  cDecomp_custom.set_debugfile("");
518  cDecomp_custom.set_iteration_loops( iteration_loops );
520  cDecomp_custom.set_trace_offset( trace_offset );
521  cDecomp_custom.set_optimizer( alg );
522  if (alg==ADAM || alg==BFGS2) {
523  int param_num_loc = gate_structure_loc->get_parameter_num();
524  int max_inner_iterations_loc = (double)param_num_loc/852 * 1e7;
525  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
526  cDecomp_custom.set_random_shift_count_max( 10000 );
527  }
528  else if ( alg==ADAM_BATCHED ) {
529  cDecomp_custom.set_optimizer( alg );
530  int max_inner_iterations_loc = 2500;
531  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
532  cDecomp_custom.set_random_shift_count_max( 5 );
533  }
534  else if ( alg==BFGS ) {
535  cDecomp_custom.set_optimizer( alg );
536  int max_inner_iterations_loc = 10000;
537  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
538  }
539  cDecomp_custom.start_decomposition();
540  number_of_iters += cDecomp_custom.get_num_iters();
541 
542  current_minimum = cDecomp_custom.get_current_minimum();
544 
545 
546  combine( gate_structure_tmp );
547  delete( gate_structure_tmp );
548  delete( gate_structure_loc );
549 
550  sstream.str("");
551  sstream << "cost function value after final optimization: " << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
552  print(sstream, 3);
553 
554 
555 
556  long long export_circuit_2_binary_loc;
557  if ( config.count("export_circuit_2_binary") > 0 ) {
558  config["export_circuit_2_binary"].get_property( export_circuit_2_binary_loc );
559  }
560  else {
561  export_circuit_2_binary_loc = 0;
562  }
563 
564 
565  if ( export_circuit_2_binary_loc > 0 ) {
566  std::string filename2("circuit_final.binary");
567 
568  if (project_name != "") {
569  filename2=project_name+ "_" +filename2;
570  }
571 
573 
574  }
575 
577 
578 
579  sstream.str("");
580  sstream << "In the decomposition with error = " << decomposition_error << " were used " << layer_num << " gates with:" << std::endl;
581 
582  // get the number of gates used in the decomposition
583  std::map<std::string, int>&& gate_nums = get_gate_nums();
584 
585  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
586  sstream << it->second << " " << it->first << " gates" << std::endl;
587  }
588 
589  sstream << std::endl;
590  print(sstream, 1);
591 
592 #if BLAS==0 // undefined BLAS
594 #elif BLAS==1 //MKL
595  MKL_Set_Num_Threads(num_threads);
596 #elif BLAS==2 //OpenBLAS
597  openblas_set_num_threads(num_threads);
598 #endif
599 
600 }
601 
606 Gates_block*
608 
609  Gates_block* gate_structure_loc = (static_cast<Gates_block*>(this))->clone();
610 
611  //measure the time for the decompositin
612  tbb::tick_count start_time_loc = tbb::tick_count::now();
613 
614  std::stringstream sstream;
615  sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
616  print(sstream, 1);
617 
618  double optimization_tolerance_loc;
619  if ( config.count("optimization_tolerance") > 0 ) {
620  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
621  }
622  else {
623  optimization_tolerance_loc = optimization_tolerance;
624  }
625 
626  // solve the optimization problem
627  N_Qubit_Decomposition_custom cDecomp_custom;
628  // solve the optimization problem in isolated optimization process
630  cDecomp_custom.set_custom_gate_structure( gate_structure_loc );
631  cDecomp_custom.set_optimized_parameters( optimized_parameters_mtx_loc.get_data(), optimized_parameters_mtx_loc.size() );
632  cDecomp_custom.set_optimization_blocks( gate_structure_loc->get_gate_num() );
633  cDecomp_custom.set_max_iteration( max_outer_iterations );
634  cDecomp_custom.set_verbose(verbose);
635  cDecomp_custom.set_cost_function_variant( cost_fnc );
636  cDecomp_custom.set_debugfile("");
637  cDecomp_custom.set_iteration_loops( iteration_loops );
638  cDecomp_custom.set_optimization_tolerance( optimization_tolerance_loc );
639  cDecomp_custom.set_trace_offset( trace_offset );
640  cDecomp_custom.set_optimizer( alg );
641  cDecomp_custom.set_project_name( project_name );
642  if (alg==ADAM || alg==BFGS2) {
643  int param_num_loc = gate_structure_loc->get_parameter_num();
644  int max_inner_iterations_loc = (double)param_num_loc/852 * 1e7;
645  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
646  cDecomp_custom.set_random_shift_count_max( 10000 );
647  }
648  else if ( alg==ADAM_BATCHED ) {
649  cDecomp_custom.set_optimizer( alg );
650  int max_inner_iterations_loc = 2500;
651  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
652  cDecomp_custom.set_random_shift_count_max( 5 );
653  }
654  else if ( alg==BFGS ) {
655  cDecomp_custom.set_optimizer( alg );
656  int max_inner_iterations_loc = 10000;
657  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
658  }
659  cDecomp_custom.start_decomposition();
660  number_of_iters += cDecomp_custom.get_num_iters();
661  //cDecomp_custom.list_gates(0);
662 
663  tbb::tick_count end_time_loc = tbb::tick_count::now();
664 
665  current_minimum = cDecomp_custom.get_current_minimum();
666  optimized_parameters_mtx_loc = cDecomp_custom.get_optimized_parameters();
667 
668 
669 
670  if ( cDecomp_custom.get_current_minimum() < optimization_tolerance_loc ) {
671  std::stringstream sstream;
672  sstream << "Optimization problem solved with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
673  print(sstream, 1);
674  }
675  else {
676  std::stringstream sstream;
677  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;
678  print(sstream, 1);
679  }
680 
681  if (current_minimum > optimization_tolerance_loc) {
682  std::stringstream sstream;
683  sstream << "Decomposition did not reached prescribed high numerical precision." << std::endl;
684  print(sstream, 1);
685  optimization_tolerance_loc = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
686  }
687 
688  sstream.str("");
689  sstream << "Continue with the compression of gate structure consisting of " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
690  print(sstream, 1);
691  return gate_structure_loc;
692 
693 
694 
695 }
696 
701 Gates_block*
703 
704  // strages to store the optimized minimums in case of different cirquit depths
705  std::vector<double> minimum_vec;
706  std::vector<Gates_block*> gate_structure_vec;
707  std::vector<Matrix_real> optimized_parameters_vec;
708 
709  double optimization_tolerance_loc;
710  if ( config.count("optimization_tolerance") > 0 ) {
711  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
712  }
713  else {
714  optimization_tolerance_loc = optimization_tolerance;
715  }
716 
717  int max_outer_iterations_loc;
718  double value_placeholder;
719  if ( config.count("max_outer_iterations") > 0 ) {
720  config["max_outer_iterations"].get_property( value_placeholder );
721  max_outer_iterations_loc = (int) value_placeholder;
722  }
723  else {
724  max_outer_iterations_loc = max_outer_iterations;
725  }
726 
727 
728  int level = level_limit_min;
729  while ( current_minimum > optimization_tolerance_loc && level <= level_limit) {
730 
731  // create gate structure to be optimized
732  Gates_block* gate_structure_loc = new Gates_block(qbit_num);
733 
734  optimized_parameters_mtx_loc = Matrix_real(0,0);
735 
736  for (int idx=0; idx<level; idx++) {
737 
738  // create the new decomposing layer and add to the gate staructure
739  add_adaptive_layers( gate_structure_loc );
740 
741  }
742 
743  // add finalyzing layer to the top of the gate structure
744  add_finalyzing_layer( gate_structure_loc );
745 
746 
747  //measure the time for the decompositin
748  tbb::tick_count start_time_loc = tbb::tick_count::now();
749 
750 
751  N_Qubit_Decomposition_custom cDecomp_custom_random, cDecomp_custom_close_to_zero;
752 
753  std::stringstream sstream;
754  sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
755  print(sstream, 1);
756 
757 
758 /*
759 #ifndef __DFE__
760  // try the decomposition withrandom and with close to zero initial values
761  tbb::parallel_invoke(
762  [&]{
763 #endif
764 */
765  // solve the optimization problem in isolated optimization process
766  cDecomp_custom_random = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config, RANDOM, accelerator_num);
767  cDecomp_custom_random.set_custom_gate_structure( gate_structure_loc );
768  cDecomp_custom_random.set_optimization_blocks( gate_structure_loc->get_gate_num() );
769  cDecomp_custom_random.set_max_iteration( max_outer_iterations_loc );
770 #ifndef __DFE__
771  cDecomp_custom_random.set_verbose(verbose);
772 #else
773  cDecomp_custom_random.set_verbose(0);
774 #endif
775  cDecomp_custom_random.set_cost_function_variant( cost_fnc );
776  cDecomp_custom_random.set_debugfile("");
777  cDecomp_custom_random.set_optimization_tolerance( optimization_tolerance_loc );
778  cDecomp_custom_random.set_trace_offset( trace_offset );
779  cDecomp_custom_random.set_optimizer( alg );
780  cDecomp_custom_random.set_project_name( project_name );
781  if ( alg == ADAM || alg == BFGS2 ) {
782  int param_num_loc = gate_structure_loc->get_parameter_num();
783  int max_inner_iterations_loc = (double)param_num_loc/852 * 1e7;
784  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
785  cDecomp_custom_random.set_random_shift_count_max( 10000 );
786  }
787  else if ( alg==ADAM_BATCHED ) {
788  cDecomp_custom_random.set_optimizer( alg );
789  int max_inner_iterations_loc = 2000;
790  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
791  cDecomp_custom_random.set_random_shift_count_max( 5 );
792  }
793  else if ( alg==BFGS ) {
794  cDecomp_custom_random.set_optimizer( alg );
795  int max_inner_iterations_loc = 10000;
796  cDecomp_custom_random.set_max_inner_iterations( max_inner_iterations_loc );
797  }
798 
799 
800  cDecomp_custom_random.start_decomposition();
801 
802 
803 
804  number_of_iters += cDecomp_custom_random.get_num_iters(); // retrive the number of iterations spent on optimization
805 /*
806 #ifndef __DFE__
807  },
808  [&]{
809  // solve the optimization problem in isolated optimization process
810  cDecomp_custom_close_to_zero = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, CLOSE_TO_ZERO);
811  cDecomp_custom_close_to_zero.set_custom_gate_structure( gate_structure_loc );
812  cDecomp_custom_close_to_zero.set_optimization_blocks( gate_structure_loc->get_gate_num() );
813  cDecomp_custom_close_to_zero.set_max_iteration( max_outer_iterations );
814  cDecomp_custom_close_to_zero.set_verbose(0);
815  cDecomp_custom_close_to_zero.set_cost_function_variant( cost_fnc );
816  cDecomp_custom_close_to_zero.set_debugfile("");
817  cDecomp_custom_close_to_zero.set_optimization_tolerance( optimization_tolerance );
818  cDecomp_custom_close_to_zero.set_optimizer( alg );
819  if ( alg == ADAM || alg == BFGS2 ) {
820  int param_num_loc = gate_structure_loc->get_parameter_num();
821  int max_inner_iterations_loc = (double)param_num_loc/852 * 1e7;
822  cDecomp_custom_close_to_zero.set_max_inner_iterations( max_inner_iterations_loc );
823  cDecomp_custom_close_to_zero.set_random_shift_count_max( 10000 );
824  }
825  cDecomp_custom.close_to_zero.set_trace_offset( trace_offset );
826  cDecomp_custom_close_to_zero.start_decomposition(true);
827  number_of_iters += cDecomp_custom_close_to_zero.get_num_iters();
828  }
829  );
830 #endif
831 */
832  tbb::tick_count end_time_loc = tbb::tick_count::now();
833 /*
834 #ifdef __DFE__
835 return NULL;
836 #endif
837 */
838  double current_minimum_random = cDecomp_custom_random.get_current_minimum();
839  double current_minimum_close_to_zero = cDecomp_custom_close_to_zero.get_current_minimum();
840  double current_minimum_loc;
841 
842 
843  // select between the results obtained for different initial value strategy
844  if ( current_minimum_random < optimization_tolerance_loc && current_minimum_close_to_zero > optimization_tolerance_loc ) {
845  current_minimum_loc = current_minimum_random;
846  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
848  }
849  else if ( current_minimum_random > optimization_tolerance_loc && current_minimum_close_to_zero < optimization_tolerance_loc ) {
850  current_minimum_loc = current_minimum_close_to_zero;
851  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
853  }
854  else if ( current_minimum_random < optimization_tolerance_loc && current_minimum_close_to_zero < optimization_tolerance_loc ) {
855  Matrix_real optimized_parameters_mtx_random = cDecomp_custom_random.get_optimized_parameters();
856  Matrix_real optimized_parameters_mtx_close_to_zero = cDecomp_custom_close_to_zero.get_optimized_parameters();
857 
858  int panelty_random = get_panelty(gate_structure_loc, optimized_parameters_mtx_random);
859  int panelty_close_to_zero = get_panelty(gate_structure_loc, optimized_parameters_mtx_close_to_zero );
860 
861  if ( panelty_random < panelty_close_to_zero ) {
862  current_minimum_loc = current_minimum_random;
863  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
865  }
866  else {
867  current_minimum_loc = current_minimum_close_to_zero;
868  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
870  }
871 
872  }
873  else {
874  if ( current_minimum_random < current_minimum_close_to_zero ) {
875  current_minimum_loc = current_minimum_random;
876  optimized_parameters_mtx_loc = cDecomp_custom_random.get_optimized_parameters();
878  }
879  else {
880  current_minimum_loc = current_minimum_close_to_zero;
881  optimized_parameters_mtx_loc = cDecomp_custom_close_to_zero.get_optimized_parameters();
883  }
884 
885  }
886 
887  minimum_vec.push_back(current_minimum_loc);
888  gate_structure_vec.push_back(gate_structure_loc);
889  optimized_parameters_vec.push_back(optimized_parameters_mtx_loc);
890 
891 
892 
893  if ( current_minimum_loc < optimization_tolerance_loc ) {
894  std::stringstream sstream;
895  sstream << "Optimization problem solved with " << gate_structure_loc->get_gate_num() << " decomposing layers in " << (end_time_loc-start_time_loc).seconds() << " seconds." << std::endl;
896  print(sstream, 1);
897  break;
898  }
899  else {
900  std::stringstream sstream;
901  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;
902  print(sstream, 1);
903  }
904 
905  level++;
906  }
907 
908 //exit(-1);
909 
910  // find the best decomposition
911  int idx_min = 0;
912  double current_minimum = minimum_vec[0];
913  for (int idx=1; idx<(int)minimum_vec.size(); idx++) {
914  if( current_minimum > minimum_vec[idx] ) {
915  idx_min = idx;
916  current_minimum = minimum_vec[idx];
917  }
918  }
919 
920 
921  Gates_block* gate_structure_loc = gate_structure_vec[idx_min];
922  optimized_parameters_mtx_loc = optimized_parameters_vec[idx_min];
923 
924  // release unnecesarry data
925  for (int idx=0; idx<(int)minimum_vec.size(); idx++) {
926  if( idx == idx_min ) {
927  continue;
928  }
929  delete( gate_structure_vec[idx] );
930  }
931  minimum_vec.clear();
932  gate_structure_vec.clear();
933  optimized_parameters_vec.clear();
934 
935 
936 
937  if (current_minimum > optimization_tolerance_loc) {
938  std::stringstream sstream;
939  sstream << "Decomposition did not reached prescribed high numerical precision." << std::endl;
940  print(sstream, 1);
941  optimization_tolerance_loc = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
942  }
943 
944 
945  return gate_structure_loc;
946 
947 }
948 
949 
950 
957 
958 
959 
960  int layer_num_max;
961  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
962  if ( layer_num_orig < 50 ) layer_num_max = layer_num_orig;
963  else if ( layer_num_orig < 60 ) layer_num_max = 4;
964  else layer_num_max = 2;
965  double optimization_tolerance_loc;
966  if ( config.count("optimization_tolerance") > 0 ) {
967  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
968  }
969  else {
970  optimization_tolerance_loc = optimization_tolerance;
971  }
972 
973  // random generator of integers
974  std::uniform_int_distribution<> distrib_int(0, 5000);
975  // create a list of layers to be tested for removal.
976  std::vector<int> layers_to_remove;
977  if (uncompressed_iter_num==0){
978  layer_num_max = 5<layer_num_orig ? 5 : layer_num_orig;
979  // create a list of layers to be tested for removal.
980  std::vector<double> layers_parameters(layer_num_orig,15.0);
981  std::vector<int> layers_idx_sorted(layer_num_orig,0);
982  //layers_to_remove.reserve(layer_num_orig);
983  for (int idx=0; idx<layer_num_orig;idx++){
984  layers_parameters[idx] = extract_theta_from_layer(gate_structure,idx,optimized_parameters_mtx);
985  layers_idx_sorted[idx] = idx;
986  }
987 
988  std::iota(layers_idx_sorted.begin(),layers_idx_sorted.end(),0); //Initializing
989  sort( layers_idx_sorted.begin(),layers_idx_sorted.end(), [&](int i,int j){return layers_parameters[i]<layers_parameters[j];} );
990 
991  for (int idx=0; idx<layer_num_max; idx++ ) { // TODO: see line 1558 to explain the -1
992  layers_to_remove.push_back( layers_idx_sorted[idx]);
993  }
994  }
995  else{
996  layers_to_remove.reserve(layer_num_orig);
997  for (int idx=0; idx<layer_num_orig; idx++ ) { // TODO: see line 1558 to explain the -1
998  layers_to_remove.push_back(idx);
999  }
1000 
1001 
1002  while ( (int)layers_to_remove.size() > layer_num_max ) {
1003  int remove_idx = distrib_int(gen) % layers_to_remove.size();
1004 
1005  layers_to_remove.erase( layers_to_remove.begin() + remove_idx );
1006  }
1007  }
1008 
1009 
1010 #ifdef __MPI__
1011  MPI_Bcast( &layers_to_remove[0], layers_to_remove.size(), MPI_INT, 0, MPI_COMM_WORLD);
1012 #endif
1013 
1014  // make a copy of the original unitary. (By removing trivial gates global phase might be added to the unitary)
1015  Matrix&& Umtx_orig = Umtx.copy();
1016 
1017  int panelties_num = layer_num_max < layer_num_orig ? layer_num_max : layer_num_orig;
1018 
1019  if ( panelties_num == 0 ) {
1020  return gate_structure;
1021  }
1022 
1023  // preallocate panelties associated with the number of remaining two-qubit controlled gates
1024  std::vector<unsigned int> panelties(panelties_num, 1<<31);
1025  std::vector<Gates_block*> gate_structures_vec(panelties_num, NULL);
1026  std::vector<double> current_minimum_vec(panelties_num, DBL_MAX);
1027  std::vector<int> iteration_num_vec(panelties_num, 0);
1028 
1029 
1030  std::vector<Matrix_real> optimized_parameters_vec(panelties_num, Matrix_real(0,0));
1031  std::vector<Matrix> Umtx_vec(panelties_num, Matrix(0,0));
1032 
1033 
1034 
1035  for (int idx=0; idx<panelties_num; idx++) {
1036 
1037  Umtx = Umtx_orig.copy();
1038 
1039  double current_minimum_loc = DBL_MAX;//current_minimum;
1040  int iteration_num = 0;
1041  Matrix_real optimized_parameters_loc = optimized_parameters_mtx.copy();
1042 
1043  Gates_block* gate_structure_reduced = compress_gate_structure( gate_structure, layers_to_remove[idx], optimized_parameters_loc, current_minimum_loc, iteration_num );
1044  if ( optimized_parameters_loc.size() == 0 ) {
1045  optimized_parameters_loc = optimized_parameters_mtx.copy();
1046  }
1047 
1048  // remove further adaptive gates if possible
1049  Gates_block* gate_structure_tmp;
1050  if ( gate_structure_reduced->get_gate_num() == gate_structure->get_gate_num() ) {
1051  gate_structure_tmp = gate_structure_reduced->clone();
1052  }
1053  else {
1054  gate_structure_tmp = remove_trivial_gates( gate_structure_reduced, optimized_parameters_loc, current_minimum_loc ); //TODO: reverse gate order
1055  }
1056 
1057  panelties[idx] = get_panelty(gate_structure_tmp, optimized_parameters_loc);
1058  gate_structures_vec[idx] = gate_structure_tmp;
1059  current_minimum_vec[idx] = current_minimum_loc;
1060  iteration_num_vec[idx] = iteration_num; // the accumulated number of optimization iterations
1061 
1062 
1063  optimized_parameters_vec[idx] = optimized_parameters_loc;
1064  Umtx_vec[idx] = Umtx;
1065 
1066 
1067  delete(gate_structure_reduced);
1068 
1069 #ifdef __DFE__
1070  if ( current_minimum_vec[idx] < optimization_tolerance_loc ) {
1071  break;
1072  }
1073 #endif
1074  }
1075 
1076 
1077 
1078  // determine the reduction with the lowest penalty
1079  unsigned int panelty_min = panelties[0];
1080  unsigned int idx_min = 0;
1081 
1082  for (size_t idx=0; idx<panelties.size(); idx++) {
1083  if ( panelty_min > panelties[idx] ) {
1084  panelty_min = panelties[idx];
1085  idx_min = idx;
1086  }
1087 
1088  else if ( panelty_min == panelties[idx] ) {
1089 
1090  if ( (distrib_int(gen) % 2) == 1 ) {
1091  idx_min = idx;
1092 
1093  panelty_min = panelties[idx];
1094  }
1095 
1096  }
1097 
1098  }
1099 
1100 #ifdef __MPI__
1101  MPI_Bcast( &idx_min, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);
1102 #endif
1103 
1104 
1105  // release gate structures other than the best one
1106  for (size_t idx=0; idx<panelties.size(); idx++) {
1107  if (idx==idx_min) {
1108  continue;
1109  }
1110 
1111 
1112  if ( gate_structures_vec[idx] == gate_structure) {
1113  continue;
1114  }
1115 
1116  if ( gate_structures_vec[idx] ) {
1117  delete( gate_structures_vec[idx] );
1118  gate_structures_vec[idx] = NULL;
1119  }
1120 
1121  }
1122 
1123 
1124  gate_structure = gate_structures_vec[idx_min];
1125  optimized_parameters_mtx = optimized_parameters_vec[idx_min];
1126  current_minimum = current_minimum_vec[idx_min];
1127  number_of_iters += iteration_num_vec[idx_min]; // the total number of the accumulated optimization iterations
1128  Umtx = Umtx_vec[idx_min];
1129 
1130  int layer_num = gate_structure->get_gate_num();
1131 
1132  if ( layer_num < layer_num_orig+1 ) {
1133  std::stringstream sstream;
1134  sstream << "gate structure reduced from " << layer_num_orig+1 << " to " << layer_num << " decomposing layers" << std::endl;
1135  print(sstream, 1);
1136  }
1137  else {
1138  std::stringstream sstream;
1139  sstream << "gate structure kept at " << layer_num << " layers" << std::endl;
1140  print(sstream, 1);
1141  }
1142 
1143 
1144  return gate_structure;
1145 
1146 
1147 
1148 }
1149 
1158 Gates_block*
1159 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 ) {
1160 
1161  // create reduced gate structure without layer indexed by layer_idx
1162  Gates_block* gate_structure_reduced = gate_structure->clone();
1163  gate_structure_reduced->release_gate( layer_idx );
1164 
1165  Matrix_real parameters_reduced;
1166  if ( optimized_parameters.size() > 0 ) {
1167  parameters_reduced = create_reduced_parameters( gate_structure, optimized_parameters, layer_idx );
1168  }
1169  else {
1170  parameters_reduced = Matrix_real(0, 0);
1171  }
1172 
1173 
1174 
1175  N_Qubit_Decomposition_custom cDecomp_custom;
1176 
1177  std::map<std::string, Config_Element> config_copy;
1178  config_copy.insert(config.begin(), config.end());
1179  if ( config.count("max_inner_iterations_compression") > 0 ) {
1180  long long val;
1181  config["max_inner_iterations_compression"].get_property( val );
1182  Config_Element element;
1183  element.set_property( "max_inner_iterations", val );
1184  config_copy["max_inner_iterations"] = element;
1185  }
1186 
1187  double optimization_tolerance_loc;
1188  if ( config.count("optimization_tolerance") > 0 ) {
1189  config["optimization_tolerance"].get_property( optimization_tolerance_loc );
1190  }
1191  else {
1192  optimization_tolerance_loc = optimization_tolerance;
1193  }
1194 
1195 
1196 
1197  // solve the optimization problem in isolated optimization process
1198  cDecomp_custom = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess, accelerator_num);
1199  cDecomp_custom.set_custom_gate_structure( gate_structure_reduced );
1200  cDecomp_custom.set_optimized_parameters( parameters_reduced.get_data(), parameters_reduced.size() );
1201  cDecomp_custom.set_verbose(0);
1202  cDecomp_custom.set_cost_function_variant( cost_fnc );
1203  cDecomp_custom.set_debugfile("");
1204  cDecomp_custom.set_max_iteration( max_outer_iterations );
1205  cDecomp_custom.set_iteration_loops( iteration_loops );
1206  cDecomp_custom.set_optimization_blocks( gate_structure_reduced->get_gate_num() ) ;
1207  cDecomp_custom.set_optimization_tolerance( optimization_tolerance_loc );
1208  cDecomp_custom.set_trace_offset( trace_offset );
1209  cDecomp_custom.set_optimizer( alg );
1210  if ( alg == ADAM || alg==BFGS2) {
1211  cDecomp_custom.set_max_inner_iterations( 1e5 );
1212  cDecomp_custom.set_random_shift_count_max( 1 );
1213  }
1214  else if ( alg==BFGS ) {
1215  cDecomp_custom.set_optimizer( alg );
1216  int max_inner_iterations_loc = 100;
1217  cDecomp_custom.set_max_inner_iterations( max_inner_iterations_loc );
1218  }
1219  cDecomp_custom.start_decomposition();
1220  iteration_num = cDecomp_custom.get_num_iters();
1221  double current_minimum_tmp = cDecomp_custom.get_current_minimum();
1222 
1223  if ( current_minimum_tmp < optimization_tolerance_loc ) {
1224  //cDecomp_custom.list_gates(0);
1225  optimized_parameters = cDecomp_custom.get_optimized_parameters();
1226  current_minimum_loc = current_minimum_tmp;
1227  return gate_structure_reduced;
1228  }
1229 
1230 
1231  return gate_structure->clone();
1232 
1233 }
1234 
1235 
1241 unsigned int
1243 
1244 
1245  int panelty = 0;
1246 
1247  // iterate over the elements of the parameter array
1248  int parameter_idx = 0;
1249  int layer_num = gate_structure->get_gate_num();
1250  //for ( int layer_idx=layer_num-1; layer_idx>=0; layer_idx--) {
1251  for ( int layer_idx=0; layer_idx<layer_num; layer_idx++) {
1252 
1253  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate( layer_idx ) );
1254 
1255  int gate_num = layer->get_gate_num();
1256  //for( int gate_idx=gate_num-1; gate_idx>=0; gate_idx-- ) {
1257  for( int gate_idx=0; gate_idx<gate_num; gate_idx++ ) {
1258 
1259  Gate* gate = layer->get_gate( gate_idx );
1260 
1261  double parameter = optimized_parameters[parameter_idx];
1262  parameter_idx = parameter_idx + gate->get_parameter_num();
1263 
1264  if ( (gate->get_type() != ADAPTIVE_OPERATION) && (gate->get_type() != CROT_OPERATION)) {
1265  continue;
1266  }
1267 
1268  if (gate->get_type() == ADAPTIVE_OPERATION){
1269  if ( std::abs(std::sin(parameter)) < 0.999 && std::abs(std::cos(parameter)) < 1e-3 ) {
1270  // Condition of pure CNOT gate
1271  panelty += 1;
1272  }
1273  else if ( std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1274  // Condition of pure Identity gate
1275  //panelty++;
1276  }
1277  else {
1278  // Condition of controlled rotation gate
1279  panelty += 2;
1280  }
1281  }
1282  else{
1283  panelty +=1;
1284  }
1285 
1286  }
1287 
1288  }
1289 
1290 
1291  return panelty;
1292 
1293 
1294 }
1295 
1296 
1302 Gates_block*
1304 
1305  Gates_block* gate_structure_ret = new Gates_block(qbit_num);
1306 
1307  int layer_num = gate_structure->get_gate_num();
1308 
1309 /*
1310  std::map<std::string, Config_Element> config_copy;
1311  config_copy.insert(config.begin(), config.end());
1312  N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1313  cDecomp_custom.set_custom_gate_structure( gate_structure );
1314  std::cout << std::endl << "before removing trivial gate: " << cDecomp_custom.optimization_problem( optimized_parameters ) << std::endl;
1315 */
1316  int parameter_idx = 0;
1317  //for (int idx=layer_num-1; idx>=0; idx-- ) {
1318  for (int idx=0; idx<layer_num; idx++ ) {
1319 
1320  Gate* gate = gate_structure->get_gate(idx);
1321 
1322  if ( gate->get_type() != BLOCK_OPERATION ) {
1323  std::string err = "N_Qubit_Decomposition_adaptive::replace_trivial_adaptive_gates: Only block gates are accepted in this conversion.";
1324  throw( err );
1325  }
1326 
1327  Gates_block* block_op = static_cast<Gates_block*>(gate);
1328  //int param_num = gate->get_parameter_num();
1329 
1330 
1331  if ( true ) {//gate_structure->contains_adaptive_gate(idx) ) {
1332 
1333  Gates_block* layer = block_op->clone();
1334 
1335  //for ( int jdx=layer->get_gate_num()-1; jdx>=0; jdx-- ) {
1336  for ( int jdx=0; jdx<layer->get_gate_num(); jdx++ ) {
1337 
1338  Gate* gate_tmp = layer->get_gate(jdx);
1339  int param_num = gate_tmp->get_parameter_num();
1340 
1341 
1342  double parameter = optimized_parameters[parameter_idx];
1343  parameter = activation_function(parameter, 1);//limit_max);
1344 
1345 //std::cout << param[0] << " " << (gate_tmp->get_type() == ADAPTIVE_OPERATION) << " " << std::abs(std::sin(param[0])) << " " << 1+std::cos(param[0]) << std::endl;
1346 
1347 
1348  if ( gate_tmp->get_type() == ADAPTIVE_OPERATION && std::abs(std::sin(parameter)) > 0.999 && std::abs(std::cos(parameter)) < 1e-3) {
1349 
1350  // convert to CZ gate
1351  int target_qbit = gate_tmp->get_target_qbit();
1352  int control_qbit = gate_tmp->get_control_qbit();
1353  layer->release_gate( jdx );
1354 
1355  RX* rx_gate_1 = new RX(qbit_num, target_qbit);
1356  CZ* cz_gate = new CZ(qbit_num, target_qbit, control_qbit);
1357  RX* rx_gate_2 = new RX(qbit_num, target_qbit);
1358  RZ* rz_gate = new RZ(qbit_num, control_qbit);
1359 
1360  Gates_block* czr_gate = new Gates_block(qbit_num);
1361  czr_gate->add_gate(rx_gate_1);
1362  czr_gate->add_gate(cz_gate);
1363  czr_gate->add_gate(rx_gate_2);
1364  czr_gate->add_gate(rz_gate);
1365 
1366  layer->insert_gate( (Gate*)czr_gate, jdx);
1367 
1368  Matrix_real parameters_new(1, optimized_parameters.size()+2);
1369 
1370 
1371  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1372 
1373  memcpy(parameters_new.get_data()+parameter_idx+3, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1374 
1375  parameters_new[parameter_idx] = -M_PI/4; // rx_1 parameter
1376  parameters_new[parameter_idx+1] = M_PI/4; // rx_2 parameter
1377 
1378 
1379  if ( std::sin(parameter) < 0 ) {
1380 // parameters_new[parameter_idx+2] = -M_PI/2; // rz parameter with original RZ_P gate, in this case no global phase occurs either
1381  parameters_new[parameter_idx+2] = -M_PI/4; // rz parameter
1382 
1383  QGD_Complex16 global_phase_factor_new;
1384  global_phase_factor_new.real = std::cos( -M_PI/4 );
1385  global_phase_factor_new.imag = std::sin( -M_PI/4 );
1386  apply_global_phase_factor(global_phase_factor_new, Umtx);
1387 
1388  }
1389  else{
1390 // parameters_new[parameter_idx+2] = M_PI/2; // rz parameter with original RZ_P gate, in this case no global phase occurs either
1391  parameters_new[parameter_idx+2] = M_PI/4; // rz parameter
1392 
1393  QGD_Complex16 global_phase_factor_new;
1394  global_phase_factor_new.real = std::cos( M_PI/4 );
1395  global_phase_factor_new.imag = std::sin( M_PI/4 );
1396  apply_global_phase_factor(global_phase_factor_new, Umtx);
1397 
1398  }
1399 
1400 
1401  optimized_parameters = parameters_new;
1402  parameter_idx += 3;
1403 
1404 
1405 
1406  }
1407 
1408  else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION && std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
1409  // release trivial gate
1410 
1411  layer->release_gate( jdx );
1412  //jdx--;
1413  Matrix_real parameters_new(1, optimized_parameters.size()-1);
1414  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1415  memcpy(parameters_new.get_data()+parameter_idx, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1416  optimized_parameters = parameters_new;
1417 
1418 
1419  }
1420 
1421  else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION ) {
1422 
1423  // controlled Y rotation decomposed into 2 CNOT gates
1424  int target_qbit = gate_tmp->get_target_qbit();
1425  int control_qbit = gate_tmp->get_control_qbit();
1426  layer->release_gate( jdx );
1427 
1428  RY* ry_gate_1 = new RY(qbit_num, target_qbit);
1429  CNOT* cnot_gate_1 = new CNOT(qbit_num, target_qbit, control_qbit);
1430  RY* ry_gate_2 = new RY(qbit_num, target_qbit);
1431  CNOT* cnot_gate_2 = new CNOT(qbit_num, target_qbit, control_qbit);
1432 
1433  Gates_block* czr_gate = new Gates_block(qbit_num);
1434  czr_gate->add_gate(ry_gate_1);
1435  czr_gate->add_gate(cnot_gate_1);
1436  czr_gate->add_gate(ry_gate_2);
1437  czr_gate->add_gate(cnot_gate_2);
1438 
1439  layer->insert_gate( (Gate*)czr_gate, jdx);
1440 
1441  Matrix_real parameters_new(1, optimized_parameters.size()+1);
1442  memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
1443  memcpy(parameters_new.get_data()+parameter_idx+2, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
1444  optimized_parameters = parameters_new;
1445 
1446  optimized_parameters[parameter_idx] = parameter/2; // ry_1 parameter
1447  optimized_parameters[parameter_idx+1] = -parameter/2; // ry_2 parameter
1448 
1449 
1450  parameter_idx += 2;
1451 
1452  }
1453 
1454  else {
1455 
1456  parameter_idx += param_num;
1457 
1458  }
1459 
1460 
1461 
1462  }
1463 
1464  gate_structure_ret->add_gate((Gate*)layer);
1465 
1466 
1467  }
1468 
1469  }
1470 
1471 /*
1472  N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1473  cDecomp_custom_.set_custom_gate_structure( gate_structure_ret );
1474  std::cout << std::endl << "after removing trivial gate: " << cDecomp_custom_.optimization_problem( optimized_parameters ) << std::endl;
1475  exit(2);
1476 */
1477  return gate_structure_ret;
1478 
1479 
1480 }
1481 
1488 Gates_block*
1489 N_Qubit_Decomposition_adaptive::remove_trivial_gates( Gates_block* gate_structure, Matrix_real& optimized_parameters, double& current_minimum_loc ) {
1490 
1491  int layer_num = gate_structure->get_gate_num();
1492 
1493  Matrix_real&& optimized_parameters_loc = optimized_parameters.copy();
1494 
1495  Gates_block* gate_structure_loc = gate_structure->clone();
1496 
1497 
1498  for (int idx=0; idx<layer_num; idx++ ) {
1499 
1500  Gates_block* layer = static_cast<Gates_block*>( gate_structure_loc->get_gate(idx) );
1501 
1502 
1503  Gate* gate_adaptive = layer->get_gate(2);
1504  double parameter = optimized_parameters_loc[layer->get_parameter_start_idx() + gate_adaptive->get_parameter_start_idx()]; // parameter for adaptive gate
1505  parameter = activation_function(parameter, 1);//limit_max);
1506 
1507  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 ) {
1508  /*
1509  optimized_parameters_loc[parameter_idx+6] = 0.0;
1510  std::map<std::string, Config_Element> config_copy;
1511  config_copy.insert(config.begin(), config.end());
1512  N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1513  cDecomp_custom.set_custom_gate_structure( gate_structure_loc );
1514  std::cout << std::endl << "before removing trivial gate: " << cDecomp_custom.optimization_problem( optimized_parameters_loc ) << std::endl;
1515  */
1516 
1517  int parameter_idx_to_be_removed = layer->get_parameter_start_idx();
1518 
1519 
1520  // find matching U3 gates into which the U3 gates in the current layer are merged
1521  std::vector<int>&& involved_qbits = layer->get_involved_qubits();
1522  for( size_t rdx=0; rdx<involved_qbits.size(); rdx++ ) {
1523 
1524  U3* U_gate_to_be_removed = static_cast<U3*>(layer->get_gate(rdx));
1525  int qbit_to_be_matched = U_gate_to_be_removed->get_target_qbit();
1526 
1527  int parameter_idx_loc = parameter_idx_to_be_removed + layer->get_parameter_num();
1528 
1529  bool found_match = false;
1530  U3* matching_gate = NULL;
1531 
1532  // iterate over subsequent layers to find the maching gate
1533  for ( int kdx=idx+1; kdx<layer_num; kdx++ ) {
1534 
1535  Gates_block* layer_test = static_cast<Gates_block*>( gate_structure_loc->get_gate(kdx) );
1536 
1537  // iterate over the gates in the tested layer
1538  int gate_num = layer_test->get_gate_num();
1539  for ( int hdx=0; hdx<gate_num; hdx++ ) {
1540 
1541  Gate* gate_test = layer_test->get_gate(hdx);
1542 
1543  if ( gate_test->get_type() == U3_OPERATION ) {
1544  int target_qbit_loc = gate_test->get_target_qbit();
1545 
1546  if ( qbit_to_be_matched == target_qbit_loc ) {
1547  found_match = true;
1548  matching_gate = static_cast<U3*>(gate_test);
1549  parameter_idx_loc = layer_test->get_parameter_start_idx()+matching_gate->get_parameter_start_idx();
1550  break;
1551  }
1552 
1553 
1554  }
1555 
1556  }
1557 
1558  if ( found_match ) break;
1559 
1560 
1561  }
1562 
1563  if ( found_match == false ) {
1564  // TODO: append a matching U3 gate to the very end of the circuit
1565  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.");
1566  throw err;
1567  }
1568 
1569  Matrix_real param1( &optimized_parameters_loc[parameter_idx_to_be_removed], 1, U_gate_to_be_removed->get_parameter_num() );
1570  Matrix U3_matrix1 = U_gate_to_be_removed->calc_one_qubit_u3(param1[0], param1[1], param1[2] );
1571 
1572  Matrix_real param2( &optimized_parameters_loc[parameter_idx_loc], 1, matching_gate->get_parameter_num() );
1573  Matrix U3_matrix2 = matching_gate->calc_one_qubit_u3(param2[0], param2[1], param2[2] );
1574 
1575  Matrix U3_prod = dot(U3_matrix2, U3_matrix1);
1576 
1577  optimized_parameters_loc[parameter_idx_to_be_removed] = 0.0;
1578  optimized_parameters_loc[parameter_idx_to_be_removed+1] = 0.0;
1579  optimized_parameters_loc[parameter_idx_to_be_removed+2] = 0.0;
1580  parameter_idx_to_be_removed = parameter_idx_to_be_removed + U_gate_to_be_removed->get_parameter_num();
1581 
1582  // calculate the new theta/2, phi, lambda parameters from U3_prod, and replace them in param2
1583  // global phase on Umtx
1584  double ctheta3_over2 = std::sqrt(U3_prod[0].real*U3_prod[0].real+U3_prod[0].imag*U3_prod[0].imag); // cos( theta/2 )
1585  double stheta3_over2 = std::sqrt(U3_prod[2].real*U3_prod[2].real+U3_prod[2].imag*U3_prod[2].imag); // sin( theta/2 )
1586  double theta3_over2 = std::atan2(stheta3_over2,ctheta3_over2); // theta/2
1587 
1588  double alpha = std::atan2(U3_prod[0].imag,U3_prod[0].real); // the global phase
1589 
1590  double lambda3;
1591  double phi3;
1592 
1593  if (std::abs(stheta3_over2)<4e-8){
1594  lambda3 = (std::atan2(U3_prod[3].imag,U3_prod[3].real)-alpha)/2;
1595  phi3 = lambda3;
1596  }
1597  else {
1598  lambda3 = std::atan2(-1*U3_prod[1].imag,-1*U3_prod[1].real)-alpha;
1599  phi3 = std::atan2(U3_prod[2].imag,U3_prod[2].real)-alpha;
1600  }
1601 
1602  // the product U3 matrix
1603  Matrix U3_new = matching_gate->calc_one_qubit_u3(theta3_over2,phi3,lambda3);
1604  QGD_Complex16 global_phase_factor_new;
1605  global_phase_factor_new.real = std::cos(alpha);
1606  global_phase_factor_new.imag = std::sin(alpha);
1607  apply_global_phase_factor(global_phase_factor_new, U3_new);
1608 
1609  // test for the product U3 matrix
1610  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) {
1611 
1612  // setting the resulting parameters if test passed
1613 
1614  param2[0] = theta3_over2;
1615  param2[1] = phi3;
1616  param2[2] = lambda3;
1617  apply_global_phase_factor(global_phase_factor_new, Umtx);
1618 
1619  }
1620  /*
1621  N_Qubit_Decomposition_custom cDecomp_custom__( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1622  cDecomp_custom__.set_custom_gate_structure( gate_structure_loc );
1623  std::cout << "right before removing a trivial gate: " << cDecomp_custom__.optimization_problem( optimized_parameters_loc ) << std::endl;
1624  */
1625  }
1626 
1627 
1628  std::stringstream sstream;
1629  sstream << "N_Qubit_Decomposition_adaptive::remove_trivial_gates: Removing trivial gateblock" << std::endl;
1630  print(sstream, 3);
1631 
1632 
1633  // remove gate from the structure
1634  int iteration_num_loc = 0;
1635  Gates_block* gate_structure_tmp = compress_gate_structure( gate_structure_loc, idx, optimized_parameters_loc, current_minimum_loc, iteration_num_loc );
1636  number_of_iters += iteration_num_loc;
1637 
1638  /*
1639  N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, config_copy, initial_guess);
1640  cDecomp_custom_.set_custom_gate_structure( gate_structure_tmp );
1641  std::cout << "after removing a trivial gate: " << cDecomp_custom_.optimization_problem( optimized_parameters_loc ) << std::endl;
1642  */
1643  optimized_parameters = optimized_parameters_loc;
1644  delete( gate_structure_loc );
1645  gate_structure_loc = gate_structure_tmp;
1646  layer_num = gate_structure_loc->get_gate_num();
1647  break;
1648 
1649 
1650 
1651 
1652  }
1653 
1654 
1655 
1656  }
1657 
1658 //std::cout << "N_Qubit_Decomposition_adaptive::remove_trivial_gates :" << gate_structure->get_gate_num() << " reduced to " << gate_structure_loc->get_gate_num() << std::endl;
1659  return gate_structure_loc;
1660 
1661 
1662 
1663 
1664 }
1665 
1666 
1673 Matrix_real
1675 
1676 
1677  // determine the index of the parameter that is about to delete
1678  int gates_num = gate_structure->get_gate_num();
1679  int parameter_idx = 0;
1680  for ( int idx=0; idx<layer_idx; idx++) {
1681  //for ( int idx=gates_num-1; idx>layer_idx; idx--) {
1682  Gate* gate = gate_structure->get_gate( idx );
1683  parameter_idx += gate->get_parameter_num();
1684  }
1685 
1686 
1687  Gate* gate = gate_structure->get_gate( layer_idx );
1688  int param_num_removed = gate->get_parameter_num();
1689 
1690  Matrix_real reduced_parameters(1, optimized_parameters.size() - param_num_removed );
1691  memcpy( reduced_parameters.get_data(), optimized_parameters.get_data(), (parameter_idx)*sizeof(double));
1692  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));
1693 
1694 
1695  return reduced_parameters;
1696 }
1697 
1698 
1699 
1700 
1701 
1702 
1703 
1707 void
1709 
1710  add_adaptive_layers( this );
1711 
1712 }
1713 
1717 void
1719 
1720 
1721  // create the new decomposing layer and add to the gate staructure
1723  gate_structure->combine( layer );
1724 
1725 
1726 }
1727 
1728 
1729 
1730 
1734 Gates_block*
1736 
1737 
1738  //The stringstream input to store the output messages.
1739  std::stringstream sstream;
1740 
1741  // creating block of gates
1742  Gates_block* block = new Gates_block( qbit_num );
1743 
1744  std::vector<Gates_block* > layers;
1745 
1746 
1747  if ( topology.size() > 0 ) {
1748  for ( std::vector<matrix_base<int>>::iterator it=topology.begin(); it!=topology.end(); it++) {
1749 
1750  if ( it->size() != 2 ) {
1751  std::string err("The connectivity data should contains two qubits.");
1752  throw err;
1753  }
1754 
1755  int control_qbit_loc = (*it)[0];
1756  int target_qbit_loc = (*it)[1];
1757 
1758  if ( control_qbit_loc >= qbit_num || target_qbit_loc >= qbit_num ) {
1759  std::string err("Label of control/target qubit should be less than the number of qubits in the register.");
1760  throw err;
1761  }
1762 
1763  Gates_block* layer = new Gates_block( qbit_num );
1764 
1765  layer->add_u3(target_qbit_loc);
1766  layer->add_u3(control_qbit_loc);
1767  layer->add_adaptive(target_qbit_loc, control_qbit_loc);
1768 
1769  layers.push_back(layer);
1770 
1771 
1772  }
1773  }
1774  else {
1775 
1776  // sequ
1777  for (int target_qbit_loc = 0; target_qbit_loc<qbit_num; target_qbit_loc++) {
1778  for (int control_qbit_loc = target_qbit_loc+1; control_qbit_loc<qbit_num; control_qbit_loc++) {
1779 
1780  Gates_block* layer = new Gates_block( qbit_num );
1781 
1782  layer->add_u3(target_qbit_loc);
1783  layer->add_u3(control_qbit_loc);
1784  layer->add_adaptive(target_qbit_loc, control_qbit_loc);
1785 
1786  layers.push_back(layer);
1787  }
1788  }
1789 
1790  }
1791 
1792 /*
1793  for (int idx=0; idx<layers.size(); idx++) {
1794  Gates_block* layer = (Gates_block*)layers[idx];
1795  block->add_gate( layers[idx] );
1796 
1797  }
1798 */
1799 
1800  bool randomized_adaptive_layers_loc;
1801  if ( config.count("randomized_adaptive_layers") > 0 ) {
1802  config["randomized_adaptive_layers"].get_property( randomized_adaptive_layers_loc );
1803  }
1804  else {
1805  randomized_adaptive_layers_loc = randomized_adaptive_layers;
1806  }
1807 
1808 
1809  // make difference between randomized adaptive layers and deterministic one
1810  if (randomized_adaptive_layers_loc) {
1811 
1812  std::uniform_int_distribution<> distrib_int(0, 5000);
1813 
1814  while (layers.size()>0) {
1815  int idx = distrib_int(gen) % layers.size();
1816 
1817 #ifdef __MPI__
1818  MPI_Bcast( &idx, 1, MPI_INT, 0, MPI_COMM_WORLD);
1819 #endif
1820  block->add_gate( layers[idx] );
1821  layers.erase( layers.begin() + idx );
1822  }
1823 
1824  }
1825  else {
1826  while (layers.size()>0) {
1827  block->add_gate( layers[0] );
1828  layers.erase( layers.begin() );
1829  }
1830 
1831  }
1832 
1833 
1834  return block;
1835 
1836 
1837 }
1838 
1839 
1843 void
1845 
1846  add_finalyzing_layer( this );
1847 
1848 }
1849 
1853 void
1855 
1856 
1857  // creating block of gates
1858  Gates_block* block = new Gates_block( qbit_num );
1859 /*
1860  block->add_un();
1861  block->add_ry(qbit_num-1);
1862 */
1863  for (int idx=0; idx<qbit_num; idx++) {
1864  block->add_u3(idx);
1865 // block->add_ry(idx);
1866  }
1867 
1868 
1869  // adding the opeartion block to the gates
1870  if ( gate_structure == NULL ) {
1871  throw ("N_Qubit_Decomposition_adaptive::add_finalyzing_layer: gate_structure is null pointer");
1872  }
1873  else {
1874  gate_structure->add_gate( block );
1875  }
1876 
1877 
1878 }
1879 
1880 
1881 
1882 
1887 void
1889 
1890  if ( gates.size() > 0 ) {
1891  release_gates();
1893  }
1894 
1896  combine( gate_structure );
1897  delete gate_structure;
1898 
1899 }
1900 
1905 void
1907 
1908  Umtx = import_unitary_from_binary(filename);
1909 
1910 #ifdef __DFE__
1911  if( qbit_num >= 5 ) {
1912  upload_Umtx_to_DFE();
1913  }
1914 #endif
1915 
1916 }
1921 void
1923 
1924  Umtx = Umtx_new;
1925 
1926 #ifdef __DFE__
1927  if( qbit_num >= 5 ) {
1928  upload_Umtx_to_DFE();
1929  }
1930 #endif
1931 
1932 }
1933 
1938 void
1940 
1941 
1942 
1943  Matrix_real optimized_parameters_mtx_tmp;
1944  Gates_block* gate_structure_tmp = import_gate_list_from_binary(optimized_parameters_mtx_tmp, filename, verbose);
1945 
1946  if ( gates.size() > 0 ) {
1947  gate_structure_tmp->combine( static_cast<Gates_block*>(this) );
1948 
1949  release_gates();
1950  combine( gate_structure_tmp );
1951 
1952 
1953  Matrix_real optimized_parameters_mtx_tmp2( 1, optimized_parameters_mtx_tmp.size() + optimized_parameters_mtx.size() );
1954 
1955  memcpy( optimized_parameters_mtx_tmp2.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
1956  memcpy( optimized_parameters_mtx_tmp2.get_data()+optimized_parameters_mtx.size(), optimized_parameters_mtx_tmp.get_data(), optimized_parameters_mtx_tmp.size()*sizeof(double) );
1957 
1958  optimized_parameters_mtx = optimized_parameters_mtx_tmp2;
1959  }
1960  else {
1961  combine( gate_structure_tmp );
1962  optimized_parameters_mtx = optimized_parameters_mtx_tmp;
1963  }
1964 
1965 }
1966 
1967 
1968 
1972 void
1974 
1975  if ( gates.size() == 0 ) {
1976  return;
1977  }
1978 
1979 
1980  std::stringstream sstream;
1981  sstream << "The cost function before applying the imported gate structure is:" << optimization_problem( optimized_parameters_mtx ) << std::endl;
1982 
1984  release_gates();
1986 
1987 
1988  sstream << "The cost function after applying the imported gate structure is:" << optimization_problem( optimized_parameters_mtx ) << std::endl;
1989  print(sstream, 3);
1990 
1991 
1992 
1993 }
1994 
1995 
2000 void
2002 
2003 
2004  std::stringstream sstream;
2005  sstream << "Add new layer to the adaptive gate structure." << std::endl;
2006  print(sstream, 2);
2007 
2009 
2010 
2011  combine( layer );
2012 
2014  memset( tmp.get_data(), 0, tmp.size()*sizeof(double) );
2015  memcpy( tmp.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
2016 
2017  optimized_parameters_mtx = tmp;
2018 
2019 }
2020 
2021 double
2023 
2024  Gates_block* layer = static_cast<Gates_block*>( gate_structure->get_gate(layer_idx) );
2025  int layer_start_idx = layer->get_parameter_start_idx();
2026  int layer_gate_num = layer->get_gate_num();
2027  double ThetaOver2=0.;
2028  for (int gate_idx=0; gate_idx<layer_gate_num; gate_idx++){
2029  Gate* gate_tmp = layer->get_gate(gate_idx);
2030  double parameter = optimized_parameters[layer_start_idx+gate_tmp->get_parameter_start_idx()];
2031  if (gate_tmp->get_type() == ADAPTIVE_OPERATION || gate_tmp->get_type() == CROT_OPERATION){
2032  ThetaOver2 = std::sin(parameter)*std::sin(parameter);
2033  break;
2034  }
2035  }
2036  return ThetaOver2;
2037 }
2038 
2039 
2040 
2041 
2042 
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.
A class representing a U3 gate.
Definition: U3.h:36
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
void release_gate(int idx)
Call to release one gate in the list.
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.
Header file for a class representing the X gate.
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:87
double current_minimum
The current minimum of the optimization problem.
void add_gate(Gate *gate)
Append a general gate to the list of gates.
cost_function_type cost_fnc
The chosen variant of the cost function.
std::vector< int > get_involved_qubits()
Call to get the qubits involved in the gates stored in the block of gates.
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:85
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.
std::vector< std::vector< int > > involved_qbits
Definition: Gates_block.h:58
int trace_offset
The offset in the first columns from which the "trace" is calculated. In this case Tr(A) = sum_(i-off...
int layer_num
number of gate layers
Definition: Gates_block.h:48
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.
virtual void apply_to(Matrix_real &parameters_mtx, Matrix &input, int parallel=0)
Call to apply the gate on the input array/matrix Gates_block*input.
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
Gates_block * import_gate_list_from_binary(Matrix_real &parameters, const std::string &filename, int verbosity)
?????????
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:46
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...
A class representing a RZ gate.
Definition: RZ.h:36
int get_parameter_num()
Call to get the number of free parameters.
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.
int number_of_iters
number of iterations
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...
virtual Matrix calc_one_qubit_u3(double Theta, double Phi, double Lambda)
Calculate the matrix of a U3 gate gate corresponding to the given parameters acting on a single qbit ...
Definition: Gate.cpp:695
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:495
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:814
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
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.
virtual Gates_block * clone()
Create a clone of the present class.
Class to store data of complex arrays and its properties.
Definition: matrix.h:38
A class representing a U3 gate.
Definition: RY.h:36
int size() const
Call to get the number of the allocated elements.
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:64
int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:486
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:41
void omp_set_num_threads(int num_threads)
Set the number of threads on runtime in MKL.
virtual void finalize_circuit()
Finalize the circuit.
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:370
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:73
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...
Matrix Umtx
The unitary to be decomposed.
double activation_function(double Phi, int limit)
?????
Definition: common.cpp:34
void export_gate_list_to_binary(Matrix_real &parameters, Gates_block *gates_block, const std::string &filename, int verbosity)
?????????
Matrix copy()
Call to create a copy of the matrix.
Definition: matrix.cpp:105
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:81
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.
A class representing a U3 gate.
Definition: RX.h:36
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.
void set_cost_function_variant(cost_function_type variant)
Call to set the variant of the cost function used in the calculations.
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:378
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:39
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