Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Gates_block.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 @author: Peter Rakyta, Ph.D.
18 */
23 #include <deque>
24 #include <set>
25 #include <tbb/enumerable_thread_specific.h>
26 #include "CU.h"
27 #include "CZ.h"
28 #include "CH.h"
29 #include "CNOT.h"
30 #include "CCX.h"
31 #include "SWAP.h"
32 #include "CSWAP.h"
33 #include "U1.h"
34 #include "U2.h"
35 #include "U3.h"
36 #include "RX.h"
37 #include "RY.h"
38 #include "R.h"
39 #include "RZ.h"
40 #include "CRY.h"
41 #include "CRX.h"
42 #include "CRZ.h"
43 #include "CP.h"
44 #include "CR.h"
45 #include "H.h"
46 #include "X.h"
47 #include "Y.h"
48 #include "Z.h"
49 #include "S.h"
50 #include "SDG.h"
51 #include "T.h"
52 #include "Tdg.h"
53 #include "SX.h"
54 #include "SXdg.h"
55 #include "SYC.h"
56 #include "CROT.h"
57 #include "RXX.h"
58 #include "RYY.h"
59 #include "RZZ.h"
60 #include "Adaptive.h"
61 #include "Gates_block.h"
62 #include "qgd_math.h"
63 
64 #ifdef _WIN32
65 #include <cstdio>
66 #endif
67 
69 
70 #ifdef USE_AVX
72 #endif
73 
74 static tbb::enumerable_thread_specific<int> gates_block_derivative_depth([](){ return 0; });
75 static tbb::enumerable_thread_specific<int> gates_block_fusion_depth([](){ return 0; });
76 
77 
78 //static tbb::spin_mutex my_mutex;
83 
84  // A string labeling the gate operation
85  name = "Circuit";
86 
87  // A string describing the type of the operation
89  // number of operation layers
90  layer_num = 0;
91  min_fusion = 14;
94  involved_qubits_cache_mutex = std::make_shared<tbb::spin_mutex>();
95 }
96 
97 
98 
103 Gates_block::Gates_block(int qbit_num_in) : Gate(qbit_num_in) {
104 
105  // A string labeling the gate operation
106  name = "Circuit";
107 
108  // A string describing the type of the operation
110  // number of operation layers
111  layer_num = 0;
112 
113  min_fusion = 14;
116  involved_qubits_cache_mutex = std::make_shared<tbb::spin_mutex>();
117 
118 }
119 
120 void
122  {
123  tbb::spin_mutex::scoped_lock lock(*involved_qubits_cache_mutex);
126  involved_qubits_cache.clear();
128  }
129  fusion_block.update(std::shared_ptr<Gates_block>{});
130 }
131 
132 
137 
138  release_gates();
139 
140 }
141 
145 void
147 
148  //free the alloctaed memory of the stored gates
149  for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
150 
151  Gate* gate = *it;
152  delete gate;
153 
154  }
155 
156  gates.clear();
157  layer_num = 0;
158  parameter_num = 0;
160 
161 }
162 
163 
167 void
169 
170  if ( idx>= (int)gates.size() ) return;
171 
172  // fist decrese the number of parameters
173  Gate* gate = gates[idx];
174  parameter_num -= gate->get_parameter_num();
175 
176  gates.erase( gates.begin() + idx );
177  delete gate;
178 
179  // TODO: develop a more efficient method for large circuits. Now it is updating the whole circuit
183 
184 
185 }
186 
192 Matrix
194 
195  return get_matrix( parameters, false );
196 
197 
198 }
199 
200 
201 
208 Matrix
210 
211  //The stringstream input to store the output messages.
212  std::stringstream sstream;
213 
214  // create matrix representation of the gate operations
215  Matrix block_mtx = create_identity(matrix_size);
216 
217  apply_to(parameters, block_mtx, parallel);
218 
219 #ifdef DEBUG
220  if (block_mtx.isnan()) {
221  std::string err( "Gates_block::get_matrix: block_mtx contains NaN." ) ;
222  throw( err );
223  }
224 #endif
225 
226  return block_mtx;
227 
228 
229 }
230 
231 
234  return get_matrix( parameters, 0 );
235 }
236 
239 
241  apply_to(parameters, block_mtx, parallel);
242  return block_mtx;
243 
244 }
245 
246 
247 
248 
249 namespace {
251 template<typename Params, typename Input>
252 static void apply_to_list_impl(Gates_block* self, Params& parameters_mtx, std::vector<Input>& inputs, int parallel) {
253  if (parallel == 0) {
254  for (Input& input : inputs) {
255  self->apply_to(parameters_mtx, input, parallel);
256  }
257  return;
258  }
259 
260  int work_batch = 1;
261  tbb::parallel_for( tbb::blocked_range<int>(0, static_cast<int>(inputs.size()), work_batch),
262  [&](tbb::blocked_range<int> r) {
263  for (int idx = r.begin(); idx < r.end(); ++idx) {
264  self->apply_to(parameters_mtx, inputs[idx], parallel);
265  }
266  });
267 }
268 
269 static Matrix_real precompute_block_sincos(const Matrix_real& parameters) {
270  const int parameter_count = static_cast<int>(parameters.size());
271  if (parameter_count <= 0) {
272  return Matrix_real(0, 0);
273  }
274 
275  Matrix_real sincos(parameter_count, 2);
276  qgd_sincos_batch<double>(parameters.get_data(), sincos.get_data(), parameter_count, sincos.stride);
277 
278  return sincos;
279 }
280 
281 static Matrix_real_float precompute_block_sincos(const Matrix_real_float& parameters) {
282  const int parameter_count = static_cast<int>(parameters.size());
283  if (parameter_count <= 0) {
284  return Matrix_real_float(0, 0);
285  }
286 
287  Matrix_real_float sincos(parameter_count, 2);
288  qgd_sincos_batch<float>(parameters.get_data(), sincos.get_data(), parameter_count, sincos.stride);
289 
290  return sincos;
291 }
292 
293 static void reset_identity(Matrix& mtx, int matrix_size) {
294  if (mtx.rows != matrix_size || mtx.cols != matrix_size || mtx.stride != matrix_size) {
295  mtx = Matrix(matrix_size, matrix_size);
296  }
297  memset(mtx.get_data(), 0, mtx.size()*sizeof(QGD_Complex16));
298  for (int idx = 0; idx < matrix_size; ++idx) {
299  mtx[idx*mtx.stride + idx].real = 1.0;
300  }
301 }
302 
303 static void reset_identity(Matrix_float& mtx, int matrix_size) {
304  if (mtx.rows != matrix_size || mtx.cols != matrix_size || mtx.stride != matrix_size) {
305  mtx = Matrix_float(matrix_size, matrix_size);
306  }
307  memset(mtx.get_data(), 0, mtx.size()*sizeof(QGD_Complex8));
308  for (int idx = 0; idx < matrix_size; ++idx) {
309  mtx[idx*mtx.stride + idx].real = 1.0f;
310  }
311 }
312 
313 struct BlockDerivativeWorkspace {
314  std::vector<Gate> suffix_gates;
315  std::vector<Matrix> suffix_matrices;
316  std::vector<Matrix> forward_inputs;
317  std::vector<std::vector<Matrix>> gate_gradients;
318  std::vector<int> suffix_targets;
319  Matrix suffix;
320 };
321 
322 struct BlockDerivativeWorkspaceFloat {
323  std::vector<Gate> suffix_gates;
324  std::vector<Matrix_float> suffix_matrices;
325  std::vector<Matrix_float> forward_inputs;
326  std::vector<std::vector<Matrix_float>> gate_gradients;
327  std::vector<int> suffix_targets;
328  Matrix_float suffix;
329 };
330 
331 static void prepare_suffix_gates(std::vector<Gate>& suffix_gates, size_t gate_count, int qbit_num) {
332  if (suffix_gates.size() == gate_count
333  && (suffix_gates.empty() || suffix_gates[0].get_qbit_num() == qbit_num)) {
334  return;
335  }
336 
337  suffix_gates.clear();
338  suffix_gates.reserve(gate_count);
339  for (size_t idx = 0; idx < gate_count; ++idx) {
340  suffix_gates.emplace_back(qbit_num);
341  }
342 }
343 
358 static bool should_use_suffix(int qbit_num, size_t gate_count, int parameter_num) {
359  if (qbit_num <= 4) return false;
360  if (qbit_num >= 7) return true;
361  double m = (double)(1 << qbit_num);
362  return (double)parameter_num * (double)gate_count > ((double)gate_count + (double)parameter_num) * m;
363 }
364 
365 static void prepare_all_qubits(std::vector<int>& qubits, int qbit_num) {
366  if (qubits.size() != static_cast<size_t>(qbit_num)) {
367  qubits.resize(static_cast<size_t>(qbit_num));
368  }
369  for (int idx = 0; idx < qbit_num; ++idx) {
370  qubits[static_cast<size_t>(idx)] = idx;
371  }
372 }
373 
374 static void build_suffix_gates(
375  const std::vector<Gate*>& gates,
376  int qbit_num,
377  int matrix_size,
378  Matrix_real& parameters_mtx_in,
379  const Matrix_real& precomputed_sincos,
380  std::vector<Gate>& suffix_gates,
381  std::vector<Matrix>& suffix_matrices,
382  std::vector<int>& targets,
383  Matrix& suffix) {
384 
385  prepare_suffix_gates(suffix_gates, gates.size(), qbit_num);
386  suffix_matrices.resize(gates.size());
387 
388  if (gates.size() < 2) {
389  return;
390  }
391 
392  prepare_all_qubits(targets, qbit_num);
393  reset_identity(suffix, matrix_size);
394 
395  for (int idx = static_cast<int>(gates.size()) - 1; idx >= 0; --idx) {
396  if (static_cast<size_t>(idx) + 1 < gates.size()) {
397  Matrix& suffix_after_current = suffix_matrices[static_cast<size_t>(idx)];
398  suffix.copy_to(suffix_after_current);
399  Gate& suffix_gate = suffix_gates[static_cast<size_t>(idx)];
400  suffix_gate.set_target_qbits(targets);
401  suffix_gate.set_matrix(suffix_after_current);
402  }
403 
404  Gate* operation = gates[static_cast<size_t>(idx)];
405  const int op_param_num = operation->get_parameter_num();
406  const int op_param_start_idx = operation->get_parameter_start_idx();
407  Matrix_real parameters_mtx(
408  parameters_mtx_in.get_data() + op_param_start_idx,
409  1,
410  op_param_num
411  );
412  Matrix_real precomputed_sincos_loc(
413  precomputed_sincos.get_data() + 2 * op_param_start_idx,
414  op_param_num,
415  2
416  );
417 
418  if (op_param_num == 0 && operation->get_type() != BLOCK_OPERATION) {
419  operation->apply_from_right(suffix);
420  }
421  else if (operation->get_type() == ADAPTIVE_OPERATION) {
422  operation->apply_from_right(parameters_mtx, suffix);
423  }
424  else {
425  operation->apply_from_right_inner(parameters_mtx, precomputed_sincos_loc, suffix);
426  }
427  }
428 }
429 
430 static void build_forward_inputs(
431  const std::vector<Gate*>& gates,
432  Matrix& input,
433  Matrix_real& parameters_mtx_in,
434  const Matrix_real& precomputed_sincos,
435  int parallel,
436  std::vector<Matrix>& forward_inputs) {
437 
438  forward_inputs.resize(gates.size());
439  if (gates.empty()) {
440  return;
441  }
442 
443  input.copy_to(forward_inputs[0]);
444  for (size_t idx = 0; idx + 1 < gates.size(); ++idx) {
445  forward_inputs[idx].copy_to(forward_inputs[idx + 1]);
446  Gate* operation = gates[idx];
447  const int op_param_num = operation->get_parameter_num();
448  const int op_param_start_idx = operation->get_parameter_start_idx();
449  Matrix_real parameters_mtx(
450  parameters_mtx_in.get_data() + op_param_start_idx,
451  1,
452  op_param_num
453  );
454  Matrix_real precomputed_sincos_loc(
455  precomputed_sincos.get_data() + 2 * op_param_start_idx,
456  op_param_num,
457  2
458  );
459 
460  if (op_param_num == 0) {
461  operation->apply_to(forward_inputs[idx + 1], parallel);
462  }
463  else if (operation->get_type() == ADAPTIVE_OPERATION) {
464  operation->apply_to(parameters_mtx, forward_inputs[idx + 1], parallel);
465  }
466  else {
467  operation->apply_to_inner(parameters_mtx, precomputed_sincos_loc, forward_inputs[idx + 1], parallel);
468  }
469  }
470 }
471 
472 static void build_forward_inputs(
473  const std::vector<Gate*>& gates,
474  Matrix_float& input,
475  Matrix_real_float& parameters_mtx_in,
476  const Matrix_real_float& precomputed_sincos,
477  int parallel,
478  std::vector<Matrix_float>& forward_inputs) {
479 
480  forward_inputs.resize(gates.size());
481  if (gates.empty()) {
482  return;
483  }
484 
485  input.copy_to(forward_inputs[0]);
486  for (size_t idx = 0; idx + 1 < gates.size(); ++idx) {
487  forward_inputs[idx].copy_to(forward_inputs[idx + 1]);
488  Gate* operation = gates[idx];
489  const int op_param_num = operation->get_parameter_num();
490  const int op_param_start_idx = operation->get_parameter_start_idx();
491  Matrix_real_float parameters_mtx(
492  parameters_mtx_in.get_data() + op_param_start_idx,
493  1,
494  op_param_num
495  );
496  Matrix_real_float precomputed_sincos_loc(
497  precomputed_sincos.get_data() + 2 * op_param_start_idx,
498  op_param_num,
499  2
500  );
501 
502  if (op_param_num == 0) {
503  operation->apply_to(forward_inputs[idx + 1], parallel);
504  }
505  else if (operation->get_type() == ADAPTIVE_OPERATION) {
506  operation->apply_to(parameters_mtx, forward_inputs[idx + 1], parallel);
507  }
508  else {
509  operation->apply_to_inner(parameters_mtx, precomputed_sincos_loc, forward_inputs[idx + 1], parallel);
510  }
511  }
512 }
513 
514 static void build_suffix_gates(
515  const std::vector<Gate*>& gates,
516  int qbit_num,
517  int matrix_size,
518  Matrix_real_float& parameters_mtx_in,
519  const Matrix_real_float& precomputed_sincos,
520  std::vector<Gate>& suffix_gates,
521  std::vector<Matrix_float>& suffix_matrices,
522  std::vector<int>& targets,
523  Matrix_float& suffix) {
524 
525  prepare_suffix_gates(suffix_gates, gates.size(), qbit_num);
526  suffix_matrices.resize(gates.size());
527 
528  if (gates.size() < 2) {
529  return;
530  }
531 
532  prepare_all_qubits(targets, qbit_num);
533  reset_identity(suffix, matrix_size);
534 
535  for (int idx = static_cast<int>(gates.size()) - 1; idx >= 0; --idx) {
536  if (static_cast<size_t>(idx) + 1 < gates.size()) {
537  Matrix_float& suffix_after_current = suffix_matrices[static_cast<size_t>(idx)];
538  suffix.copy_to(suffix_after_current);
539  Gate& suffix_gate = suffix_gates[static_cast<size_t>(idx)];
540  suffix_gate.set_target_qbits(targets);
541  suffix_gate.set_matrix(suffix_after_current);
542  }
543 
544  Gate* operation = gates[static_cast<size_t>(idx)];
545  const int op_param_num = operation->get_parameter_num();
546  const int op_param_start_idx = operation->get_parameter_start_idx();
547  Matrix_real_float parameters_mtx(
548  parameters_mtx_in.get_data() + op_param_start_idx,
549  1,
550  op_param_num
551  );
552  Matrix_real_float precomputed_sincos_loc(
553  precomputed_sincos.get_data() + 2 * op_param_start_idx,
554  op_param_num,
555  2
556  );
557 
558  if (op_param_num == 0 && operation->get_type() != BLOCK_OPERATION) {
559  operation->apply_from_right(suffix);
560  }
561  else if (operation->get_type() == ADAPTIVE_OPERATION) {
562  operation->apply_from_right(parameters_mtx, suffix);
563  }
564  else {
565  operation->apply_from_right_inner(parameters_mtx, precomputed_sincos_loc, suffix);
566  }
567  }
568 }
569 } // anonymous namespace
570 
571 
577 void
578 Gates_block::apply_to_list( Matrix_real& parameters_mtx, std::vector<Matrix>& inputs, int parallel ) {
579 
580  apply_to_list_impl(this, parameters_mtx, inputs, parallel);
581 
582 }
583 
584 
591 void
592 Gates_block::apply_to_list( Matrix_real_float& parameters_mtx, std::vector<Matrix_float>& inputs, int parallel ) {
593 
594  apply_to_list_impl(this, parameters_mtx, inputs, parallel);
595 
596 }
597 
598 
605 void
606 Gates_block::apply_to( Matrix_real& parameters_mtx_in, Matrix& input, int parallel ) {
607 
608  Matrix_real precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
609  apply_to_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
610 }
611 
612 
613 void
614 Gates_block::apply_to_inner( Matrix_real& parameters_mtx_in, const Matrix_real& precomputed_sincos_in, Matrix& input, int parallel ) {
615 
616 
617 
618  std::vector<int> involved_qubits = get_involved_qubits();
619 
620  if (input.rows != matrix_size ) {
621  std::string err("Gates_block::apply_to: Wrong input size in Gates_block gate apply.");
622  throw err;
623  }
624 
625  if (qbit_num > 31) {
626  std::string err("Gates_block::apply_to: Number of qubits supported up to 31");
627  throw err;
628  }
629 
630  int size = static_cast<int>(involved_qubits.size());
631 
632  if (min_fusion != -1 && qbit_num >= min_fusion && size <= (input.cols == 1 ? 5 : 2) && qbit_num != size && gates.size() > 1) {
633  auto fb = fusion_block.get();
634  int& fusion_depth = gates_block_fusion_depth.local();
635  static tbb::enumerable_thread_specific<std::deque<Matrix>> Umtx_mini_reusable_tls([](){ return std::deque<Matrix>{}; });
636  std::deque<Matrix>& Umtx_mini_reusable = Umtx_mini_reusable_tls.local();
637  if (Umtx_mini_reusable.size() <= static_cast<size_t>(fusion_depth)) {
638  Umtx_mini_reusable.resize(fusion_depth + 1);
639  }
640  Matrix& Umtx_mini = Umtx_mini_reusable[fusion_depth];
641  reset_identity(Umtx_mini, Power_of_2(size));
642  if (fb == nullptr) {
643 
644  std::vector<int> old_to_new(qbit_num, -2);
645  for (int i = 0; i < size; i++){
646  old_to_new[i] = involved_qubits[i];
647  }
648 
649  Gates_block* clone_block = clone();
650  clone_block->reorder_qubits(old_to_new);
651  clone_block->set_qbit_num(size);
652  fusion_block.update(clone_block);
653  fb = fusion_block.get();
654  }
655  fusion_depth++;
656  fb->apply_to_inner(parameters_mtx_in, precomputed_sincos_in, Umtx_mini, parallel);
657  fusion_depth--;
658 
659  static tbb::enumerable_thread_specific<std::deque<Gate>> merged_gate_reusable_tls([](){ return std::deque<Gate>{}; });
660  static tbb::enumerable_thread_specific<std::deque<int>> merged_gate_qbit_num_tls([](){ return std::deque<int>{}; });
661  std::deque<Gate>& merged_gate_reusable = merged_gate_reusable_tls.local();
662  std::deque<int>& merged_gate_qbit_num = merged_gate_qbit_num_tls.local();
663  if (merged_gate_reusable.size() <= static_cast<size_t>(fusion_depth)) {
664  merged_gate_reusable.resize(fusion_depth + 1);
665  merged_gate_qbit_num.resize(fusion_depth + 1, -1);
666  }
667  Gate& merged_gate = merged_gate_reusable[fusion_depth];
668  int& merged_gate_qbits = merged_gate_qbit_num[fusion_depth];
669  if (merged_gate_qbits != qbit_num) {
670  merged_gate = Gate(qbit_num);
671  merged_gate_qbits = qbit_num;
672  }
673  merged_gate.set_target_qbits(involved_qubits);
674  merged_gate.set_matrix(Umtx_mini);
675 
676  // Keep legacy high-qubit fused execution behavior, but route through Gate::apply_to.
677  int fused_parallel = parallel;
678  if (parallel != 0 && size <= 5 && qbit_num >= 14) {
679  fused_parallel = 2;
680  }
681  merged_gate.apply_to(input, fused_parallel);
682  } else {
683  for( size_t idx=0; idx<gates.size(); idx++) {
684  Gate* operation = gates[idx];
685  const int op_param_num = operation->get_parameter_num();
686  const int op_param_start_idx = operation->get_parameter_start_idx();
687 
688  Matrix_real parameters_mtx_loc(parameters_mtx_in.get_data() + op_param_start_idx, 1, op_param_num);
689  Matrix_real precomputed_sincos_loc(precomputed_sincos_in.get_data() + 2 * op_param_start_idx, op_param_num, 2);
690 
691  if ( parameters_mtx_loc.size() == 0 && operation->get_type() != BLOCK_OPERATION ) {
692  operation->apply_to(input, parallel);
693  }
694  else {
695  if (operation->get_type() == ADAPTIVE_OPERATION) {
696  operation->apply_to(parameters_mtx_loc, input, parallel);
697  }
698  else {
699  operation->apply_to_inner(parameters_mtx_loc, precomputed_sincos_loc, input, parallel);
700  }
701  }
702 #ifdef DEBUG
703  if (input.isnan()) {
704  std::string err( "Gates_block::apply_to: transformed matrix contains NaN." );
705  throw( err );
706  }
707 #endif
708  }
709  }
710 }
711 
712 
713 void
714 Gates_block::apply_to( Matrix_float& input, int parallel ) {
715 
716  Matrix_real_float parameters_empty(1, 0);
717  apply_to(parameters_empty, input, parallel);
718 }
719 
720 
721 void
722 Gates_block::apply_to( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel ) {
723 
724  Matrix_real_float precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
725  apply_to_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
726 }
727 
728 
729 void
730 Gates_block::apply_to_inner( Matrix_real_float& parameters_mtx_in, const Matrix_real_float& precomputed_sincos_in, Matrix_float& input, int parallel ) {
731 
732  std::vector<int> involved_qubits = get_involved_qubits();
733 
734  if (input.rows != matrix_size ) {
735  std::string err("Gates_block::apply_to(Matrix_real_float&, Matrix_float&): Wrong input size in Gates_block gate apply.");
736  throw err;
737  }
738 
739  if (qbit_num > 31) {
740  std::string err("Gates_block::apply_to(Matrix_real_float&, Matrix_float&): Number of qubits supported up to 31");
741  throw err;
742  }
743 
744  int size = static_cast<int>(involved_qubits.size());
745 
746  if (min_fusion != -1 && qbit_num >= min_fusion && size <= (input.cols == 1 ? 5 : 2) && qbit_num != size && gates.size() > 1) {
747  auto fb = fusion_block.get();
748  int& fusion_depth = gates_block_fusion_depth.local();
749  static tbb::enumerable_thread_specific<std::deque<Matrix_float>> Umtx_mini_reusable_tls([](){ return std::deque<Matrix_float>{}; });
750  std::deque<Matrix_float>& Umtx_mini_reusable = Umtx_mini_reusable_tls.local();
751  if (Umtx_mini_reusable.size() <= static_cast<size_t>(fusion_depth)) {
752  Umtx_mini_reusable.resize(fusion_depth + 1);
753  }
754  Matrix_float& Umtx_mini = Umtx_mini_reusable[fusion_depth];
755  reset_identity(Umtx_mini, Power_of_2(size));
756  if (fb == nullptr) {
757 
758  std::vector<int> old_to_new(qbit_num, -2);
759  for (int i = 0; i < size; i++) {
760  old_to_new[i] = involved_qubits[i];
761  }
762 
763  Gates_block* clone_block = clone();
764  clone_block->reorder_qubits(old_to_new);
765  clone_block->set_qbit_num(size);
766  fusion_block.update(clone_block);
767  fb = fusion_block.get();
768  }
769  fusion_depth++;
770  fb->apply_to_inner(parameters_mtx_in, precomputed_sincos_in, Umtx_mini, parallel);
771  fusion_depth--;
772 
773  static tbb::enumerable_thread_specific<std::deque<Gate>> merged_gate_reusable_tls([](){ return std::deque<Gate>{}; });
774  static tbb::enumerable_thread_specific<std::deque<int>> merged_gate_qbit_num_tls([](){ return std::deque<int>{}; });
775  std::deque<Gate>& merged_gate_reusable = merged_gate_reusable_tls.local();
776  std::deque<int>& merged_gate_qbit_num = merged_gate_qbit_num_tls.local();
777  if (merged_gate_reusable.size() <= static_cast<size_t>(fusion_depth)) {
778  merged_gate_reusable.resize(fusion_depth + 1);
779  merged_gate_qbit_num.resize(fusion_depth + 1, -1);
780  }
781  Gate& merged_gate = merged_gate_reusable[fusion_depth];
782  int& merged_gate_qbits = merged_gate_qbit_num[fusion_depth];
783  if (merged_gate_qbits != qbit_num) {
784  merged_gate = Gate(qbit_num);
785  merged_gate_qbits = qbit_num;
786  }
787  merged_gate.set_target_qbits(involved_qubits);
788  merged_gate.set_matrix(Umtx_mini);
789 
790  // Keep legacy high-qubit fused execution behavior, but route through Gate::apply_to.
791  int fused_parallel = parallel;
792  if (parallel != 0 && size <= 5 && qbit_num >= 14) {
793  fused_parallel = 2;
794  }
795  merged_gate.apply_to(input, fused_parallel);
796  }
797  else {
798  for (size_t idx = 0; idx < gates.size(); idx++) {
799  Gate* operation = gates[idx];
800  const int op_param_num = operation->get_parameter_num();
801  const int op_param_start_idx = operation->get_parameter_start_idx();
802 
803  Matrix_real_float parameters_mtx_loc(
804  parameters_mtx_in.get_data() + op_param_start_idx,
805  1,
806  op_param_num
807  );
808 
809  Matrix_real_float precomputed_sincos_loc(
810  precomputed_sincos_in.get_data() + 2 * op_param_start_idx,
811  op_param_num,
812  2
813  );
814 
815  if (parameters_mtx_loc.size() == 0 && operation->get_type() != BLOCK_OPERATION) {
816  operation->apply_to(input, parallel);
817  }
818  else {
819  if (operation->get_type() == ADAPTIVE_OPERATION) {
820  operation->apply_to(parameters_mtx_loc, input, parallel);
821  }
822  else {
823  operation->apply_to_inner(parameters_mtx_loc, precomputed_sincos_loc, input, parallel);
824  }
825  }
826 #ifdef DEBUG
827  if (input.isnan()) {
828  std::string err("Gates_block::apply_to(Matrix_real_float&, Matrix_float&): transformed matrix contains NaN.");
829  throw(err);
830  }
831 #endif
832  }
833  }
834 }
835 
836 
837 void
838 Gates_block::apply_to( Matrix_real_any& parameters_mtx_in, Matrix_any& input, int parallel ) {
839 
840  if (parameters_mtx_in.is_float64() && input.is_float64()) {
841  apply_to(parameters_mtx_in.as_float64(), input.as_float64(), parallel);
842  return;
843  }
844 
845  if (parameters_mtx_in.is_float32() && input.is_float32()) {
846  apply_to(parameters_mtx_in.as_float32(), input.as_float32(), parallel);
847  return;
848  }
849 
850  std::string err("Gates_block::apply_to(Matrix_real_any&, Matrix_any&): precision mismatch between parameters and input");
851  throw err;
852 }
853 
854 
855 
857 
864 bool is_qbit_present(std::vector<int> involved_qubits, int new_qbit, int num_of_qbits){
865 
866  bool contained=false;
867 
868  for (int idx=0; idx<num_of_qbits; idx++) {
869 
870  if(involved_qubits[idx] == new_qbit) {
871  contained=true;
872  }
873  }
874 
875  return contained;
876 
877 }
878 
879 
884 void
886 
887  Matrix_real precomputed_sincos = precompute_block_sincos(parameters_mtx);
888  apply_from_right_inner(parameters_mtx, precomputed_sincos, input);
889 }
890 
891 
892 void
893 Gates_block::apply_from_right_inner( Matrix_real& parameters_mtx, const Matrix_real& precomputed_sincos, Matrix& input ) {
894 
895  for (int idx = static_cast<int>(gates.size()) - 1; idx >= 0; --idx) {
896 
897  Gate* operation = gates[idx];
898  const int op_param_num = operation->get_parameter_num();
899  const int op_param_start_idx = operation->get_parameter_start_idx();
900 
901  Matrix_real parameters_mtx_loc(
902  parameters_mtx.get_data() + op_param_start_idx,
903  1,
904  op_param_num
905  );
906  Matrix_real precomputed_sincos_loc(
907  precomputed_sincos.get_data() + 2 * op_param_start_idx,
908  op_param_num,
909  2
910  );
911 
912  if (parameters_mtx_loc.size() == 0 && operation->get_type() != BLOCK_OPERATION) {
913  operation->apply_from_right(input);
914  }
915  else {
916  if (operation->get_type() == ADAPTIVE_OPERATION) {
917  operation->apply_from_right(parameters_mtx_loc, input);
918  }
919  else {
920  operation->apply_from_right_inner(parameters_mtx_loc, precomputed_sincos_loc, input);
921  }
922  }
923 
924 #ifdef DEBUG
925  if (input.isnan()) {
926  std::string err( "Gates_block::apply_from_right: transformed matrix contains NaN." );
927  throw( err );
928  }
929 #endif
930 
931 
932  }
933 
934 
935 }
936 
937 
938 void
940 
941  Matrix_real_float precomputed_sincos = precompute_block_sincos(parameters_mtx);
942  apply_from_right_inner(parameters_mtx, precomputed_sincos, input);
943 }
944 
945 
946 void
947 Gates_block::apply_from_right_inner( Matrix_real_float& parameters_mtx, const Matrix_real_float& precomputed_sincos, Matrix_float& input ) {
948 
949  for (int idx = static_cast<int>(gates.size()) - 1; idx >= 0; --idx) {
950 
951  Gate* operation = gates[idx];
952  const int op_param_num = operation->get_parameter_num();
953  const int op_param_start_idx = operation->get_parameter_start_idx();
954 
955  Matrix_real_float parameters_mtx_loc(
956  parameters_mtx.get_data() + op_param_start_idx,
957  1,
958  op_param_num
959  );
960 
961  Matrix_real_float precomputed_sincos_loc(
962  precomputed_sincos.get_data() + 2 * op_param_start_idx,
963  op_param_num,
964  2
965  );
966 
967  if (parameters_mtx_loc.size() == 0 && operation->get_type() != BLOCK_OPERATION) {
968  operation->apply_from_right(input);
969  }
970  else {
971  if (operation->get_type() == ADAPTIVE_OPERATION) {
972  operation->apply_from_right(parameters_mtx_loc, input);
973  }
974  else {
975  operation->apply_from_right_inner(parameters_mtx_loc, precomputed_sincos_loc, input);
976  }
977  }
978 
979 #ifdef DEBUG
980  if (input.isnan()) {
981  std::string err("Gates_block::apply_from_right(Matrix_real_float&, Matrix_float&): transformed matrix contains NaN.");
982  throw(err);
983  }
984 #endif
985  }
986 }
987 
988 
995 std::vector<Matrix>
996 Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input, int parallel ) {
997 
998  std::vector<Matrix> grad;
999  apply_derivate_to(parameters_mtx_in, input, parallel, grad);
1000  return grad;
1001 }
1002 
1003 
1004 void
1005 Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input, int parallel, std::vector<Matrix>& output ) {
1006 
1007  apply_derivate_to(parameters_mtx_in, input, parallel, output, 0, true);
1008 }
1009 
1010 
1011 void
1012 Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input, int parallel, std::vector<Matrix>& output, size_t output_offset, bool resize_output ) {
1013 
1014  const size_t output_size = output_offset + static_cast<size_t>(parameter_num);
1015  if (resize_output) {
1016  output.resize(output_size);
1017  }
1018  else if (output.size() < output_size) {
1019  std::string err("Gates_block::apply_derivate_to: output vector is too small.");
1020  throw err;
1021  }
1022 
1023  Matrix_real precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
1024  static tbb::enumerable_thread_specific<std::deque<BlockDerivativeWorkspace>> workspace_tls;
1025  std::deque<BlockDerivativeWorkspace>& workspaces = workspace_tls.local();
1026  int& workspace_depth = gates_block_derivative_depth.local();
1027  if (workspaces.size() <= static_cast<size_t>(workspace_depth)) {
1028  workspaces.resize(static_cast<size_t>(workspace_depth) + 1);
1029  }
1030  BlockDerivativeWorkspace& workspace = workspaces[static_cast<size_t>(workspace_depth)];
1031  std::vector<Gate>& suffix_gates = workspace.suffix_gates;
1032  std::vector<Matrix>& suffix_matrices = workspace.suffix_matrices;
1033  std::vector<Matrix>& forward_inputs = workspace.forward_inputs;
1034  forward_inputs.resize(gates.size());
1035  workspace.gate_gradients.resize(gates.size());
1036 
1037  const bool use_suffix = should_use_suffix(qbit_num, gates.size(), parameter_num);
1038 
1039  if (parallel == 0 || gates.size() < 2) {
1040  if (use_suffix) {
1041  build_suffix_gates(gates, qbit_num, matrix_size, parameters_mtx_in, precomputed_sincos, suffix_gates, suffix_matrices, workspace.suffix_targets, workspace.suffix);
1042  }
1043  build_forward_inputs(gates, input, parameters_mtx_in, precomputed_sincos, parallel, forward_inputs);
1044  }
1045  else {
1046  if (use_suffix) {
1047  tbb::parallel_invoke(
1048  [&]() {
1049  build_suffix_gates(gates, qbit_num, matrix_size, parameters_mtx_in, precomputed_sincos, suffix_gates, suffix_matrices, workspace.suffix_targets, workspace.suffix);
1050  },
1051  [&]() {
1052  build_forward_inputs(gates, input, parameters_mtx_in, precomputed_sincos, parallel, forward_inputs);
1053  }
1054  );
1055  } else {
1056  build_forward_inputs(gates, input, parameters_mtx_in, precomputed_sincos, parallel, forward_inputs);
1057  }
1058  }
1059 
1060  auto apply_derivative_for_gate = [&](size_t deriv_idx) {
1061  Gate* gate_deriv = gates[deriv_idx];
1062 
1063  // for constant gate no gradient component is calculated
1064  if ( gate_deriv->get_parameter_num() == 0 ) {
1065  return;
1066  }
1067 
1068  int deriv_parameter_idx = gate_deriv->get_parameter_start_idx();
1069 
1070  int& derivative_depth = gates_block_derivative_depth.local();
1071  static tbb::enumerable_thread_specific<std::deque<Matrix>> input_loc_reusable_tls([](){ return std::deque<Matrix>{}; });
1072  std::deque<Matrix>& input_loc_reusable = input_loc_reusable_tls.local();
1073  if (input_loc_reusable.size() <= static_cast<size_t>(derivative_depth)) {
1074  input_loc_reusable.resize(derivative_depth + 1);
1075  }
1076  Matrix& input_loc = input_loc_reusable[derivative_depth];
1077  forward_inputs[deriv_idx].copy_to(input_loc);
1078 
1079  derivative_depth++;
1080  Gate* operation = gates[deriv_idx];
1081  const int op_param_num = operation->get_parameter_num();
1082  const int op_param_start_idx = operation->get_parameter_start_idx();
1083  Matrix_real parameters_mtx(parameters_mtx_in.get_data() + op_param_start_idx, 1, op_param_num);
1084  std::vector<Matrix>& grad_loc = workspace.gate_gradients[deriv_idx];
1085 
1086  if (operation->get_type() == ADAPTIVE_OPERATION) {
1087  operation->apply_derivate_to( parameters_mtx, input_loc, parallel, grad_loc );
1088  }
1089  else {
1090  operation->apply_derivate_to( parameters_mtx, input_loc, parallel, grad_loc );
1091  }
1092 
1093  if (deriv_idx + 1 < gates.size()) {
1094  if (use_suffix) {
1095  Gate& suffix_gate = suffix_gates[deriv_idx];
1096  if (parallel == 0) {
1097  suffix_gate.apply_to_list(grad_loc, parallel);
1098  }
1099  else {
1100  tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(grad_loc.size()), 1),
1101  [&](tbb::blocked_range<int> r) {
1102  for (int grad_idx = r.begin(); grad_idx < r.end(); ++grad_idx) {
1103  suffix_gate.apply_to(grad_loc[static_cast<size_t>(grad_idx)], parallel);
1104  }
1105  });
1106  }
1107  } else {
1108  // Prefix-only: apply remaining gates sequentially using fast dedicated kernels.
1109  for (size_t g = deriv_idx + 1; g < gates.size(); ++g) {
1110  Gate* op = gates[g];
1111  const int pn = op->get_parameter_num();
1112  const int ps = op->get_parameter_start_idx();
1113  if (pn == 0) {
1114  for (auto& m : grad_loc) op->apply_to(m, parallel);
1115  } else {
1116  Matrix_real pm(parameters_mtx_in.get_data() + ps, 1, pn);
1117  Matrix_real psc(precomputed_sincos.get_data() + 2 * ps, pn, 2);
1118  if (op->get_type() == ADAPTIVE_OPERATION) {
1119  for (auto& m : grad_loc) op->apply_to(pm, m, parallel);
1120  } else {
1121  for (auto& m : grad_loc) op->apply_to_inner(pm, psc, m, parallel);
1122  }
1123  }
1124  }
1125  }
1126  }
1127  derivative_depth--;
1128 
1129 
1130  for ( int idx = 0; idx<(int)grad_loc.size(); idx++ ) {
1131  grad_loc[idx].copy_to(output[output_offset + static_cast<size_t>(deriv_parameter_idx + idx)]);
1132  }
1133  };
1134 
1135  if (parallel == 0) {
1136  for (size_t deriv_idx=0; deriv_idx<gates.size(); ++deriv_idx) {
1137  apply_derivative_for_gate(deriv_idx);
1138  }
1139  return;
1140  }
1141 
1142  int work_batch = 1;
1143  tbb::parallel_for( tbb::blocked_range<size_t>(0,gates.size(),work_batch), [&](tbb::blocked_range<size_t> r) {
1144  for (size_t deriv_idx=r.begin(); deriv_idx<r.end(); ++deriv_idx) {
1145  apply_derivative_for_gate(deriv_idx);
1146  }
1147  });
1148 
1149 
1150 }
1151 
1152 
1161 std::vector<Matrix_float>
1162 Gates_block::apply_derivate_to( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel ) {
1163 
1164  std::vector<Matrix_float> grad;
1165  apply_derivate_to(parameters_mtx_in, input, parallel, grad);
1166  return grad;
1167 }
1168 
1169 
1170 void
1171 Gates_block::apply_derivate_to( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel, std::vector<Matrix_float>& output ) {
1172 
1173  apply_derivate_to(parameters_mtx_in, input, parallel, output, 0, true);
1174 }
1175 
1176 
1177 void
1178 Gates_block::apply_derivate_to( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel, std::vector<Matrix_float>& output, size_t output_offset, bool resize_output ) {
1179 
1180  const size_t output_size = output_offset + static_cast<size_t>(parameter_num);
1181  if (resize_output) {
1182  output.resize(output_size);
1183  }
1184  else if (output.size() < output_size) {
1185  std::string err("Gates_block::apply_derivate_to(Matrix_real_float): output vector is too small.");
1186  throw err;
1187  }
1188 
1189  Matrix_real_float precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
1190  static tbb::enumerable_thread_specific<std::deque<BlockDerivativeWorkspaceFloat>> workspace_tls;
1191  std::deque<BlockDerivativeWorkspaceFloat>& workspaces = workspace_tls.local();
1192  int& workspace_depth = gates_block_derivative_depth.local();
1193  if (workspaces.size() <= static_cast<size_t>(workspace_depth)) {
1194  workspaces.resize(static_cast<size_t>(workspace_depth) + 1);
1195  }
1196  BlockDerivativeWorkspaceFloat& workspace = workspaces[static_cast<size_t>(workspace_depth)];
1197  std::vector<Gate>& suffix_gates = workspace.suffix_gates;
1198  std::vector<Matrix_float>& suffix_matrices = workspace.suffix_matrices;
1199  std::vector<Matrix_float>& forward_inputs = workspace.forward_inputs;
1200  forward_inputs.resize(gates.size());
1201  workspace.gate_gradients.resize(gates.size());
1202 
1203  const bool use_suffix = should_use_suffix(qbit_num, gates.size(), parameter_num);
1204 
1205  if (parallel == 0 || gates.size() < 2) {
1206  if (use_suffix) {
1207  build_suffix_gates(gates, qbit_num, matrix_size, parameters_mtx_in, precomputed_sincos, suffix_gates, suffix_matrices, workspace.suffix_targets, workspace.suffix);
1208  }
1209  build_forward_inputs(gates, input, parameters_mtx_in, precomputed_sincos, parallel, forward_inputs);
1210  }
1211  else {
1212  if (use_suffix) {
1213  tbb::parallel_invoke(
1214  [&]() {
1215  build_suffix_gates(gates, qbit_num, matrix_size, parameters_mtx_in, precomputed_sincos, suffix_gates, suffix_matrices, workspace.suffix_targets, workspace.suffix);
1216  },
1217  [&]() {
1218  build_forward_inputs(gates, input, parameters_mtx_in, precomputed_sincos, parallel, forward_inputs);
1219  }
1220  );
1221  } else {
1222  build_forward_inputs(gates, input, parameters_mtx_in, precomputed_sincos, parallel, forward_inputs);
1223  }
1224  }
1225 
1226  auto apply_derivative_for_gate = [&](size_t deriv_idx) {
1227  Gate* gate_deriv = gates[deriv_idx];
1228 
1229  // for constant gate no gradient component is calculated
1230  if ( gate_deriv->get_parameter_num() == 0 ) {
1231  return;
1232  }
1233 
1234  int deriv_parameter_idx = gate_deriv->get_parameter_start_idx();
1235 
1236  int& derivative_depth = gates_block_derivative_depth.local();
1237  static tbb::enumerable_thread_specific<std::deque<Matrix_float>> input_loc_reusable_tls([](){ return std::deque<Matrix_float>{}; });
1238  std::deque<Matrix_float>& input_loc_reusable = input_loc_reusable_tls.local();
1239  if (input_loc_reusable.size() <= static_cast<size_t>(derivative_depth)) {
1240  input_loc_reusable.resize(derivative_depth + 1);
1241  }
1242  Matrix_float& input_loc = input_loc_reusable[derivative_depth];
1243  forward_inputs[deriv_idx].copy_to(input_loc);
1244 
1245  derivative_depth++;
1246  Gate* operation = gates[deriv_idx];
1247  const int op_param_num = operation->get_parameter_num();
1248  const int op_param_start_idx = operation->get_parameter_start_idx();
1249  Matrix_real_float parameters_mtx(parameters_mtx_in.get_data() + op_param_start_idx, 1, op_param_num);
1250  std::vector<Matrix_float>& grad_loc = workspace.gate_gradients[deriv_idx];
1251 
1252  if (operation->get_type() == ADAPTIVE_OPERATION) {
1253  operation->apply_derivate_to( parameters_mtx, input_loc, parallel, grad_loc );
1254  }
1255  else {
1256  operation->apply_derivate_to( parameters_mtx, input_loc, parallel, grad_loc );
1257  }
1258 
1259  if (deriv_idx + 1 < gates.size()) {
1260  if (use_suffix) {
1261  Gate& suffix_gate = suffix_gates[deriv_idx];
1262  if (parallel == 0) {
1263  suffix_gate.apply_to_list(grad_loc, parallel);
1264  }
1265  else {
1266  tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(grad_loc.size()), 1),
1267  [&](tbb::blocked_range<int> r) {
1268  for (int grad_idx = r.begin(); grad_idx < r.end(); ++grad_idx) {
1269  suffix_gate.apply_to(grad_loc[static_cast<size_t>(grad_idx)], parallel);
1270  }
1271  });
1272  }
1273  } else {
1274  // Prefix-only: apply remaining gates sequentially using fast dedicated kernels.
1275  for (size_t g = deriv_idx + 1; g < gates.size(); ++g) {
1276  Gate* op = gates[g];
1277  const int pn = op->get_parameter_num();
1278  const int ps = op->get_parameter_start_idx();
1279  if (pn == 0) {
1280  for (auto& m : grad_loc) op->apply_to(m, parallel);
1281  } else {
1282  Matrix_real_float pm(parameters_mtx_in.get_data() + ps, 1, pn);
1283  Matrix_real_float psc(precomputed_sincos.get_data() + 2 * ps, pn, 2);
1284  if (op->get_type() == ADAPTIVE_OPERATION) {
1285  for (auto& m : grad_loc) op->apply_to(pm, m, parallel);
1286  } else {
1287  for (auto& m : grad_loc) op->apply_to_inner(pm, psc, m, parallel);
1288  }
1289  }
1290  }
1291  }
1292  }
1293  derivative_depth--;
1294 
1295 
1296  for ( int idx = 0; idx<(int)grad_loc.size(); idx++ ) {
1297  grad_loc[idx].copy_to(output[output_offset + static_cast<size_t>(deriv_parameter_idx + idx)]);
1298  }
1299  };
1300 
1301  if (parallel == 0) {
1302  for (size_t deriv_idx=0; deriv_idx<gates.size(); ++deriv_idx) {
1303  apply_derivative_for_gate(deriv_idx);
1304  }
1305  return;
1306  }
1307 
1308  int work_batch = 1;
1309  tbb::parallel_for( tbb::blocked_range<size_t>(0,gates.size(),work_batch), [&](tbb::blocked_range<size_t> r) {
1310  for (size_t deriv_idx=r.begin(); deriv_idx<r.end(); ++deriv_idx) {
1311  apply_derivative_for_gate(deriv_idx);
1312  }
1313  });
1314 
1315 
1316 }
1317 
1318 
1319 std::vector<Matrix>
1320 Gates_block::apply_to_combined( Matrix_real& parameters_mtx_in, Matrix& input, int parallel ) {
1321 
1322  Matrix_real precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
1323  return apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
1324 }
1325 
1326 
1327 void
1328 Gates_block::apply_to_combined( Matrix_real& parameters_mtx_in, Matrix& input, int parallel, std::vector<Matrix>& output ) {
1329 
1330  Matrix_real precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
1331  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, output);
1332 }
1333 
1334 
1335 std::vector<Matrix>
1336 Gates_block::apply_to_combined_inner( Matrix_real& parameters_mtx_in, const Matrix_real& precomputed_sincos, Matrix& input, int parallel ) {
1337 
1338  std::vector<Matrix> ret;
1339  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, ret);
1340  return ret;
1341 }
1342 
1343 
1344 void
1345 Gates_block::apply_to_combined_inner( Matrix_real& parameters_mtx_in, const Matrix_real& precomputed_sincos, Matrix& input, int parallel, std::vector<Matrix>& output ) {
1346 
1347  output.resize(static_cast<size_t>(parameter_num) + 1);
1348  if (parallel == 0) {
1349  input.copy_to(output[0]);
1350  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
1351  apply_derivate_to(parameters_mtx_in, input, parallel, output, 1, false);
1352  }
1353  else {
1354  tbb::parallel_invoke(
1355  [&]() {
1356  input.copy_to(output[0]);
1357  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
1358  },
1359  [&]() {
1360  apply_derivate_to(parameters_mtx_in, input, parallel, output, 1, false);
1361  }
1362  );
1363  }
1364 }
1365 
1366 
1367 std::vector<Matrix_float>
1368 Gates_block::apply_to_combined( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel ) {
1369 
1370  Matrix_real_float precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
1371  return apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
1372 }
1373 
1374 
1375 void
1376 Gates_block::apply_to_combined( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel, std::vector<Matrix_float>& output ) {
1377 
1378  Matrix_real_float precomputed_sincos = precompute_block_sincos(parameters_mtx_in);
1379  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, output);
1380 }
1381 
1382 
1383 std::vector<Matrix_float>
1384 Gates_block::apply_to_combined_inner( Matrix_real_float& parameters_mtx_in, const Matrix_real_float& precomputed_sincos, Matrix_float& input, int parallel ) {
1385 
1386  std::vector<Matrix_float> ret;
1387  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, ret);
1388  return ret;
1389 }
1390 
1391 
1392 void
1393 Gates_block::apply_to_combined_inner( Matrix_real_float& parameters_mtx_in, const Matrix_real_float& precomputed_sincos, Matrix_float& input, int parallel, std::vector<Matrix_float>& output ) {
1394 
1395  output.resize(static_cast<size_t>(parameter_num) + 1);
1396  if (parallel == 0) {
1397  input.copy_to(output[0]);
1398  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
1399  apply_derivate_to(parameters_mtx_in, input, parallel, output, 1, false);
1400  }
1401  else {
1402  tbb::parallel_invoke(
1403  [&]() {
1404  input.copy_to(output[0]);
1405  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
1406  },
1407  [&]() {
1408  apply_derivate_to(parameters_mtx_in, input, parallel, output, 1, false);
1409  }
1410  );
1411  }
1412 }
1413 
1414 
1420 
1421  // create the operation
1422  Gate* operation = static_cast<Gate*>(new U1( qbit_num, target_qbit ));
1423 
1424  // adding the operation to the end of the list of gates
1425  add_gate( operation );
1426 }
1427 
1433 
1434  // create the operation
1435  Gate* gate = static_cast<Gate*>(new U1( qbit_num, target_qbit ));
1436 
1437  // adding the operation to the front of the list of gates
1438  add_gate_to_front( gate );
1439 }
1440 
1446 
1447  // create the operation
1448  Gate* operation = static_cast<Gate*>(new U2( qbit_num, target_qbit ));
1449 
1450  // adding the operation to the end of the list of gates
1451  add_gate( operation );
1452 }
1453 
1459 
1460  // create the operation
1461  Gate* gate = static_cast<Gate*>(new U2( qbit_num, target_qbit ));
1462 
1463  // adding the operation to the front of the list of gates
1464  add_gate_to_front( gate );
1465 }
1466 
1472 
1473  // create the operation
1474  Gate* operation = static_cast<Gate*>(new U3( qbit_num, target_qbit ));
1475 
1476  // adding the operation to the end of the list of gates
1477  add_gate( operation );
1478 }
1479 
1485 
1486  // create the operation
1487  Gate* gate = static_cast<Gate*>(new U3( qbit_num, target_qbit ));
1488 
1489  // adding the operation to the front of the list of gates
1490  add_gate_to_front( gate );
1491 
1492 }
1493 
1499 
1500  // create the operation
1501  Gate* operation = static_cast<Gate*>(new RX( qbit_num, target_qbit));
1502 
1503  // adding the operation to the end of the list of gates
1504  add_gate( operation );
1505 }
1506 
1512 
1513  // create the operation
1514  Gate* gate = static_cast<Gate*>(new RX( qbit_num, target_qbit ));
1515 
1516  // adding the operation to the front of the list of gates
1517  add_gate_to_front( gate );
1518 
1519 }
1520 
1525 void Gates_block::add_rxx(std::vector<int> target_qbits) {
1526 
1527  // create the operation
1528  Gate* operation = static_cast<Gate*>(new RXX( qbit_num, target_qbits));
1529 
1530  // adding the operation to the end of the list of gates
1531  add_gate( operation );
1532 }
1533 
1539 
1540  // create the operation
1541  Gate* gate = static_cast<Gate*>(new RXX( qbit_num, target_qbits ));
1542 
1543  // adding the operation to the front of the list of gates
1544  add_gate_to_front( gate );
1545 
1546 }
1547 
1552 void Gates_block::add_ryy(std::vector<int> target_qbits) {
1553 
1554  // create the operation
1555  Gate* operation = static_cast<Gate*>(new RYY( qbit_num, target_qbits));
1556 
1557  // adding the operation to the end of the list of gates
1558  add_gate( operation );
1559 }
1560 
1566 
1567  // create the operation
1568  Gate* gate = static_cast<Gate*>(new RYY( qbit_num, target_qbits ));
1569 
1570  // adding the operation to the front of the list of gates
1571  add_gate_to_front( gate );
1572 
1573 }
1574 
1579 void Gates_block::add_rzz(std::vector<int> target_qbits) {
1580 
1581  // create the operation
1582  Gate* operation = static_cast<Gate*>(new RZZ( qbit_num, target_qbits));
1583 
1584  // adding the operation to the end of the list of gates
1585  add_gate( operation );
1586 }
1587 
1593 
1594  // create the operation
1595  Gate* gate = static_cast<Gate*>(new RZZ( qbit_num, target_qbits ));
1596 
1597  // adding the operation to the front of the list of gates
1598  add_gate_to_front( gate );
1599 
1600 }
1606 
1607  // create the operation
1608  Gate* operation = static_cast<Gate*>(new R( qbit_num, target_qbit));
1609 
1610  // adding the operation to the end of the list of gates
1611  add_gate( operation );
1612 }
1613 
1619 
1620  // create the operation
1621  Gate* gate = static_cast<Gate*>(new R( qbit_num, target_qbit ));
1622 
1623  // adding the operation to the front of the list of gates
1624  add_gate_to_front( gate );
1625 
1626 }
1627 
1628 
1634 
1635  // create the operation
1636  Gate* operation = static_cast<Gate*>(new RY( qbit_num, target_qbit));
1637 
1638  // adding the operation to the end of the list of gates
1639  add_gate( operation );
1640 }
1641 
1642 
1648 
1649  // create the operation
1650  Gate* gate = static_cast<Gate*>(new RY( qbit_num, target_qbit ));
1651 
1652  // adding the operation to the front of the list of gates
1653  add_gate_to_front( gate );
1654 
1655 }
1656 
1657 
1658 
1659 
1666 
1667  // create the operation
1668  Gate* operation = static_cast<Gate*>(new CRY( qbit_num, target_qbit, control_qbit));
1669 
1670  // adding the operation to the end of the list of gates
1671  add_gate( operation );
1672 }
1673 
1674 
1675 
1682 
1683  // create the operation
1684  Gate* gate = static_cast<Gate*>(new CRY( qbit_num, target_qbit, control_qbit ));
1685 
1686  // adding the operation to the front of the list of gates
1687  add_gate_to_front( gate );
1688 
1689 }
1690 
1697 
1698  // create the operation
1699  Gate* operation = static_cast<Gate*>(new CRX( qbit_num, target_qbit, control_qbit));
1700 
1701  // adding the operation to the end of the list of gates
1702  add_gate( operation );
1703 }
1704 
1705 
1706 
1713 
1714  // create the operation
1715  Gate* gate = static_cast<Gate*>(new CRX( qbit_num, target_qbit, control_qbit ));
1716 
1717  // adding the operation to the front of the list of gates
1718  add_gate_to_front( gate );
1719 
1720 }
1721 
1728 
1729  // create the operation
1730  Gate* operation = static_cast<Gate*>(new CRZ( qbit_num, target_qbit, control_qbit));
1731 
1732  // adding the operation to the end of the list of gates
1733  add_gate( operation );
1734 }
1735 
1736 
1737 
1744 
1745  // create the operation
1746  Gate* gate = static_cast<Gate*>(new CRZ( qbit_num, target_qbit, control_qbit ));
1747 
1748  // adding the operation to the front of the list of gates
1749  add_gate_to_front( gate );
1750 
1751 }
1752 
1759 
1760  // create the operation
1761  Gate* operation = static_cast<Gate*>(new CP( qbit_num, target_qbit, control_qbit));
1762 
1763  // adding the operation to the end of the list of gates
1764  add_gate( operation );
1765 }
1766 
1767 
1768 
1775 
1776  // create the operation
1777  Gate* gate = static_cast<Gate*>(new CP( qbit_num, target_qbit, control_qbit ));
1778 
1779  // adding the operation to the front of the list of gates
1780  add_gate_to_front( gate );
1781 
1782 }
1783 
1790 
1791  // create the operation
1792  Gate* operation = static_cast<Gate*>(new CR( qbit_num, target_qbit, control_qbit));
1793 
1794  // adding the operation to the end of the list of gates
1795  add_gate( operation );
1796 }
1797 
1798 
1799 
1806 
1807  // create the operation
1808  Gate* gate = static_cast<Gate*>(new CR( qbit_num, target_qbit, control_qbit ));
1809 
1810  // adding the operation to the front of the list of gates
1811  add_gate_to_front( gate );
1812 
1813 }
1814 
1815 
1822 
1823  // create the operation
1824  Gate* operation = static_cast<Gate*>(new CROT( qbit_num, target_qbit, control_qbit));
1825 
1826  // adding the operation to the end of the list of gates
1827  add_gate( operation );
1828 }
1829 
1830 
1831 
1838 
1839  // create the operation
1840  Gate* gate = static_cast<Gate*>(new CROT( qbit_num, target_qbit, control_qbit ));
1841 
1842  // adding the operation to the front of the list of gates
1843  add_gate_to_front( gate );
1844 
1845 }
1846 
1852 
1853  // create the operation
1854  Gate* operation = static_cast<Gate*>(new RZ( qbit_num, target_qbit));
1855 
1856  // adding the operation to the end of the list of gates
1857  add_gate( operation );
1858 }
1859 
1865 
1866  // create the operation
1867  Gate* gate = static_cast<Gate*>(new RZ( qbit_num, target_qbit ));
1868 
1869  // adding the operation to the front of the list of gates
1870  add_gate_to_front( gate );
1871 
1872 }
1873 
1874 
1881 
1882  // new cnot operation
1883  Gate* gate = static_cast<Gate*>(new CNOT(qbit_num, target_qbit, control_qbit ));
1884 
1885  // append the operation to the list
1886  add_gate(gate);
1887 
1888 }
1889 
1890 
1891 
1898 
1899  // new cnot operation
1900  Gate* gate = static_cast<Gate*>(new CNOT(qbit_num, target_qbit, control_qbit ));
1901 
1902  // put the operation to tghe front of the list
1903  add_gate_to_front(gate);
1904 
1905 }
1906 
1907 
1908 
1909 
1916 
1917  // new cz operation
1918  Gate* gate = static_cast<Gate*>(new CZ(qbit_num, target_qbit, control_qbit ));
1919 
1920  // append the operation to the list
1921  add_gate(gate);
1922 
1923 }
1924 
1925 
1926 
1933 
1934  // new cz operation
1935  Gate* gate = static_cast<Gate*>(new CZ(qbit_num, target_qbit, control_qbit ));
1936 
1937  // put the operation to tghe front of the list
1938  add_gate_to_front(gate);
1939 
1940 }
1941 
1947 
1948  // create the operation
1949  Gate* operation = static_cast<Gate*>(new H( qbit_num, target_qbit));
1950 
1951  // adding the operation to the end of the list of gates
1952  add_gate( operation );
1953 }
1954 
1960 
1961  // create the operation
1962  Gate* gate = static_cast<Gate*>(new H( qbit_num, target_qbit ));
1963 
1964  // adding the operation to the front of the list of gates
1965  add_gate_to_front( gate );
1966 
1967 }
1968 
1969 
1970 
1971 
1972 
1978 
1979  // create the operation
1980  Gate* operation = static_cast<Gate*>(new X( qbit_num, target_qbit));
1981 
1982  // adding the operation to the end of the list of gates
1983  add_gate( operation );
1984 }
1985 
1991 
1992  // create the operation
1993  Gate* gate = static_cast<Gate*>(new X( qbit_num, target_qbit ));
1994 
1995  // adding the operation to the front of the list of gates
1996  add_gate_to_front( gate );
1997 
1998 }
1999 
2000 
2001 
2007 
2008  // create the operation
2009  Gate* operation = static_cast<Gate*>(new Y( qbit_num, target_qbit));
2010 
2011  // adding the operation to the end of the list of gates
2012  add_gate( operation );
2013 }
2014 
2020 
2021  // create the operation
2022  Gate* gate = static_cast<Gate*>(new Y( qbit_num, target_qbit ));
2023 
2024  // adding the operation to the front of the list of gates
2025  add_gate_to_front( gate );
2026 
2027 }
2028 
2029 
2030 
2036 
2037  // create the operation
2038  Gate* operation = static_cast<Gate*>(new Z( qbit_num, target_qbit));
2039 
2040  // adding the operation to the end of the list of gates
2041  add_gate( operation );
2042 }
2043 
2049 
2050  // create the operation
2051  Gate* gate = static_cast<Gate*>(new Z( qbit_num, target_qbit ));
2052 
2053  // adding the operation to the front of the list of gates
2054  add_gate_to_front( gate );
2055 
2056 }
2057 
2058 
2059 
2060 
2066 
2067  // create the operation
2068  Gate* operation = static_cast<Gate*>(new S( qbit_num, target_qbit));
2069 
2070  // adding the operation to the end of the list of gates
2071  add_gate( operation );
2072 }
2073 
2079 
2080  // create the operation
2081  Gate* gate = static_cast<Gate*>(new S( qbit_num, target_qbit ));
2082 
2083  // adding the operation to the front of the list of gates
2084  add_gate_to_front( gate );
2085 
2086 }
2087 
2088 
2089 
2095 
2096  // create the operation
2097  Gate* operation = static_cast<Gate*>(new SDG( qbit_num, target_qbit));
2098 
2099  // adding the operation to the end of the list of gates
2100  add_gate( operation );
2101 }
2102 
2108 
2109  // create the operation
2110  Gate* gate = static_cast<Gate*>(new SDG( qbit_num, target_qbit ));
2111 
2112  // adding the operation to the front of the list of gates
2113  add_gate_to_front( gate );
2114 
2115 }
2116 
2117 
2118 
2119 
2125 
2126  // create the operation
2127  Gate* operation = static_cast<Gate*>(new T( qbit_num, target_qbit));
2128 
2129  // adding the operation to the end of the list of gates
2130  add_gate( operation );
2131 }
2132 
2138 
2139  // create the operation
2140  Gate* gate = static_cast<Gate*>(new T( qbit_num, target_qbit ));
2141 
2142  // adding the operation to the front of the list of gates
2143  add_gate_to_front( gate );
2144 
2145 }
2146 
2147 
2148 
2149 
2155 
2156  // create the operation
2157  Gate* operation = static_cast<Gate*>(new Tdg( qbit_num, target_qbit));
2158 
2159  // adding the operation to the end of the list of gates
2160  add_gate( operation );
2161 }
2162 
2168 
2169  // create the operation
2170  Gate* gate = static_cast<Gate*>(new Tdg( qbit_num, target_qbit ));
2171 
2172  // adding the operation to the front of the list of gates
2173  add_gate_to_front( gate );
2174 
2175 }
2176 
2177 
2178 
2179 
2180 
2181 
2187 
2188  // create the operation
2189  Gate* operation = static_cast<Gate*>(new SX( qbit_num, target_qbit));
2190 
2191  // adding the operation to the end of the list of gates
2192  add_gate( operation );
2193 }
2194 
2200 
2201  // create the operation
2202  Gate* gate = static_cast<Gate*>(new SX( qbit_num, target_qbit ));
2203 
2204  // adding the operation to the front of the list of gates
2205  add_gate_to_front( gate );
2206 
2207 }
2208 
2209 
2215 
2216  // create the operation
2217  Gate* operation = static_cast<Gate*>(new SXdg( qbit_num, target_qbit));
2218 
2219  // adding the operation to the end of the list of gates
2220  add_gate( operation );
2221 }
2222 
2228 
2229  // create the operation
2230  Gate* gate = static_cast<Gate*>(new SXdg( qbit_num, target_qbit ));
2231 
2232  // adding the operation to the front of the list of gates
2233  add_gate_to_front( gate );
2234 
2235 }
2236 
2237 
2244 
2245  // new cz operation
2246  Gate* gate = static_cast<Gate*>(new SYC(qbit_num, target_qbit, control_qbit ));
2247 
2248  // append the operation to the list
2249  add_gate(gate);
2250 
2251 }
2252 
2253 
2254 
2261 
2262  // new cz operation
2263  Gate* gate = static_cast<Gate*>(new SYC(qbit_num, target_qbit, control_qbit ));
2264 
2265  // put the operation to tghe front of the list
2266  add_gate_to_front(gate);
2267 
2268 }
2269 
2270 
2271 
2272 
2279 
2280  // new cz operation
2281  Gate* gate = static_cast<Gate*>(new CH(qbit_num, target_qbit, control_qbit ));
2282 
2283  // append the operation to the list
2284  add_gate(gate);
2285 
2286 }
2287 
2288 
2289 
2296 
2297  // new cz operation
2298  Gate* gate = static_cast<Gate*>(new CH(qbit_num, target_qbit, control_qbit ));
2299 
2300  // put the operation to tghe front of the list
2301  add_gate_to_front(gate);
2302 
2303 }
2304 
2311 
2312  // new cz operation
2313  Gate* gate = static_cast<Gate*>(new CU(qbit_num, target_qbit, control_qbit ));
2314 
2315  // append the operation to the list
2316  add_gate(gate);
2317 
2318 }
2319 
2320 
2321 
2328 
2329  // new cz operation
2330  Gate* gate = static_cast<Gate*>(new CU(qbit_num, target_qbit, control_qbit ));
2331 
2332  // put the operation to tghe front of the list
2333  add_gate_to_front(gate);
2334 
2335 }
2336 
2342 void Gates_block::add_ccx( int target_qbit, const std::vector<int>& control_qbits) {
2343  // new ccx operation
2344  Gate* gate = static_cast<Gate*>(new CCX(qbit_num, target_qbit, control_qbits));
2345  // append the operation to the list
2346  add_gate(gate);
2347 }
2348 
2354 void Gates_block::add_ccx_to_front( int target_qbit, const std::vector<int>& control_qbits) {
2355  // new ccx operation
2356  Gate* gate = static_cast<Gate*>(new CCX(qbit_num, target_qbit, control_qbits));
2357  // put the operation to the front of the list
2358  add_gate_to_front(gate);
2359 }
2360 
2365 void Gates_block::add_swap( const std::vector<int>& target_qbits) {
2366  // new swap operation
2367  Gate* gate = static_cast<Gate*>(new SWAP(qbit_num, target_qbits));
2368  // append the operation to the list
2369  add_gate(gate);
2370 }
2371 
2376 void Gates_block::add_swap_to_front( const std::vector<int>& target_qbits) {
2377  // new swap operation
2378  Gate* gate = static_cast<Gate*>(new SWAP(qbit_num, target_qbits));
2379  // put the operation to the front of the list
2380  add_gate_to_front(gate);
2381 }
2382 
2388 void Gates_block::add_cswap( const std::vector<int>& target_qbits, const std::vector<int>& control_qbits) {
2389  // new cswap operation
2390  Gate* gate = static_cast<Gate*>(new CSWAP(qbit_num, target_qbits, control_qbits));
2391  // append the operation to the list
2392  add_gate(gate);
2393 }
2394 
2400 void Gates_block::add_cswap_to_front( const std::vector<int>& target_qbits, const std::vector<int>& control_qbits) {
2401  // new cswap operation
2402  Gate* gate = static_cast<Gate*>(new CSWAP(qbit_num, target_qbits, control_qbits));
2403  // put the operation to the front of the list
2404  add_gate_to_front(gate);
2405 }
2406 
2411 void Gates_block::add_gates( std::vector<Gate*> gates_in) {
2412 
2413  for(std::vector<Gate*>::iterator it = gates_in.begin(); it != gates_in.end(); ++it) {
2414  add_gate( *it );
2415  }
2416 
2417 }
2418 
2419 
2424 void Gates_block::add_gates_to_front( std::vector<Gate*> gates_in) {
2425 
2426  // adding gates in reversed order!!
2427  for(std::vector<Gate*>::iterator it = gates_in.end(); it != gates_in.begin(); --it) {
2428  add_gate_to_front( *it );
2429  }
2430 
2431 }
2432 
2433 
2434 
2441 
2442  // create the operation
2443  Gate* operation = static_cast<Gate*>(new Adaptive( qbit_num, target_qbit, control_qbit));
2444 
2445  // adding the operation to the end of the list of gates
2446  add_gate( operation );
2447 }
2448 
2449 
2456 
2457  // create the operation
2458  Gate* gate = static_cast<Gate*>(new Adaptive( qbit_num, target_qbit, control_qbit ));
2459 
2460  // adding the operation to the front of the list of gates
2461  add_gate_to_front( gate );
2462 
2463 }
2464 
2465 
2473  const Matrix& operation_mtx,
2474  const std::vector<int>& target_qbits,
2475  const std::vector<int>& control_qbits
2476 ) {
2477 
2478  if (operation_mtx.rows != matrix_size || operation_mtx.cols != matrix_size) {
2479  std::string err("Gates_block::add_general_operation: operation matrix has invalid size.");
2480  throw err;
2481  }
2482 
2483  Gate* operation = new Gate(qbit_num);
2484  operation->set_matrix(operation_mtx);
2485  if (!target_qbits.empty()) {
2486  operation->set_target_qbits(target_qbits);
2487  }
2488  if (!control_qbits.empty()) {
2489  operation->set_control_qbits(control_qbits);
2490  }
2491  add_gate(operation);
2492 }
2493 
2494 
2495 
2501 
2502  //set the number of qubit in the gate
2503  gate->set_qbit_num( qbit_num );
2504 
2505  gate->clear_children();
2506  gate->clear_parents();
2507  // determine the parents of the gate
2508  determine_parents( gate );
2509 
2510  // append the gate to the list
2511  gates.push_back(gate);
2513 
2514  // set the parameter starting index in the parameters array used to execute the circuit.
2516 
2517  // increase the number of parameters by the number of parameters
2519 
2520  // increase the number of layers if necessary
2521  if (gate->get_type() == BLOCK_OPERATION) {
2522  layer_num = layer_num + 1;
2523  }
2524 
2525 }
2526 
2527 
2528 
2534 
2535 
2536  // set the number of qubit in the gate
2537  gate->set_qbit_num( qbit_num );
2538  gate->clear_children();
2539  gate->clear_parents();
2540 
2541  // determine the parents of the gate
2542  determine_children( gate );
2543 
2544  gates.insert( gates.begin(), gate);
2546 
2547  // increase the number of U3 gate parameters by the number of parameters
2549 
2550  // increase the number of layers if necessary
2551  if (gate->get_type() == BLOCK_OPERATION) {
2552  layer_num = layer_num + 1;
2553  }
2554 
2555  // TODO: develop a more efficient method for large circuits. Now it is updating the whole circuit
2558 
2559 }
2560 
2561 
2562 
2568 void
2569 Gates_block::insert_gate( Gate* gate, int idx ) {
2570 
2571 
2572  // set the number of qubit in the gate
2573  gate->set_qbit_num( qbit_num );
2574 
2575  gates.insert( gates.begin()+idx, gate);
2577 
2578  // increase the number of U3 gate parameters by the number of parameters
2580 
2581  // increase the number of layers if necessary
2582  if (gate->get_type() == BLOCK_OPERATION) {
2583  layer_num = layer_num + 1;
2584  }
2585 
2586  // TODO: develop a more efficient method for large circuits. Now it is updating the whole circuit
2589 
2590 
2591 }
2592 
2593 
2598 void Gates_block::add_gate_nums( std::map<std::string, int>& gate_nums ) {
2599 
2600  for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
2601 
2602  // get the specific gate in the circuit (might be a gate or another subcircuit)
2603  Gate* gate = *it;
2604 
2605 
2606  if (gate->get_type() == BLOCK_OPERATION) {
2607  Gates_block* circuit = static_cast<Gates_block*>(gate);
2608  circuit->add_gate_nums( gate_nums );
2609  }
2610  else {
2611  std::string gate_name = gate->get_name();
2612 
2613  if( gate_nums.find(gate_name) == gate_nums.end() ) {
2614  gate_nums[ gate_name ] = 1;
2615  }
2616  else {
2617  gate_nums[ gate_name ] = gate_nums[ gate_name ] + 1;
2618  }
2619  }
2620 
2621  }
2622 
2623 }
2624 
2625 
2630 std::map<std::string, int> Gates_block::get_gate_nums() {
2631 
2632  std::map<std::string, int> gate_nums;
2633 
2634  add_gate_nums( gate_nums );
2635 
2636  return gate_nums;
2637 
2638 }
2639 
2640 
2646  return parameter_num;
2647 }
2648 
2649 
2650 
2651 
2657  return static_cast<int>(gates.size());
2658 }
2659 
2660 
2666 void Gates_block::list_gates( const Matrix_real &parameters, int start_index ) {
2667 //TODO: rewrite the function with switch statement
2668 
2669  //The stringstream input to store the output messages.
2670  std::stringstream sstream;
2671  sstream << std::endl << "The gates in the list of gates:" << std::endl;
2672  print(sstream, 1);
2673 
2674  int gate_idx = start_index;
2675  int parameter_idx = 0;
2676  double *parameters_data = parameters.get_data();
2677  //const_cast <Matrix_real&>(parameters);
2678 
2679 
2680  for(size_t op_idx = 0; op_idx<gates.size(); op_idx++) {
2681 
2682  Gate* gate = gates[op_idx];
2683 
2684  if (gate->get_type() == CNOT_OPERATION) {
2685  CNOT* cnot_gate = static_cast<CNOT*>(gate);
2686  std::stringstream sstream;
2687  sstream << gate_idx << "th gate: CNOT with control qubit: " << cnot_gate->get_control_qbit() << " and target qubit: " << cnot_gate->get_target_qbit() << std::endl;
2688  print(sstream, 1);
2689  gate_idx = gate_idx + 1;
2690  }
2691  else if (gate->get_type() == CROT_OPERATION) {
2692  CROT* crot_gate = static_cast<CROT*>(gate);
2693  std::stringstream sstream;
2694  double theta0,phi0;
2695  theta0 = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2696  phi0 = std::fmod( parameters_data[parameter_idx+1], 2*M_PI);
2697  parameter_idx = parameter_idx + 2;
2698  sstream << gate_idx << "th gate: CROT with control qubit: " << crot_gate->get_control_qbit() << " and target qubit: " << crot_gate->get_target_qbit()<< " and parameters theta=" << theta0 <<" and phi="<< phi0 << std::endl;
2699  print(sstream, 1);
2700  gate_idx = gate_idx + 1;
2701  }
2702  else if (gate->get_type() == CZ_OPERATION) {
2703  CZ* cz_gate = static_cast<CZ*>(gate);
2704  std::stringstream sstream;
2705  sstream << gate_idx << "th gate: CZ with control qubit: " << cz_gate->get_control_qbit() << " and target qubit: " << cz_gate->get_target_qbit() << std::endl;
2706  print(sstream, 1);
2707  gate_idx = gate_idx + 1;
2708  }
2709  else if (gate->get_type() == CH_OPERATION) {
2710  CH* ch_gate = static_cast<CH*>(gate);
2711  std::stringstream sstream;
2712  sstream << gate_idx << "th gate: CH with control qubit: " << ch_gate->get_control_qbit() << " and target qubit: " << ch_gate->get_target_qbit() << std::endl;
2713  print(sstream, 1);
2714  gate_idx = gate_idx + 1;
2715  }
2716  else if (gate->get_type() == SYC_OPERATION) {
2717  SYC* syc_gate = static_cast<SYC*>(gate);
2718  std::stringstream sstream;
2719  sstream << gate_idx << "th gate: Sycamore gate with control qubit: " << syc_gate->get_control_qbit() << " and target qubit: " << syc_gate->get_target_qbit() << std::endl;
2720  print(sstream, 1);
2721  gate_idx = gate_idx + 1;
2722  }
2723  else if (gate->get_type() == U1_OPERATION) {
2724  U1* u1_gate = static_cast<U1*>(gate);
2725  double lambda = std::fmod(parameters_data[parameter_idx], 2*M_PI);
2726  parameter_idx = parameter_idx + 1;
2727  std::stringstream sstream;
2728  sstream << gate_idx << "th gate: U1 on target qubit: " << u1_gate->get_target_qbit() << " with parameter lambda = " << lambda << std::endl;
2729  print(sstream, 1);
2730  gate_idx = gate_idx + 1;
2731  }
2732  else if (gate->get_type() == U2_OPERATION) {
2733  U2* u2_gate = static_cast<U2*>(gate);
2734  double phi = std::fmod(parameters_data[parameter_idx], 2*M_PI);
2735  double lambda = std::fmod(parameters_data[parameter_idx+1], 2*M_PI);
2736  parameter_idx = parameter_idx + 2;
2737  std::stringstream sstream;
2738  sstream << gate_idx << "th gate: U2 on target qubit: " << u2_gate->get_target_qbit() << " with parameters phi = " << phi << " and lambda = " << lambda << std::endl;
2739  print(sstream, 1);
2740  gate_idx = gate_idx + 1;
2741  }
2742  else if (gate->get_type() == U3_OPERATION) {
2743  U3* u3_gate = static_cast<U3*>(gate);
2744  double theta = std::fmod(parameters_data[parameter_idx], 4*M_PI);
2745  double phi = std::fmod(parameters_data[parameter_idx+1], 2*M_PI);
2746  double lambda = std::fmod(parameters_data[parameter_idx+2], 2*M_PI);
2747  parameter_idx = parameter_idx + 3;
2748  std::stringstream sstream;
2749  sstream << gate_idx << "th gate: U3 on target qubit: " << u3_gate->get_target_qbit() << " with parameters theta = " << theta << ", phi = " << phi << " and lambda = " << lambda << std::endl;
2750  print(sstream, 1);
2751  gate_idx = gate_idx + 1;
2752  }
2753  else if (gate->get_type() == R_OPERATION) {
2754  // definig the rotation parameter
2755  double vartheta,varphi;
2756  // get the inverse parameters of the U3 rotation
2757  R* r_gate = static_cast<R*>(gate);
2758  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2759  varphi = std::fmod( parameters_data[parameter_idx+1], 2*M_PI);
2760  parameter_idx = parameter_idx + 2;
2761 
2762  std::stringstream sstream;
2763  sstream << gate_idx << "th gate: R on target qubit: " << r_gate->get_target_qbit() << " and with parameters theta = " << vartheta << " and phi:" << varphi << std::endl;
2764  print(sstream, 1);
2765  gate_idx = gate_idx + 1;
2766  }
2767  else if (gate->get_type() == RX_OPERATION) {
2768  // definig the rotation parameter
2769  double vartheta;
2770  // get the inverse parameters of the U3 rotation
2771  RX* rx_gate = static_cast<RX*>(gate);
2772  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2773  parameter_idx = parameter_idx + 1;
2774 
2775  std::stringstream sstream;
2776  sstream << gate_idx << "th gate: RX on target qubit: " << rx_gate->get_target_qbit() << " and with parameters theta = " << vartheta << std::endl;
2777  print(sstream, 1);
2778  gate_idx = gate_idx + 1;
2779  }
2780  else if (gate->get_type() == RXX_OPERATION) {
2781  // definig the rotation parameter
2782  double vartheta;
2783  // get the inverse parameters of the U3 rotation
2784  RXX* rxx_gate = static_cast<RXX*>(gate);
2785  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2786  parameter_idx = parameter_idx + 1;
2787  std::vector<int> involved_qbits = rxx_gate->get_involved_qubits(true);
2788  std::stringstream sstream;
2789  sstream << gate_idx << "th gate: RXX on target qubits: " << involved_qbits[0] <<" and " << involved_qbits[1] << " and with parameters theta = " << vartheta << std::endl;
2790  print(sstream, 1);
2791  gate_idx = gate_idx + 1;
2792  }
2793  else if (gate->get_type() == RYY_OPERATION) {
2794  // definig the rotation parameter
2795  double vartheta;
2796  // get the inverse parameters of the U3 rotation
2797  RYY* ryy_gate = static_cast<RYY*>(gate);
2798  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2799  parameter_idx = parameter_idx + 1;
2800  std::vector<int> involved_qbits = ryy_gate->get_involved_qubits(true);
2801  std::stringstream sstream;
2802  sstream << gate_idx << "th gate: RYY on target qubits: " << involved_qbits[0] <<" and " << involved_qbits[1] << " and with parameters theta = " << vartheta << std::endl;
2803  print(sstream, 1);
2804  gate_idx = gate_idx + 1;
2805  }
2806  else if (gate->get_type() == RZZ_OPERATION) {
2807  // definig the rotation parameter
2808  double vartheta;
2809  // get the inverse parameters of the U3 rotation
2810  RZZ* rzz_gate = static_cast<RZZ*>(gate);
2811  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2812  parameter_idx = parameter_idx + 1;
2813  std::vector<int> involved_qbits = rzz_gate->get_involved_qubits(true);
2814  std::stringstream sstream;
2815  sstream << gate_idx << "th gate: RZZ on target qubits: " << involved_qbits[0] <<" and " << involved_qbits[1] << " and with parameters theta = " << vartheta << std::endl;
2816  print(sstream, 1);
2817  gate_idx = gate_idx + 1;
2818  }
2819  else if (gate->get_type() == RY_OPERATION) {
2820  // definig the rotation parameter
2821  double vartheta;
2822  // get the inverse parameters of the U3 rotation
2823  RY* ry_gate = static_cast<RY*>(gate);
2824  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2825  parameter_idx = parameter_idx + 1;
2826 
2827  std::stringstream sstream;
2828  sstream << gate_idx << "th gate: RY on target qubit: " << ry_gate->get_target_qbit() << " and with parameters theta = " << vartheta << std::endl;
2829  print(sstream, 1);
2830  gate_idx = gate_idx + 1;
2831  }
2832  else if (gate->get_type() == CRY_OPERATION) {
2833  // definig the rotation parameter
2834  double vartheta;
2835  // get the inverse parameters of the U3 rotation
2836  CRY* cry_gate = static_cast<CRY*>(gate);
2837  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2838  parameter_idx = parameter_idx + 1;
2839 
2840  std::stringstream sstream;
2841  sstream << gate_idx << "th gate: CRY on target qubit: " << cry_gate->get_target_qbit() << ", control qubit" << cry_gate->get_control_qbit() << " and with parameters theta = " << vartheta << std::endl;
2842  print(sstream, 1);
2843  gate_idx = gate_idx + 1;
2844  }
2845  else if (gate->get_type() == CRX_OPERATION) {
2846  // definig the rotation parameter
2847  double vartheta;
2848  // get the inverse parameters of the U3 rotation
2849  CRX* crx_gate = static_cast<CRX*>(gate);
2850  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2851  parameter_idx = parameter_idx + 1;
2852 
2853  std::stringstream sstream;
2854  sstream << gate_idx << "th gate: CRX on target qubit: " << crx_gate->get_target_qbit() << ", control qubit" << crx_gate->get_control_qbit() << " and with parameters theta = " << vartheta << std::endl;
2855  print(sstream, 1);
2856  gate_idx = gate_idx + 1;
2857  }
2858  else if (gate->get_type() == CRZ_OPERATION) {
2859  // definig the rotation parameter
2860  double vartheta;
2861  // get the inverse parameters of the U3 rotation
2862  CRZ* crz_gate = static_cast<CRZ*>(gate);
2863  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2864  parameter_idx = parameter_idx + 1;
2865 
2866  std::stringstream sstream;
2867  sstream << gate_idx << "th gate: CRZ on target qubit: " << crz_gate->get_target_qbit() << ", control qubit" << crz_gate->get_control_qbit() << " and with parameters theta = " << vartheta << std::endl;
2868  print(sstream, 1);
2869  gate_idx = gate_idx + 1;
2870  }
2871  else if (gate->get_type() == CP_OPERATION) {
2872  // definig the rotation parameter
2873  double lambda;
2874  // get the inverse parameters of the U3 rotation
2875  CP* cp_gate = static_cast<CP*>(gate);
2876  lambda = std::fmod( parameters_data[parameter_idx], 2*M_PI);
2877  parameter_idx = parameter_idx + 1;
2878 
2879  std::stringstream sstream;
2880  sstream << gate_idx << "th gate: CP on target qubit: " << cp_gate->get_target_qbit() << ", control qubit" << cp_gate->get_control_qbit() << " and with parameters lambda = " << lambda << std::endl;
2881  print(sstream, 1);
2882  gate_idx = gate_idx + 1;
2883  }
2884  else if (gate->get_type() == CR_OPERATION) {
2885  // definig the rotation parameter
2886  double vartheta,varphi;
2887  // get the inverse parameters of the U3 rotation
2888  CR* cr_gate = static_cast<CR*>(gate);
2889  vartheta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2890  varphi = std::fmod(parameters_data[parameter_idx+1],2*M_PI);
2891  parameter_idx = parameter_idx + 2;
2892 
2893  std::stringstream sstream;
2894  sstream << gate_idx << "th gate: CR on target qubit: " << cr_gate->get_target_qbit() << ", control qubit" << cr_gate->get_control_qbit() << " and with parameters theta and phi = " << vartheta << " , " << varphi << std::endl;
2895  print(sstream, 1);
2896  gate_idx = gate_idx + 1;
2897  }
2898  else if (gate->get_type() == RZ_OPERATION) {
2899  // definig the rotation parameter
2900  double varphi;
2901  // get the inverse parameters of the U3 rotation
2902  RZ* rz_gate = static_cast<RZ*>(gate);
2903  varphi = std::fmod( 2*parameters_data[parameter_idx], 2*M_PI);
2904  parameter_idx = parameter_idx + 1;
2905 
2906  std::stringstream sstream;
2907  sstream << gate_idx << "th gate: RZ on target qubit: " << rz_gate->get_target_qbit() << " and with parameters varphi = " << varphi << std::endl;
2908  print(sstream, 1);
2909  gate_idx = gate_idx + 1;
2910  }
2911  else if (gate->get_type() == H_OPERATION) {
2912  // get the inverse parameters of the U3 rotation
2913  H* h_gate = static_cast<H*>(gate);
2914  std::stringstream sstream;
2915  sstream << gate_idx << "th gate: Hadamard on target qubit: " << h_gate->get_target_qbit() << std::endl;
2916  print(sstream, 1);
2917  gate_idx = gate_idx + 1;
2918  }
2919  else if (gate->get_type() == X_OPERATION) {
2920  // get the inverse parameters of the U3 rotation
2921  X* x_gate = static_cast<X*>(gate);
2922  std::stringstream sstream;
2923  sstream << gate_idx << "th gate: X on target qubit: " << x_gate->get_target_qbit() << std::endl;
2924  print(sstream, 1);
2925  gate_idx = gate_idx + 1;
2926  }
2927  else if (gate->get_type() == Y_OPERATION) {
2928  // get the inverse parameters of the U3 rotation
2929  Y* y_gate = static_cast<Y*>(gate);
2930  std::stringstream sstream;
2931  sstream << gate_idx << "th gate: Y on target qubit: " << y_gate->get_target_qbit() << std::endl;
2932  print(sstream, 1);
2933  gate_idx = gate_idx + 1;
2934  }
2935  else if (gate->get_type() == Z_OPERATION) {
2936  // get the inverse parameters of the U3 rotation
2937  Z* z_gate = static_cast<Z*>(gate);
2938  std::stringstream sstream;
2939  sstream << gate_idx << "th gate: Z on target qubit: " << z_gate->get_target_qbit() << std::endl;
2940  print(sstream, 1);
2941  gate_idx = gate_idx + 1;
2942  }
2943  else if (gate->get_type() == S_OPERATION) {
2944  // get the inverse parameters of the U3 rotation
2945  S* s_gate = static_cast<S*>(gate);
2946  std::stringstream sstream;
2947  sstream << gate_idx << "th gate: S on target qubit: " << s_gate->get_target_qbit() << std::endl;
2948  print(sstream, 1);
2949  gate_idx = gate_idx + 1;
2950  }
2951  else if (gate->get_type() == SDG_OPERATION) {
2952  // get the inverse parameters of the U3 rotation
2953  SDG* sdg_gate = static_cast<SDG*>(gate);
2954  std::stringstream sstream;
2955  sstream << gate_idx << "th gate: Sdg on target qubit: " << sdg_gate->get_target_qbit() << std::endl;
2956  print(sstream, 1);
2957  gate_idx = gate_idx + 1;
2958  }
2959  else if (gate->get_type() == SX_OPERATION) {
2960  // get the inverse parameters of the U3 rotation
2961  SX* sx_gate = static_cast<SX*>(gate);
2962 
2963  std::stringstream sstream;
2964  sstream << gate_idx << "th gate: SX on target qubit: " << sx_gate->get_target_qbit() << std::endl;
2965  print(sstream, 1);
2966  gate_idx = gate_idx + 1;
2967  }
2968  else if (gate->get_type() == SXDG_OPERATION) {
2969  // get the inverse parameters of the U3 rotation
2970  SXdg* sxdg_gate = static_cast<SXdg*>(gate);
2971 
2972  std::stringstream sstream;
2973  sstream << gate_idx << "th gate: SXdg on target qubit: " << sxdg_gate->get_target_qbit() << std::endl;
2974  print(sstream, 1);
2975  gate_idx = gate_idx + 1;
2976  }
2977  else if (gate->get_type() == BLOCK_OPERATION) {
2978  Gates_block* block_gate = static_cast<Gates_block*>(gate);
2979  const Matrix_real parameters_layer(parameters.get_data() + parameter_idx, 1, gate->get_parameter_num() );
2980  block_gate->list_gates( parameters_layer, gate_idx );
2981  parameter_idx = parameter_idx + block_gate->get_parameter_num();
2982  gate_idx = gate_idx + block_gate->get_gate_num();
2983  }
2984  else if (gate->get_type() == ADAPTIVE_OPERATION) {
2985  // definig the rotation parameter
2986  double Theta;
2987  // get the inverse parameters of the U3 rotation
2988  Adaptive* ad_gate = static_cast<Adaptive*>(gate);
2989  Theta = std::fmod( 2*parameters_data[parameter_idx], 4*M_PI);
2990  parameter_idx = parameter_idx + 1;
2991 
2992  std::stringstream sstream;
2993  sstream << gate_idx << "th gate: Adaptive gate on target qubit: " << ad_gate->get_target_qbit() << ", control qubit " << ad_gate->get_control_qbit() << " and with parameters Theta = " << Theta << std::endl;
2994  print(sstream, 1);
2995  gate_idx = gate_idx + 1;
2996  }
2997  else {
2998  std::string err("Gates_block::list_gates: unimplemented gate");
2999  throw err;
3000  }
3001 
3002  }
3003 
3004 
3005 }
3006 
3007 
3013 Gates_block*
3014 Gates_block::create_remapped_circuit( const std::map<int, int>& qbit_map ) {
3015 
3016  return create_remapped_circuit( qbit_map, qbit_num );
3017 
3018 }
3019 
3020 
3027 Gates_block*
3028 Gates_block::create_remapped_circuit( const std::map<int, int>& qbit_map, const int qbit_num_ ) {
3029 
3030 
3031  Gates_block* ret = new Gates_block( qbit_num_ );
3032  for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
3033 
3034  Gate* op = *it;
3035  switch (op->get_type()) {
3036  case CNOT_OPERATION: case CZ_OPERATION:
3037  case CH_OPERATION: case SYC_OPERATION:
3038  case U1_OPERATION: case U2_OPERATION:
3039  case U3_OPERATION: case RY_OPERATION:
3040  case CRY_OPERATION: case RX_OPERATION:
3041  case CRX_OPERATION: case CRZ_OPERATION:
3042  case CP_OPERATION: case SWAP_OPERATION:
3043  case CSWAP_OPERATION: case CCX_OPERATION:
3044  case RZ_OPERATION: case X_OPERATION:
3045  case Y_OPERATION: case Z_OPERATION:
3046  case SX_OPERATION: case BLOCK_OPERATION:
3047  case GENERAL_OPERATION:
3048  case ADAPTIVE_OPERATION: case H_OPERATION:
3049  case S_OPERATION: case SDG_OPERATION:
3050  case T_OPERATION: case TDG_OPERATION:
3051  case CU_OPERATION:
3052  case RXX_OPERATION: case RYY_OPERATION:
3053  case RZZ_OPERATION: case SXDG_OPERATION:
3054  {
3055  Gate* cloned_op = op->clone();
3056 
3057  if ( qbit_num_ > qbit_num ) {
3058  // qbit num needs to be set in prior to avoid conflict
3059  cloned_op->set_qbit_num( qbit_num_ );
3060  }
3061 
3062  // Handle target qubits (both single and vector-based)
3063  std::vector<int> target_qbits = cloned_op->get_target_qbits();
3064  if (!target_qbits.empty()) {
3065  // Vector-based targets (SWAP, CSWAP, etc.)
3066  std::vector<int> remapped_targets;
3067  for (int target_qbit : target_qbits) {
3068  if (qbit_map.find(target_qbit) != qbit_map.end()) {
3069  remapped_targets.push_back(qbit_map.at(target_qbit));
3070  } else {
3071  std::string err("Gates_block::create_remapped_circuit: Missing target qubit from the qbit map.");
3072  throw err;
3073  }
3074  }
3075  cloned_op->set_target_qbits(remapped_targets);
3076  } else {
3077  // Legacy single target qubit
3078  int target_qbit = cloned_op->get_target_qbit();
3079  if (target_qbit != -1) {
3080  if (qbit_map.find(target_qbit) != qbit_map.end()) {
3081  cloned_op->set_target_qbit(qbit_map.at(target_qbit));
3082  } else {
3083  std::string err("Gates_block::create_remapped_circuit: Missing target qubit from the qbit map.");
3084  throw err;
3085  }
3086  }
3087  }
3088 
3089  // Handle control qubits (both single and vector-based)
3090  std::vector<int> control_qbits = cloned_op->get_control_qbits();
3091  if (!control_qbits.empty()) {
3092  // Vector-based controls (CCX, CSWAP, etc.)
3093  std::vector<int> remapped_controls;
3094  for (int control_qbit : control_qbits) {
3095  if (qbit_map.find(control_qbit) != qbit_map.end()) {
3096  remapped_controls.push_back(qbit_map.at(control_qbit));
3097  } else {
3098  std::string err("Gates_block::create_remapped_circuit: Missing control qubit from the qbit map.");
3099  throw err;
3100  }
3101  }
3102  cloned_op->set_control_qbits(remapped_controls);
3103  } else {
3104  // Legacy single control qubit
3105  int control_qbit = cloned_op->get_control_qbit();
3106  if (control_qbit != -1) {
3107  if (qbit_map.find(control_qbit) != qbit_map.end()) {
3108  cloned_op->set_control_qbit(qbit_map.at(control_qbit));
3109  } else {
3110  std::string err("Gates_block::create_remapped_circuit: Missing control qubit from the qbit map.");
3111  throw err;
3112  }
3113  }
3114  }
3115 
3116  if ( qbit_num_ < qbit_num ) {
3117  // qbit num needs to be set post to avoid conflict
3118  cloned_op->set_qbit_num( qbit_num_ );
3119  }
3120 
3121  ret->add_gate( cloned_op );
3122 
3123  break;
3124  }
3125  default:
3126  std::string err("Gates_block::create_remapped_circuit: unimplemented gate");
3127  throw err;
3128  }
3129 
3130  }
3131 
3132  return ret;
3133 }
3134 
3135 
3140 void Gates_block::reorder_qubits( std::vector<int> qbit_list ) {
3141 
3142  for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
3143 
3144  Gate* gate = *it;
3145 
3146  gate->reorder_qubits( qbit_list );
3147 
3148 
3149  }
3150 
3152 }
3153 
3158 std::vector<int> Gates_block::get_involved_qubits(bool only_target) {
3159 
3160  {
3161  tbb::spin_mutex::scoped_lock lock(*involved_qubits_cache_mutex);
3162  if (only_target && involved_target_qubits_cache_valid) {
3164  }
3165  if (!only_target && involved_qubits_cache_valid) {
3166  return involved_qubits_cache;
3167  }
3168  }
3169 
3170  std::vector<int> involved_qbits;
3171  std::vector<char> seen(qbit_num > 0 ? qbit_num : 0, 0);
3172 
3173  for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
3174 
3175  Gate* gate = *it;
3176 
3177  // Use the gate's get_involved_qubits() method which properly handles
3178  // both single and vector-based qubits (SWAP, CSWAP, CCX, etc.)
3179  std::vector<int> gate_qbits = gate->get_involved_qubits(only_target);
3180  for (std::vector<int>::iterator in_it = gate_qbits.begin(); in_it != gate_qbits.end(); ++in_it){
3181  const int qbit = *in_it;
3182  if (qbit < 0) {
3183  continue;
3184  }
3185  if (qbit >= static_cast<int>(seen.size())) {
3186  if (std::find(involved_qbits.begin(), involved_qbits.end(), qbit) == involved_qbits.end()) {
3187  involved_qbits.push_back(qbit);
3188  }
3189  }
3190  else if (!seen[qbit]) {
3191  seen[qbit] = 1;
3192  involved_qbits.push_back(qbit);
3193  }
3194  }
3195 
3196  }
3197 
3198  std::sort(involved_qbits.begin(), involved_qbits.end());
3199 
3200  {
3201  tbb::spin_mutex::scoped_lock lock(*involved_qubits_cache_mutex);
3202  if (only_target) {
3203  involved_target_qubits_cache = involved_qbits;
3205  }
3206  else {
3207  involved_qubits_cache = involved_qbits;
3209  }
3210  }
3211 
3212  return involved_qbits;
3213 }
3214 
3215 
3220 std::vector<Gate*> Gates_block::get_gates() {
3221  return gates;
3222 }
3223 
3224 
3230 
3231  if (idx > (int)gates.size() ) {
3232  return NULL;
3233  }
3234 
3235  return gates[idx];
3236 }
3237 
3238 
3244 
3245  // getting the list of gates
3246  std::vector<Gate*> gates_in = op_block->get_gates();
3247 
3248  int qbit_num_loc = op_block->get_qbit_num();
3249  if ( qbit_num_loc != qbit_num ) {
3250  std::string err("Gates_block::combine: number of qubits in the circuits must be the same");
3251  throw err;
3252  }
3253 
3254  for(std::vector<Gate*>::iterator it = (gates_in).begin(); it != (gates_in).end(); ++it) {
3255  Gate* op = *it;
3256  Gate* op_cloned = op->clone();
3257  op_cloned->clear_children();
3258  op_cloned->clear_parents();
3259  add_gate( op_cloned );
3260  }
3261 
3262 }
3263 
3265  this->min_fusion = min_fusion;
3266  fusion_block.update(std::shared_ptr<Gates_block>{});
3267  for (size_t i = 0; i < gates.size(); i++){
3268  if ( gates[i]->get_type() == BLOCK_OPERATION ) {
3269  ((Gates_block*)gates[i])->set_min_fusion( min_fusion );
3270  }
3271  }
3272 }
3273 
3278 void Gates_block::set_qbit_num( int qbit_num_in ) {
3279 
3280  // setting the number of qubits
3281  Gate::set_qbit_num(qbit_num_in);
3282 
3283  // setting the number of qubit in the gates
3284  for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
3285  Gate* op = *it;
3286  switch (op->get_type()) {
3287  case CNOT_OPERATION: case CZ_OPERATION:
3288  case CH_OPERATION: case SYC_OPERATION:
3289  case U1_OPERATION: case U2_OPERATION:
3290  case U3_OPERATION:
3291  case CP_OPERATION: case SWAP_OPERATION:
3292  case CSWAP_OPERATION: case CCX_OPERATION:
3293  case RY_OPERATION: case CRY_OPERATION:
3294  case RX_OPERATION: case CR_OPERATION:
3295  case RZ_OPERATION: case X_OPERATION:
3296  case Y_OPERATION: case Z_OPERATION:
3297  case SX_OPERATION: case BLOCK_OPERATION:
3298  case GENERAL_OPERATION:
3300  case H_OPERATION: case R_OPERATION:
3301  case CU_OPERATION:
3302  case S_OPERATION: case SDG_OPERATION:
3303  case T_OPERATION: case TDG_OPERATION:
3304  case RXX_OPERATION: case RYY_OPERATION:
3305  case RZZ_OPERATION: case SXDG_OPERATION:
3306  op->set_qbit_num( qbit_num_in );
3307  break;
3308  default:
3309  std::string err("Gates_block::set_qbit_num: unimplemented gate");
3310  throw err;
3311  }
3312  }
3314 }
3315 
3316 
3322 
3323  // creatign new instance of class Gates_block
3324  Gates_block* ret = new Gates_block( qbit_num );
3325 
3327  ret->set_parents( parents );
3328  ret->set_children( children );
3329 
3330  // extracting the gates from the current class
3331  if (extract_gates( ret ) != 0 ) {
3332  std::string err("Gates_block::clone(): extracting gates was not succesfull");
3333  throw err;
3334  };
3335 
3336  return ret;
3337 
3338 }
3339 
3340 
3347 
3348  op_block->release_gates();
3349 
3350  for ( std::vector<Gate*>::iterator it=gates.begin(); it != gates.end(); ++it ) {
3351  Gate* op = *it;
3352  switch (op->get_type()) {
3353  case CNOT_OPERATION: case CZ_OPERATION:
3354  case CH_OPERATION: case SYC_OPERATION:
3355  case U1_OPERATION: case U2_OPERATION:
3356  case U3_OPERATION: case CP_OPERATION:
3357  case RY_OPERATION: case CRY_OPERATION:
3358  case CRX_OPERATION: case CRZ_OPERATION:
3359  case RX_OPERATION: case CR_OPERATION:
3360  case RZ_OPERATION: case X_OPERATION:
3361  case Y_OPERATION: case Z_OPERATION:
3362  case SX_OPERATION: case BLOCK_OPERATION:
3363  case GENERAL_OPERATION:
3365  case H_OPERATION: case R_OPERATION:
3366  case CU_OPERATION:
3367  case S_OPERATION: case SDG_OPERATION:
3368  case T_OPERATION: case TDG_OPERATION:
3369  case SWAP_OPERATION: case RXX_OPERATION:
3370  case RYY_OPERATION: case RZZ_OPERATION:
3371  case CSWAP_OPERATION: case CCX_OPERATION:
3372  case SXDG_OPERATION:
3373  {
3374  Gate* op_cloned = op->clone();
3375  op_block->add_gate( op_cloned );
3376  break; }
3377  default:
3378  std::string err("Gates_block::extract_gates: unimplemented gate");
3379  throw err;
3380  }
3381 
3382  }
3383 
3384  return 0;
3385 
3386 }
3387 
3388 
3389 
3395 
3396  for ( std::vector<Gate*>::iterator it=gates.begin(); it != gates.end(); ++it ) {
3397  Gate* op = *it;
3398 
3399  if (op->get_type() == ADAPTIVE_OPERATION) {
3400  return true;
3401  }
3402  else if (op->get_type() == BLOCK_OPERATION) {
3403  Gates_block* block_op = static_cast<Gates_block*>( op );
3404  bool ret = block_op->contains_adaptive_gate();
3405  if ( ret ) return true;
3406  }
3407  }
3408 
3409  return false;
3410 
3411 }
3412 
3413 
3414 
3415 
3422 
3423 
3424  Gate* op = gates[idx];
3425 
3426  if (op->get_type() == ADAPTIVE_OPERATION) {
3427  return true;
3428  }
3429  else if (op->get_type() == BLOCK_OPERATION) {
3430  Gates_block* block_op = static_cast<Gates_block*>( op );
3431  return block_op->contains_adaptive_gate();
3432  }
3433 
3434  return false;
3435 
3436 }
3437 
3438 
3439 
3447 Matrix
3448 Gates_block::get_reduced_density_matrix( Matrix_real& parameters_mtx, Matrix& input_state, matrix_base<int>& qbit_list_subset ) {
3449 
3450 
3451  if (input_state.cols != 1) {
3452  std::string error("Gates_block::get_reduced_density_matrix: The number of columns in input state should be 1");
3453  throw error;
3454  }
3455 
3456 
3457  if (input_state.rows != matrix_size) {
3458  std::string error("Gates_block::get_reduced_density_matrix: The number of rows in input state should be 2^qbit_num");
3459  throw error;
3460  }
3461 
3462  // determine the transformed state
3463  Matrix transformed_state = input_state.copy();
3464  if ( parameters_mtx.size() > 0 ) {
3465  bool parallel = true;
3466  apply_to( parameters_mtx, transformed_state, parallel );
3467  }
3468 
3469 
3470  int subset_qbit_num = qbit_list_subset.size();
3471  int complementary_qbit_num = qbit_num - subset_qbit_num;
3472 
3473 
3474  // list of complementary qubits
3475  matrix_base<int> qbit_list_complementary( complementary_qbit_num, 1);
3476  int qbit_idx_count = 0;
3477  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
3478 
3479  bool qbit_idx_in_subset = false;
3480 
3481  for (int subset_qbit_idx=0; subset_qbit_idx<subset_qbit_num; subset_qbit_idx++) {
3482  if ( qbit_idx == qbit_list_subset[subset_qbit_idx] ) {
3483  qbit_idx_in_subset = true;
3484  break;
3485  }
3486  }
3487 
3488 
3489  if ( qbit_idx_in_subset ) {
3490  continue;
3491  }
3492 
3493  qbit_list_complementary[qbit_idx_count] = qbit_idx;
3494  qbit_idx_count++;
3495  }
3496 
3497 
3498  // 000010000 one-hot encoded numbers indicating the bit position of the qubits in the register
3499  matrix_base<int> qbit_masks(qbit_num, 1);
3500  for (int qbit_idx=0; qbit_idx<qbit_num; qbit_idx++) {
3501  qbit_masks[ qbit_idx ] = 1 << qbit_idx;
3502  }
3503 
3504 
3505 
3506  // retrieve the reduced density matrix
3507  int rho_matrix_size = 1 << subset_qbit_num;
3508 
3509  Matrix rho(rho_matrix_size, rho_matrix_size);
3510  memset( rho.get_data(), 0, rho.size()*sizeof(QGD_Complex16) );
3511 
3512 
3513 
3514 
3515  int complementary_basis_num = 1 << complementary_qbit_num;
3516  for ( int row_idx=0; row_idx<rho_matrix_size; row_idx++ ) {
3517 
3518  // index of the amplitude in the state vector
3519  int idx = 0;
3520  for (int qbit_idx=0; qbit_idx<subset_qbit_num; qbit_idx++) {
3521  if ( row_idx & qbit_masks[ qbit_idx ] ) {
3522  idx = idx | qbit_masks[ qbit_list_subset[qbit_idx] ];
3523  }
3524  }
3525 
3526  for ( int col_idx=row_idx; col_idx<rho_matrix_size; col_idx++ ) {
3527 
3528  // index of the amplitude in the state vector
3529  int jdx = 0;
3530  for (int qbit_idx=0; qbit_idx<subset_qbit_num; qbit_idx++) {
3531  if ( col_idx & qbit_masks[ qbit_idx ] ) {
3532  jdx = jdx | qbit_masks[ qbit_list_subset[qbit_idx] ];
3533  }
3534  }
3535 
3536 
3537  // thread local storage for partial permanent
3538  tbb::combinable<QGD_Complex16> priv_addend {[](){QGD_Complex16 ret; ret.real = 0.0; ret.imag = 0.0; return ret;}};
3539 
3540 
3541  tbb::parallel_for( tbb::blocked_range<int>(0, complementary_basis_num, 1024), [&](tbb::blocked_range<int> r) {
3542 
3543  QGD_Complex16& rho_element_priv = priv_addend.local();
3544 
3545  for (int compl_idx=r.begin(); compl_idx<r.end(); compl_idx++) {
3546 
3547 
3548  int idx_loc = idx;
3549  int jdx_loc = jdx;
3550 
3551  for (int qbit_idx=0; qbit_idx<complementary_qbit_num; qbit_idx++) {
3552  if ( compl_idx & qbit_masks[ qbit_idx ] ) {
3553  idx_loc = idx_loc | qbit_masks[ qbit_list_complementary[qbit_idx] ];
3554  jdx_loc = jdx_loc | qbit_masks[ qbit_list_complementary[qbit_idx] ];
3555  }
3556  }
3557 
3558  QGD_Complex16 element_idx = transformed_state[ idx_loc ];
3559  QGD_Complex16& element_jdx = transformed_state[ jdx_loc ];
3560 
3561  // conjugate because of the bra-vector
3562  element_idx.imag = -element_idx.imag;
3563 
3564  QGD_Complex16 addend = mult( element_idx, element_jdx );
3565 
3566  rho_element_priv.real = rho_element_priv.real + addend.real;
3567  rho_element_priv.imag = rho_element_priv.imag + addend.imag;
3568 
3569 
3570  }
3571  });
3572 
3573 
3574 
3575  QGD_Complex16 rho_element;
3576  rho_element.real = 0.0;
3577  rho_element.imag = 0.0;
3578 
3579  priv_addend.combine_each([&](QGD_Complex16 &a) {
3580  rho_element.real = rho_element.real + a.real;
3581  rho_element.imag = rho_element.imag + a.imag;
3582  });
3583 
3584  rho[ row_idx * rho.stride + col_idx ].real += rho_element.real;
3585  rho[ row_idx * rho.stride + col_idx ].imag += rho_element.imag;
3586 
3587  if ( row_idx == col_idx ) {
3588  continue;
3589  }
3590 
3591  rho[ col_idx * rho.stride + row_idx ].real += rho_element.real;
3592  rho[ col_idx * rho.stride + row_idx ].imag -= rho_element.imag;
3593 
3594  }
3595 
3596 
3597  }
3598 
3599 
3600  // test the trace of the reduced density matrix
3601  double trace = 0.0;
3602  for( int idx=0; idx<rho_matrix_size; idx++) {
3603  trace = trace + rho[idx*rho.stride+idx].real;
3604  }
3605 
3606  if ( abs( trace-1.0 ) > 1e-6 ) {
3607  std::stringstream sstream;
3608  sstream << "Warning: Gates_block::get_reduced_density_matrix: The error of the trace of the reduced density matrix is " << abs( trace-1.0 ) << std::endl;
3609  print(sstream, 1);
3610  }
3611 
3612  return rho;
3613 
3614 
3615 }
3616 
3617 
3625 double Gates_block::get_second_Renyi_entropy( Matrix_real& parameters_mtx, Matrix& input_state, matrix_base<int>& qbit_list_subset ) {
3626 
3627  // determine the reduced density matrix
3628  Matrix rho = get_reduced_density_matrix( parameters_mtx, input_state, qbit_list_subset );
3629 
3630 
3631  // calculate the second Rényi entropy
3632  // Tr( rho @ rho )
3633  double trace_rho_square = 0.0;
3634 
3635  for (int idx=0; idx<rho.rows; idx++) {
3636 
3637  double trace_tmp = 0.0;
3638 
3639  for (int jdx=0; jdx<rho.rows; jdx++) {
3640  QGD_Complex16& element = rho[ idx*rho.stride + jdx ];
3641  double tmp = element.real * element.real + element.imag * element.imag;
3642 
3643  trace_tmp = trace_tmp + tmp;
3644  }
3645 
3646 
3647  trace_rho_square = trace_rho_square + trace_tmp;
3648 
3649 
3650  }
3651 
3652 
3653 
3654 
3655  double entropy = -log(trace_rho_square);
3656 
3657 
3658  return entropy;
3659 
3660 }
3661 
3662 
3669 std::vector<int> remove_list_intersection( std::vector<int>& list1, std::vector<int>& list2 ) {
3670 
3671  std::vector<int> ret = list1;
3672 
3673  for( std::vector<int>::iterator it2 = list2.begin(); it2 != list2.end(); it2++ ) {
3674 
3675  std::vector<int>::iterator element_found = std::find(ret.begin(), ret.end(), *it2);
3676 
3677  if( element_found != ret.end() ) {
3678  ret.erase( element_found );
3679  }
3680 
3681  }
3682 
3683  return ret;
3684 
3685 
3686 
3687 }
3688 
3689 
3694 void
3696 
3697 
3698  std::vector<int>&& involved_qubits = gate->get_involved_qubits();
3699 /*
3700  std::cout << "involved qubits in the current gate: " << std::endl;
3701  for( int idx=0; idx<involved_qubits.size(); idx++ ) {
3702  std::cout << involved_qubits[idx] << ", ";
3703  }
3704  std::cout << std::endl;
3705  */
3706  // iterate over gates in the circuit
3707  for( int idx=static_cast<int>(gates.size()-1); idx>=0; idx-- ) {
3708  Gate* gate_loc = gates[idx];
3709  std::vector<int>&& involved_qubits_loc = gate_loc->get_involved_qubits();
3710 
3711  std::vector<int>&& reduced_qbit_list = remove_list_intersection( involved_qubits, involved_qubits_loc );
3712 
3713  if( reduced_qbit_list.size() < involved_qubits.size() ) {
3714  // parent gate found, setting parent-child relation
3715 
3716  gate->add_parent( gate_loc );
3717  gate_loc->add_child( gate );
3718 
3719  involved_qubits = std::move(reduced_qbit_list);
3720 
3721 
3722  }
3723 
3724 
3725  // return if no further involved qubits left
3726  if( involved_qubits.size() == 0 ) {
3727  break;
3728  }
3729  }
3730 
3731 
3732 }
3733 
3734 
3735 
3740 void
3742 
3743 
3744  std::vector<int>&& involved_qubits = gate->get_involved_qubits();
3745 
3746  // iterate over gates in the circuit
3747  for( size_t idx=0; idx<gates.size(); idx++ ) {
3748  Gate* gate_loc = gates[idx];
3749  std::vector<int>&& involved_qubits_loc = gate_loc->get_involved_qubits();
3750 
3751  std::vector<int>&& reduced_qbit_list = remove_list_intersection( involved_qubits, involved_qubits_loc );
3752 
3753  if( reduced_qbit_list.size() < involved_qubits.size() ) {
3754  // child gate found, setting parent-child relation
3755 
3756  gate->add_child( gate_loc );
3757  gate_loc->add_parent( gate );
3758 
3759  involved_qubits = std::move(reduced_qbit_list);
3760 
3761 
3762  }
3763 
3764 
3765  // return if no further involved qubits left
3766  if( involved_qubits.size() == 0 ) {
3767  break;
3768  }
3769  }
3770 
3771 }
3772 
3773 
3777 void
3779 
3780  int parameter_idx = 0;
3781 
3782  for( std::vector<Gate*>::iterator gate_it = gates.begin(); gate_it != gates.end(); gate_it++ ) {
3783 
3784  Gate* gate = *gate_it;
3785 
3786  gate->set_parameter_start_idx( parameter_idx );
3787 
3788  parameter_idx = parameter_idx + gate->get_parameter_num();
3789 
3790  }
3791 
3792 }
3793 
3794 
3798 void
3800 
3801  // first clear parent/children data from the gates
3802  for( std::vector<Gate*>::iterator gate_it = gates.begin(); gate_it != gates.end(); gate_it++ ) {
3803  Gate* gate = *gate_it;
3804  gate->clear_children();
3805  gate->clear_parents();
3806  }
3807 
3808 
3809  // first clear parent/children data from the gates
3810  for( std::vector<Gate*>::iterator gate_it = gates.begin(); gate_it != gates.end(); gate_it++ ) {
3811  Gate* gate = *gate_it;
3812 
3813  // determine the parents of the gate
3814  determine_parents( gate );
3815 
3816  }
3817 
3818 
3819 
3820 
3821 }
3822 
3823 
3827 Gates_block*
3829 
3830  Gates_block* flat_circuit = new Gates_block( qbit_num );
3831 
3832  for( std::vector<Gate*>::iterator gate_it=gates.begin(); gate_it != gates.end(); gate_it++ ) {
3833 
3834  Gate* gate = *gate_it;
3835 
3836  if( gate->get_type() == BLOCK_OPERATION ) {
3837 
3838  Gates_block* circuit_inner = static_cast<Gates_block*>( gate );
3839  Gates_block* flat_circuit_inner = circuit_inner->get_flat_circuit();
3840 
3841  flat_circuit->combine( flat_circuit_inner );
3842 
3843  delete( flat_circuit_inner );
3844  }
3845  else {
3846  flat_circuit->add_gate( gate->clone() );
3847  }
3848 
3849  }
3850 
3851 
3852 
3853  return flat_circuit;
3854 
3855 
3856 }
3857 
3858 
3859 
3865 Matrix_real
3867 
3868  if ( get_parameter_start_idx() + get_parameter_num() < parameters.size() ) {
3869  std::string err("Gates_block::extract_parameters: Cant extract parameters, since th einput arary has not enough elements.");
3870  throw err;
3871  }
3872 
3873  Matrix_real extracted_parameters(1, get_parameter_num());
3874 
3875  memcpy( extracted_parameters.get_data(), parameters.get_data() + get_parameter_start_idx(), get_parameter_num()*sizeof(double) );
3876 
3877  return extracted_parameters;
3878 
3879 }
3880 
3881 
3882 #ifdef __DFE__
3883 
3884 
3889 DFEgate_kernel_type* Gates_block::convert_to_DFE_gates_with_derivates( Matrix_real& parameters_mtx, int& gatesNum, int& gateSetNum, int& redundantGateSets, bool only_derivates ) {
3890 
3892  if ( parameter_num != parameters_mtx.size() ) {
3893  std::string error("Gates_block::convert_to_DFE_gates: wrong number of parameters");
3894  throw error;
3895  }
3896 
3897  std::map<std::string, int> gate_nums = get_gate_nums();
3898  int gates_total_num = 0;
3899  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
3900  gates_total_num = gates_total_num + it->second;
3901  }
3902 
3903  int chained_gates_num = get_chained_gates_num();
3904  int gate_padding = gates_total_num % chained_gates_num == 0 ? 0 : chained_gates_num - (gates_total_num % chained_gates_num);
3905  gatesNum = gates_total_num+gate_padding;
3906 /*
3907 std::cout << "chained gates num: " << chained_gates_num << std::endl;
3908 std::cout << "number of gates: " << gatesNum << std::endl;
3909 */
3910 
3911 
3912  gateSetNum = only_derivates ? parameter_num : parameter_num+1;
3913 
3914 #ifdef __MPI__
3915  int rem = gateSetNum % (4 * world_size );
3916  if ( rem == 0 ) {
3917  redundantGateSets = 0;
3918  }
3919  else {
3920  redundantGateSets = (4 * world_size ) - (gateSetNum % (4 * world_size ));
3921  gateSetNum = gateSetNum + redundantGateSets;
3922  }
3923 #else
3924  int rem = gateSetNum % 4;
3925  if ( rem == 0 ) {
3926  redundantGateSets = 0;
3927  }
3928  else {
3929  redundantGateSets = 4 - (gateSetNum % 4);
3930  gateSetNum = gateSetNum + redundantGateSets;
3931  }
3932 #endif
3933 
3934 
3935  DFEgate_kernel_type* DFEgates = new DFEgate_kernel_type[gatesNum*gateSetNum];
3936 
3937 
3938  int gate_idx = 0;
3939  convert_to_DFE_gates( parameters_mtx, DFEgates, gate_idx );
3940 
3941 
3942  // padding with identity gates
3943  for (int idx=gate_idx; idx<gatesNum; idx++ ){
3944 
3945  DFEgate_kernel_type& DFEGate = DFEgates[idx];
3946 
3947 
3948  DFEGate.target_qbit = 0;
3949  DFEGate.control_qbit = -1;
3950  DFEGate.gate_type = U3_OPERATION;
3951  DFEGate.ThetaOver2 = (int32_t)(0);
3952  DFEGate.Phi = (int32_t)(0);
3953  DFEGate.Lambda = (int32_t)(0);
3954  DFEGate.metadata = 0;
3955 
3956  }
3957 /*
3958  for ( int idx=0; idx<gatesNum; idx++ ) {
3959 
3960  std::cout << "target qubit: " << (int)DFEgates[idx].target_qbit << " control qubit: " << (int)DFEgates[idx].control_qbit << " gate type: " << (int)DFEgates[idx].gate_type << std::endl;
3961  }
3962 */
3963 
3964  // adjust parameters for derivation
3965  if (only_derivates ) {
3966  for (int idx=1; idx<(gateSetNum-1); idx++) {
3967  memcpy(DFEgates+idx*gatesNum, DFEgates, gatesNum*sizeof(DFEgate_kernel_type));
3968  }
3969  }
3970  else {
3971  for (int idx=0; idx<(gateSetNum-1); idx++) {
3972  memcpy(DFEgates+(idx+1)*gatesNum, DFEgates, gatesNum*sizeof(DFEgate_kernel_type));
3973  }
3974  }
3975 
3976  gate_idx = 0;
3977  int gate_set_index = parameter_num-1;
3978  if (only_derivates) {
3979  adjust_parameters_for_derivation( DFEgates, gatesNum, gate_idx, gate_set_index );
3980  }
3981  else {
3982  adjust_parameters_for_derivation( DFEgates+gatesNum, gatesNum, gate_idx, gate_set_index );
3983  }
3984 
3985 /*
3986  for ( int idx=0; idx<gatesNum*(parameter_num+1); idx++ ) {
3987 
3988  std::cout << "target qubit: " << (int)DFEgates[idx].target_qbit << " control qubit: " << (int)DFEgates[idx].control_qbit << " theta: " << (int)DFEgates[idx].ThetaOver2 << " lambda: " << (int)DFEgates[idx].Lambda << std::endl;
3989  }
3990 */
3991  return DFEgates;
3992 
3993 }
3994 
3995 
4000 void Gates_block::adjust_parameters_for_derivation( DFEgate_kernel_type* DFEgates, const int gatesNum, int& gate_idx, int& gate_set_index) {
4001 
4003  //int gate_set_index = parameter_num-1;
4004 
4005  int32_t parameter_shift = (int32_t)(M_PI/2*(1<<25));
4006 
4007  for(int op_idx = gates.size()-1; op_idx>=0; op_idx--) {
4008 
4009  Gate* gate = gates[op_idx];
4010 //std::cout << gate_idx << " " << gate_set_index << " " << gate->get_type() << std::endl;
4011 
4012  if (gate->get_type() == CNOT_OPERATION) {
4013  gate_idx = gate_idx + 1;
4014  }
4015  else if (gate->get_type() == CZ_OPERATION) {
4016  gate_idx = gate_idx + 1;
4017  }
4018  else if (gate->get_type() == CH_OPERATION) {
4019  gate_idx = gate_idx + 1;
4020  }
4021  else if (gate->get_type() == SYC_OPERATION) {
4022  std::string error("Gates_block::convert_to_DFE_gates: SYC_gate not implemented");
4023  throw error;
4024  }
4025  else if (gate->get_type() == U1_OPERATION) {
4026  // Lambda parameter derivative
4027  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4028  DFEGate.Lambda = DFEGate.Lambda + parameter_shift;
4029  DFEGate.metadata = 5 + (1<<7); // Lambda parameter derivative// The 0st and 3nd element in kernel matrix should be zero for derivates and 5 = 0101, plus the leading bit indicates that derivate is processed
4030  gate_set_index = gate_set_index - 1;
4031 
4032  parameter_idx = parameter_idx - 1;
4033  gate_idx = gate_idx + 1;
4034  }
4035  else if (gate->get_type() == U2_OPERATION) {
4036  // Lambda parameter derivative
4037  DFEgate_kernel_type& DFEGate2 = DFEgates[gate_set_index*gatesNum + gate_idx];
4038  DFEGate2.Lambda = DFEGate2.Lambda + parameter_shift;
4039  DFEGate2.metadata = 5 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 5 = 0101, plus the leading bit indicates that derivate is processed
4040  gate_set_index = gate_set_index - 1;
4041 
4042  // Phi parameter derivative
4043  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4044  DFEGate.Phi = DFEGate.Phi + parameter_shift;
4045  DFEGate.metadata = 3 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 5 = 0101, plus the leading bit indicates that derivate is processed
4046  gate_set_index = gate_set_index - 1;
4047 
4048  parameter_idx = parameter_idx - 2;
4049  gate_idx = gate_idx + 1;
4050  }
4051  else if (gate->get_type() == U3_OPERATION) {
4052  // Lambda parameter derivative
4053  DFEgate_kernel_type& DFEGate3 = DFEgates[gate_set_index*gatesNum + gate_idx];
4054  DFEGate3.Lambda = DFEGate3.Lambda + parameter_shift;
4055  DFEGate3.metadata = 5 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 5 = 0101, plus the leading bit indicates that derivate is processed
4056  gate_set_index = gate_set_index - 1;
4057 
4058  // Phi parameter derivative
4059  DFEgate_kernel_type& DFEGate2 = DFEgates[gate_set_index*gatesNum + gate_idx];
4060  DFEGate2.Phi = DFEGate2.Phi + parameter_shift;
4061  DFEGate2.metadata = 3 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 5 = 0101, plus the leading bit indicates that derivate is processed
4062  gate_set_index = gate_set_index - 1;
4063 
4064  // Theta parameter derivative
4065  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4066  DFEGate.ThetaOver2 = DFEGate.ThetaOver2 + parameter_shift;
4067  DFEGate.metadata = (1<<7); // the leading bit indicates that derivate is processed
4068  gate_set_index = gate_set_index - 1;
4069 
4070  parameter_idx = parameter_idx - 3;
4071  gate_idx = gate_idx + 1;
4072  }
4073  else if (gate->get_type() == RX_OPERATION) { // Did not cehcked
4074 
4075  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4076  DFEGate.ThetaOver2 = DFEGate.ThetaOver2 + parameter_shift;
4077  DFEGate.metadata = (1<<7); // the leading bit indicates that derivate is processed
4078  gate_set_index = gate_set_index - 1;
4079 
4080  parameter_idx = parameter_idx - 1;
4081 
4082  gate_idx = gate_idx + 1;
4083  }
4084  else if (gate->get_type() == RY_OPERATION) { // Did not cehcked
4085 
4086  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4087  DFEGate.ThetaOver2 = DFEGate.ThetaOver2 + parameter_shift;
4088  DFEGate.metadata = (1<<7); // the leading bit indicates that derivate is processed
4089  gate_set_index = gate_set_index - 1;
4090 
4091  parameter_idx = parameter_idx - 1;
4092 
4093 
4094  gate_idx = gate_idx + 1;
4095  }
4096  else if (gate->get_type() == CRY_OPERATION) {
4097 
4098  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4099  DFEGate.ThetaOver2 = DFEGate.ThetaOver2 + parameter_shift;
4100  DFEGate.metadata = (1<<7); // the leading bit indicates that derivate is processed
4101  gate_set_index = gate_set_index - 1;
4102 
4103  parameter_idx = parameter_idx - 1;
4104 
4105  gate_idx = gate_idx + 1;
4106  }
4107  else if (gate->get_type() == RZ_OPERATION) { // Did not cehcked
4108 
4109  std::string error("Gates_block::adjust_parameters_for_derivation: RZ gate not implemented for DFE");
4110  throw error;
4111  }
4112  else if (gate->get_type() == CU_OPERATION) {
4113  std::string error("Gates_block::convert_to_DFE_gates: CU_gate not implemented");
4114  throw error;
4115 
4116  }
4117  else if (gate->get_type() == S_OPERATION) {
4118  std::string error("Gates_block::convert_to_DFE_gates: S_gate not implemented");
4119  throw error;
4120 
4121  }
4122  else if (gate->get_type() == SDG_OPERATION) {
4123  std::string error("Gates_block::convert_to_DFE_gates: Sdg_gate not implemented");
4124  throw error;
4125 
4126  }
4127  else if (gate->get_type() == H_OPERATION) {
4128  std::string error("Gates_block::convert_to_DFE_gates: H_gate not implemented");
4129  throw error;
4130 
4131  }
4132  else if (gate->get_type() == X_OPERATION) {
4133  std::string error("Gates_block::convert_to_DFE_gates: X_gate not implemented");
4134  throw error;
4135 
4136  }
4137  else if (gate->get_type() == Y_OPERATION) {
4138  std::string error("Gates_block::convert_to_DFE_gates: Y_gate not implemented");
4139  throw error;
4140 
4141  }
4142  else if (gate->get_type() == Z_OPERATION) {
4143  std::string error("Gates_block::convert_to_DFE_gates: Z_gate not implemented");
4144  throw error;
4145 
4146  }
4147  else if (gate->get_type() == T_OPERATION) {
4148  std::string error("Gates_block::convert_to_DFE_gates: T_gate not implemented");
4149  throw error;
4150 
4151  }
4152  else if (gate->get_type() == TDG_OPERATION) {
4153  std::string error("Gates_block::convert_to_DFE_gates: Tdg_gate not implemented");
4154  throw error;
4155  }
4156  else if (gate->get_type() == SX_OPERATION) {
4157  std::string error("Gates_block::convert_to_DFE_gates: SX_gate not implemented");
4158  throw error;
4159  }
4160  else if (gate->get_type() == SXDG_OPERATION) {
4161  std::string error("Gates_block::convert_to_DFE_gates: SXdg_gate not implemented");
4162  throw error;
4163  }
4164  else if (gate->get_type() == BLOCK_OPERATION) {
4165 
4166  Gates_block* block_gate = static_cast<Gates_block*>(gate);
4167  block_gate->adjust_parameters_for_derivation( DFEgates, gatesNum, gate_idx, gate_set_index);
4168  //gate_set_index = gate_set_index - block_gate->get_parameter_num();
4169 
4170  parameter_idx = parameter_idx - block_gate->get_parameter_num();
4171  }
4172  else if (gate->get_type() == GENERAL_OPERATION) {
4173  std::string error("Gates_block::convert_to_DFE_gates: general_gate not implemented");
4174  throw error;
4175  }
4176  else if (gate->get_type() == ADAPTIVE_OPERATION) {
4177 
4178  DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
4179  DFEGate.metadata = (1<<7); // the leading bit indicates that derivate is processed
4180  DFEGate.ThetaOver2 = DFEGate.ThetaOver2 + parameter_shift;
4181  gate_set_index = gate_set_index - 1;
4182 
4183  parameter_idx = parameter_idx - 1;
4184 
4185  gate_idx = gate_idx + 1;
4186  }
4187  else {
4188  std::string err("Gates_block::adjust_parameters_for_derivation: unimplemented gate");
4189  throw err;
4190  }
4191 
4192  }
4193 
4194 
4195 }
4196 
4197 
4203 Gates_block::convert_to_batched_DFE_gates( std::vector<Matrix_real>& parameters_mtx_vec, int& gatesNum, int& gateSetNum, int& redundantGateSets ) {
4204 
4205 
4206  std::map<std::string, int> gate_nums = get_gate_nums();
4207  int gates_total_num = 0;
4208  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
4209  gates_total_num = gates_total_num + it->second;
4210  }
4211 
4212  int chained_gates_num = get_chained_gates_num();
4213  int gate_padding = gates_total_num % chained_gates_num == 0 ? 0 : chained_gates_num - (gates_total_num % chained_gates_num);
4214  gatesNum = gates_total_num+gate_padding;
4215 /*
4216 std::cout << "chained gates num: " << chained_gates_num << std::endl;
4217 std::cout << "number of gates: " << gatesNum << std::endl;
4218 */
4219 
4220 
4221  gateSetNum = parameters_mtx_vec.size();
4222 
4223 #ifdef __MPI__
4224  int rem = gateSetNum % (4 * world_size );
4225  if ( rem == 0 ) {
4226  redundantGateSets = 0;
4227  }
4228  else {
4229  redundantGateSets = (4 * world_size ) - (gateSetNum % (4 * world_size ));
4230  gateSetNum = gateSetNum + redundantGateSets;
4231  }
4232 #else
4233  int rem = gateSetNum % 4;
4234  if ( rem == 0 ) {
4235  redundantGateSets = 0;
4236  }
4237  else {
4238  redundantGateSets = 4 - (gateSetNum % 4);
4239  gateSetNum = gateSetNum + redundantGateSets;
4240  }
4241 #endif
4242 
4243  DFEgate_kernel_type* DFEgates = new DFEgate_kernel_type[gatesNum*gateSetNum];
4244 
4245 
4246  tbb::parallel_for( 0, gateSetNum, 1, [&](int gateset_idx) {
4247 
4248  int gate_idx = gateset_idx * gatesNum;
4249 
4250  if ( gateset_idx < parameters_mtx_vec.size() ) {
4251  Matrix_real& parameters_mtx = parameters_mtx_vec[gateset_idx];
4252  convert_to_DFE_gates( parameters_mtx, DFEgates, gate_idx );
4253  }
4254 
4255  // padding with identity gates
4256  for (int idx=gate_idx; idx<(gateset_idx+1)*gatesNum; idx++ ){
4257 
4258  DFEgate_kernel_type& DFEGate = DFEgates[idx];
4259 
4260 
4261  DFEGate.target_qbit = 0;
4262  DFEGate.control_qbit = -1;
4263  DFEGate.gate_type = U3_OPERATION;
4264  DFEGate.ThetaOver2 = (int32_t)(0);
4265  DFEGate.Phi = (int32_t)(0);
4266  DFEGate.Lambda = (int32_t)(0);
4267  DFEGate.metadata = 0;
4268 
4269  gate_idx++;
4270 
4271  }
4272 
4273  });
4274 
4275 
4276  return DFEgates;
4277 
4278 }
4279 
4284 DFEgate_kernel_type* Gates_block::convert_to_DFE_gates( Matrix_real& parameters_mtx, int& gatesNum ) {
4285 
4287  if ( parameter_num != parameters_mtx.size() ) {
4288  std::string error("Gates_block::convert_to_DFE_gates: wrong number of parameters");
4289  throw error;
4290  }
4291 
4292 
4293  std::map<std::string, int> gate_nums = get_gate_nums();
4294  int gates_total_num = 0;
4295  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
4296  gates_total_num = gates_total_num + it->second;
4297  }
4298 
4299  int chained_gates_num = get_chained_gates_num();
4300  int gate_padding = chained_gates_num - (gates_total_num % chained_gates_num);
4301  gatesNum = gates_total_num+gate_padding;
4302 
4303 
4304 
4305  DFEgate_kernel_type* DFEgates = new DFEgate_kernel_type[gates_total_num+gate_padding];
4306 
4307  int gate_idx = 0;
4308  convert_to_DFE_gates( parameters_mtx, DFEgates, gate_idx );
4309 
4310 
4311  // padding with identity gates
4312  for (int idx=gate_idx; idx<gatesNum; idx++ ){
4313 
4314  DFEgate_kernel_type& DFEGate = DFEgates[idx];
4315 
4316 
4317  DFEGate.target_qbit = 0;
4318  DFEGate.control_qbit = -1;
4319  DFEGate.gate_type = U3_OPERATION;
4320  DFEGate.ThetaOver2 = (int32_t)(0);
4321  DFEGate.Phi = (int32_t)(0);
4322  DFEGate.Lambda = (int32_t)(0);
4323  DFEGate.metadata = 0;
4324 
4325  }
4326 /*
4327  for ( int idx=0; idx<gatesNum; idx++ ) {
4328 
4329  std::cout << "target qubit: " << (int)DFEgates[idx].target_qbit << " control qubit: " << (int)DFEgates[idx].control_qbit << " gate type: " << (int)DFEgates[idx].gate_type << std::endl;
4330  }
4331 */
4332 
4333 
4334  return DFEgates;
4335 
4336 }
4337 
4338 
4339 
4344 void Gates_block::convert_to_DFE_gates( const Matrix_real& parameters_mtx, DFEgate_kernel_type* DFEgates, int& start_index ) {
4345 
4346 
4347  int& gate_idx = start_index;
4349  double *parameters_data = parameters_mtx.get_data();
4350  //const_cast <Matrix_real&>(parameters);
4351 
4352 
4353  for(int op_idx = gates.size()-1; op_idx>=0; op_idx--) {
4354 
4355  Gate* gate = gates[op_idx];
4356  DFEgate_kernel_type& DFEGate = DFEgates[gate_idx];
4357 
4358  if (gate->get_type() == CNOT_OPERATION) {
4359  CNOT* cnot_gate = static_cast<CNOT*>(gate);
4360  DFEGate.target_qbit = cnot_gate->get_target_qbit();
4361  DFEGate.control_qbit = cnot_gate->get_control_qbit();
4362  DFEGate.gate_type = CNOT_OPERATION;
4363  DFEGate.ThetaOver2 = (int32_t)(M_PI/2*(1<<25));
4364  DFEGate.Phi = (int32_t)(0);
4365  DFEGate.Lambda = (int32_t)(M_PI*(1<<25));
4366  DFEGate.metadata = 0;
4367  gate_idx = gate_idx + 1;
4368  }
4369  else if (gate->get_type() == CZ_OPERATION) {
4370  CZ* cz_gate = static_cast<CZ*>(gate);
4371  DFEGate.target_qbit = cz_gate->get_target_qbit();
4372  DFEGate.control_qbit = cz_gate->get_control_qbit();
4373  DFEGate.gate_type = CZ_OPERATION;
4374  DFEGate.ThetaOver2 = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4375  DFEGate.Phi = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4376  DFEGate.Lambda = (int32_t)(M_PI*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4377  DFEGate.metadata = 0;
4378  gate_idx = gate_idx + 1;
4379  }
4380  else if (gate->get_type() == CH_OPERATION) {
4381  CH* ch_gate = static_cast<CH*>(gate);
4382  DFEGate.target_qbit = ch_gate->get_target_qbit();
4383  DFEGate.control_qbit = ch_gate->get_control_qbit();
4384  DFEGate.gate_type = CH_OPERATION;
4385  DFEGate.ThetaOver2 = (int32_t)(M_PI/4*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4386  DFEGate.Phi = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4387  DFEGate.Lambda = (int32_t)(M_PI*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4388  DFEGate.metadata = 0;
4389  gate_idx = gate_idx + 1;
4390  }
4391  else if (gate->get_type() == SYC_OPERATION) {
4392  std::string error("Gates_block::convert_to_DFE_gates: SYC_gate not implemented");
4393  throw error;
4394  }
4395  else if (gate->get_type() == U1_OPERATION) {
4396  double lambda = std::fmod(parameters_data[parameter_idx-1], 2*M_PI);
4397  parameter_idx = parameter_idx - 1;
4398 
4399  DFEGate.target_qbit = gate->get_target_qbit();
4400  DFEGate.control_qbit = -1;
4401  DFEGate.gate_type = U1_OPERATION;
4402  DFEGate.ThetaOver2 = (int32_t)(0);
4403  DFEGate.Phi = (int32_t)(0);
4404  DFEGate.Lambda = (int32_t)(lambda*(1<<25));
4405  DFEGate.metadata = 0;
4406  gate_idx = gate_idx + 1;
4407  }
4408  else if (gate->get_type() == U2_OPERATION) {
4409  double phi = std::fmod(parameters_data[parameter_idx-2], 2*M_PI);
4410  double lambda = std::fmod(parameters_data[parameter_idx-1], 2*M_PI);
4411  parameter_idx = parameter_idx - 2;
4412 
4413  DFEGate.target_qbit = gate->get_target_qbit();
4414  DFEGate.control_qbit = -1;
4415  DFEGate.gate_type = U2_OPERATION;
4416  DFEGate.ThetaOver2 = (int32_t)(M_PI/4*(1<<25)); // theta/2 = pi/4 for U2
4417  DFEGate.Phi = (int32_t)(phi*(1<<25));
4418  DFEGate.Lambda = (int32_t)(lambda*(1<<25));
4419  DFEGate.metadata = 0;
4420  gate_idx = gate_idx + 1;
4421  }
4422  else if (gate->get_type() == U3_OPERATION) {
4423  double theta = std::fmod(parameters_data[parameter_idx-3], 4*M_PI);
4424  double phi = std::fmod(parameters_data[parameter_idx-2], 2*M_PI);
4425  double lambda = std::fmod(parameters_data[parameter_idx-1], 2*M_PI);
4426  parameter_idx = parameter_idx - 3;
4427 
4428  DFEGate.target_qbit = gate->get_target_qbit();
4429  DFEGate.control_qbit = -1;
4430  DFEGate.gate_type = U3_OPERATION;
4431  DFEGate.ThetaOver2 = (int32_t)(theta/2.0*(1<<25)); // theta/2
4432  DFEGate.Phi = (int32_t)(phi*(1<<25));
4433  DFEGate.Lambda = (int32_t)(lambda*(1<<25));
4434  DFEGate.metadata = 0;
4435  gate_idx = gate_idx + 1;
4436  }
4437  else if (gate->get_type() == RX_OPERATION) {
4438  // definig the rotation parameter
4439  double varthetaOver2;
4440  // get the inverse parameters of the U3 rotation
4441  RX* rx_gate = static_cast<RX*>(gate);
4442  varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
4443  parameter_idx = parameter_idx - 1;
4444 
4445  DFEGate.target_qbit = rx_gate->get_target_qbit();
4446  DFEGate.control_qbit = -1;
4447  DFEGate.gate_type = RX_OPERATION;
4448  DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4449  DFEGate.Phi = (int32_t)(-M_PI/2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4450  DFEGate.Lambda = (int32_t)(M_PI/2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4451  DFEGate.metadata = 0;
4452 
4453  gate_idx = gate_idx + 1;
4454  }
4455  else if (gate->get_type() == RY_OPERATION) {
4456  // definig the rotation parameter
4457  double varthetaOver2;
4458  // get the inverse parameters of the U3 rotation
4459  RY* ry_gate = static_cast<RY*>(gate);
4460  varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
4461  parameter_idx = parameter_idx - 1;
4462 
4463  DFEGate.target_qbit = ry_gate->get_target_qbit();
4464  DFEGate.control_qbit = -1;
4465  DFEGate.gate_type = RY_OPERATION;
4466  DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4467  DFEGate.Phi = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4468  DFEGate.Lambda = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4469  DFEGate.metadata = 0;
4470 
4471  gate_idx = gate_idx + 1;
4472  }
4473  else if (gate->get_type() == CRY_OPERATION) {
4474  // definig the rotation parameter
4475  double Phi;
4476  // get the inverse parameters of the U3 rotation
4477  CRY* cry_gate = static_cast<CRY*>(gate);
4478  double varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
4479  parameter_idx = parameter_idx - 1;
4480  DFEGate.target_qbit = cry_gate->get_target_qbit();
4481  DFEGate.control_qbit = cry_gate->get_control_qbit();
4482  DFEGate.gate_type = CRY_OPERATION;
4483  DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4484  DFEGate.Phi = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4485  DFEGate.Lambda = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
4486  DFEGate.metadata = 0;
4487 
4488  gate_idx = gate_idx + 1;
4489  }
4490  else if (gate->get_type() == RZ_OPERATION) { // Did not cehcked
4491 
4492  std::string error("Gates_block::convert_to_DFE_gates: RZ gate not implemented for DFE");
4493  throw error;
4494  }
4495  else if (gate->get_type() == H_OPERATION) {
4496  // get the inverse parameters of the Hadamard rotation
4497  H* h_gate = static_cast<H*>(gate);
4498 
4499  std::string error("Gates_block::convert_to_DFE_gates: Hadamard gate not implemented");
4500  throw error;
4501 
4502  gate_idx = gate_idx + 1;
4503  }
4504  else if (gate->get_type() == X_OPERATION) {
4505  // get the inverse parameters of the U3 rotation
4506  X* x_gate = static_cast<X*>(gate);
4507 
4508  DFEGate.target_qbit = x_gate->get_target_qbit();
4509  DFEGate.control_qbit = -1;
4510  DFEGate.gate_type = X_OPERATION;
4511  DFEGate.ThetaOver2 = (int32_t)(M_PI/2*(1<<25));
4512  DFEGate.Phi = (int32_t)(0);
4513  DFEGate.Lambda = (int32_t)(M_PI*(1<<25));
4514  DFEGate.metadata = 0;
4515 
4516  gate_idx = gate_idx + 1;
4517  }
4518  else if (gate->get_type() == Y_OPERATION) {
4519  // get the inverse parameters of the U3 rotation
4520  Y* y_gate = static_cast<Y*>(gate);
4521 
4522  DFEGate.target_qbit = y_gate->get_target_qbit();
4523  DFEGate.control_qbit = -1;
4524  DFEGate.gate_type = Y_OPERATION;
4525  DFEGate.ThetaOver2 = (int32_t)(M_PI/2*(1<<25));
4526  DFEGate.Phi = (int32_t)(M_PI/2*(1<<25));
4527  DFEGate.Lambda = (int32_t)(M_PI/2*(1<<25));
4528  DFEGate.metadata = 0;
4529 
4530  gate_idx = gate_idx + 1;
4531  }
4532  else if (gate->get_type() == Z_OPERATION) {
4533  // get the inverse parameters of the U3 rotation
4534  Z* z_gate = static_cast<Z*>(gate);
4535 
4536  DFEGate.target_qbit = z_gate->get_target_qbit();
4537  DFEGate.control_qbit = -1;
4538  DFEGate.gate_type = Z_OPERATION;
4539  DFEGate.ThetaOver2 = (int32_t)(0);
4540  DFEGate.Phi = (int32_t)(0);
4541  DFEGate.Lambda = (int32_t)(M_PI*(1<<25));
4542  DFEGate.metadata = 0;
4543 
4544  gate_idx = gate_idx + 1;
4545  }
4546  else if (gate->get_type() == CU_OPERATION) {
4547  // get the inverse parameters of the U3 rotation
4548  CU* s_gate = static_cast<CU*>(gate);
4549  std::string error("Gates_block::convert_to_DFE_gates: CU_gate not implemented");
4550  throw error;
4551  }
4552  else if (gate->get_type() == S_OPERATION) {
4553  // get the inverse parameters of the U3 rotation
4554  S* s_gate = static_cast<S*>(gate);
4555  std::string error("Gates_block::convert_to_DFE_gates: S_gate not implemented");
4556  throw error;
4557  }
4558  else if (gate->get_type() == SDG_OPERATION) {
4559  // get the inverse parameters of the U3 rotation
4560  SDG* s_gate = static_cast<SDG*>(gate);
4561  std::string error("Gates_block::convert_to_DFE_gates: Sdg_gate not implemented");
4562  throw error;
4563  }
4564  else if (gate->get_type() == SX_OPERATION) {
4565  // get the inverse parameters of the U3 rotation
4566  SX* sx_gate = static_cast<SX*>(gate);
4567  std::string error("Gates_block::convert_to_DFE_gates: SX_gate not implemented");
4568  throw error;
4569  }
4570  else if (gate->get_type() == BLOCK_OPERATION) {
4571  Gates_block* block_gate = static_cast<Gates_block*>(gate);
4572  const Matrix_real parameters_layer_mtx(parameters_mtx.get_data() + parameter_idx - gate->get_parameter_num(), 1, gate->get_parameter_num() );
4573  block_gate->convert_to_DFE_gates( parameters_layer_mtx, DFEgates, gate_idx );
4574  parameter_idx = parameter_idx - block_gate->get_parameter_num();
4575  }
4576  else if (gate->get_type() == GENERAL_OPERATION) {
4577  std::string error("Gates_block::convert_to_DFE_gates: general_gate not implemented");
4578  throw error;
4579  }
4580  else if (gate->get_type() == ADAPTIVE_OPERATION) {
4581  // definig the rotation parameter
4582  double Phi;
4583  // get the inverse parameters of the U3 rotation
4584  Adaptive* ad_gate = static_cast<Adaptive*>(gate);
4585  double varthetaOver2 = std::fmod( activation_function(parameters_data[parameter_idx-1], ad_gate->get_limit()), 2*M_PI);
4586  parameter_idx = parameter_idx - 1;
4587  DFEGate.target_qbit = ad_gate->get_target_qbit();
4588  DFEGate.control_qbit = ad_gate->get_control_qbit();
4589  DFEGate.gate_type = ADAPTIVE_OPERATION;
4590  DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25));
4591  DFEGate.Phi = (int32_t)(0);
4592  DFEGate.Lambda = (int32_t)(0);
4593  DFEGate.metadata = 0;
4594 
4595  gate_idx = gate_idx + 1;
4596  }
4597  else {
4598  std::string err("Gates_block::convert_to_DFE_gates: unimplemented gate");
4599  throw err;
4600  }
4601 
4602  }
4603 
4604 
4605  return;
4606 
4607 }
4608 
4609 
4610 #endif
4611 
4612 
4613 #ifdef __GROQ__
4614 
4622 void
4623 Gates_block::extract_gate_kernels_target_and_control_qubits(std::vector<Matrix> &u3_qbit, std::vector<int> &target_qbit, std::vector<int> &control_qbit, Matrix_real& parameters_mtx)
4624 {
4625 
4626  if ( u3_qbit.size() == 0 ) {
4627  std::map<std::string, int> gate_nums = get_gate_nums();
4628  int gates_total_num = 0;
4629  for( auto it=gate_nums.begin(); it != gate_nums.end(); it++ ) {
4630  gates_total_num = gates_total_num + it->second;
4631  }
4632 
4633  u3_qbit.reserve( gates_total_num );
4634  target_qbit.reserve( gates_total_num );
4635  control_qbit.reserve( gates_total_num );
4636  }
4637 /*
4638  u3_qbit.reserve(u3_qbit.capacity() + gates.size());
4639  target_qbit.reserve(target_qbit.capacity() + gates.size());
4640  control_qbit.reserve(control_qbit.capacity() + gates.size());
4641 */
4642  double* parameters = parameters_mtx.get_data();
4643 
4644 
4645  for( int idx=0; idx<gates.size(); idx++) {
4646  Gate* operation = gates[idx];
4647  //parameters = parameters + operation->get_parameter_num();
4648 
4649  Matrix_real params_mtx(parameters + operation->get_parameter_start_idx(), 1, operation->get_parameter_num());
4650 
4651  switch (operation->get_type()) {
4652  case CNOT_OPERATION: case CZ_OPERATION:
4653  case CH_OPERATION: {
4654  Matrix_real empty_params(0, 0);
4655  u3_qbit.push_back(operation->gate_kernel(empty_params));
4656  break;
4657  }
4658  case H_OPERATION: {
4659  Matrix_real empty_params(0, 0);
4660  u3_qbit.push_back(operation->gate_kernel(empty_params));
4661  break;
4662  }
4663  case X_OPERATION: {
4664  Matrix_real empty_params(0, 0);
4665  u3_qbit.push_back(operation->gate_kernel(empty_params));
4666  break;
4667  }
4668  case Y_OPERATION: {
4669  Matrix_real empty_params(0, 0);
4670  u3_qbit.push_back(operation->gate_kernel(empty_params));
4671  break;
4672  }
4673  case Z_OPERATION: {
4674  Matrix_real empty_params(0, 0);
4675  u3_qbit.push_back(operation->gate_kernel(empty_params));
4676  break;
4677  }
4678  case T_OPERATION: {
4679  Matrix_real empty_params(0, 0);
4680  u3_qbit.push_back(operation->gate_kernel(empty_params));
4681  break;
4682  }
4683  case S_OPERATION: {
4684  Matrix_real empty_params(0, 0);
4685  u3_qbit.push_back(operation->gate_kernel(empty_params));
4686  break;
4687  }
4688  case SDG_OPERATION: {
4689  Matrix_real empty_params(0, 0);
4690  u3_qbit.push_back(operation->gate_kernel(empty_params));
4691  break;
4692  }
4693  case TDG_OPERATION: {
4694  Matrix_real empty_params(0, 0);
4695  u3_qbit.push_back(operation->gate_kernel(empty_params));
4696  break;
4697  }
4698  case SX_OPERATION: {
4699  Matrix_real empty_params(0, 0);
4700  u3_qbit.push_back(operation->gate_kernel(empty_params));
4701  break;
4702  }
4703  case SXDG_OPERATION: {
4704  Matrix_real empty_params(0, 0);
4705  u3_qbit.push_back(operation->gate_kernel(empty_params));
4706  break;
4707  }
4708  case U1_OPERATION: {
4709  u3_qbit.push_back(Gate::calc_one_qubit_u3(0.0, 0.0, params_mtx[0]));
4710  break;
4711  }
4712  case U2_OPERATION: {
4713  u3_qbit.push_back(Gate::calc_one_qubit_u3(M_PI / 4.0, params_mtx[0], params_mtx[1]));
4714  break;
4715  }
4716  case U3_OPERATION: {
4717  u3_qbit.push_back(Gate::calc_one_qubit_u3(params_mtx[0], params_mtx[1], params_mtx[2]));
4718  break;
4719  }
4720  case RX_OPERATION: {
4721  // set static values for the angles
4722  double ThetaOver2 = params_mtx[0];
4723  double Phi = -M_PI/2;
4724  double Lambda = M_PI/2;
4725  u3_qbit.push_back(Gate::calc_one_qubit_u3(ThetaOver2, Phi, Lambda));
4726  break;
4727  }
4728  case RY_OPERATION: {
4729  double ThetaOver2 = params_mtx[0];
4730  double Phi = 0.0;
4731  double Lambda = 0.0;
4732  u3_qbit.push_back(Gate::calc_one_qubit_u3(ThetaOver2, Phi, Lambda));
4733  break;
4734  }
4735  case CRY_OPERATION: {
4736  double ThetaOver2 = params_mtx[0];
4737  double Phi = 0.0;
4738  double Lambda = 0.0;
4739  u3_qbit.push_back(Gate::calc_one_qubit_u3(ThetaOver2, Phi, Lambda));
4740  break;
4741  }
4742  case RZ_OPERATION: {
4743  u3_qbit.push_back(Gate::calc_one_qubit_u3(0.0, 0.0, params_mtx[0]));
4744  break;
4745  }
4746  case BLOCK_OPERATION: {
4747 
4748  Gates_block* block_operation = static_cast<Gates_block*>(operation);
4749  //std::vector<Gate*> involved_gates = block_operation->get_gates();
4750 
4751  std::vector<int>&& involved_qubits = block_operation->get_involved_qubits();
4752 
4753  if ( involved_qubits.size() == 1 && block_operation->gates.size() > 1 && block_operation->get_qbit_num() > 1 ) {
4754  // possibly merge successive single qubit gates
4755 
4756  Gates_block gates_block_mini = Gates_block(1);
4757 
4758  for (int idx=0; idx<block_operation->gates.size(); idx++){
4759  Gate* gate = block_operation->gates[idx]->clone();
4760  gate->set_target_qbit(0);
4761  gate->set_qbit_num(1);
4762 
4763  gates_block_mini.add_gate(gate);
4764  }
4765 
4766  Matrix merged_kernel = create_identity(2);
4767 
4768  gates_block_mini.apply_to(params_mtx, merged_kernel);
4769 
4770  u3_qbit.push_back( merged_kernel );
4771  target_qbit.push_back( involved_qubits[0] );
4772  control_qbit.push_back( -1 );
4773 
4774  }
4775  else {
4776  block_operation->extract_gate_kernels_target_and_control_qubits(u3_qbit, target_qbit, control_qbit, params_mtx);
4777  }
4778  continue;
4779  }
4780  //case ADAPTIVE_OPERATION:
4781  //case SYC_OPERATION:
4782  //case GENERAL_OPERATION:
4783  default:
4784  std::string err("Optimization_Interface::apply_to: unimplemented gate (" + std::to_string(operation->get_type()) + ")");
4785  throw err;
4786  }
4787 
4788  target_qbit.push_back(operation->get_target_qbit());
4789  control_qbit.push_back(operation->get_control_qbit());
4790  }
4791 
4792 
4793 
4794 }
4795 #endif
4796 
4797 
4798 
4806 void
4807 export_gate_list_to_binary(Matrix_real& parameters, Gates_block* gates_block, const std::string& filename, int verbosity) {
4808 
4809  std::stringstream sstream;
4810  sstream << "Exporting circuit into binary format. Filename: " << filename << std::endl;
4811  logging log;
4812  log.verbose = verbosity;
4813  log.print(sstream, 3);
4814 
4815  FILE* pFile;
4816  const char* c_filename = filename.c_str();
4817 
4818 #ifdef _WIN32
4819  errno_t err = fopen_s(&pFile, c_filename, "wb");
4820  if (err != 0) {
4821  pFile = NULL;
4822  }
4823 #else
4824  pFile = fopen(c_filename, "wb");
4825 #endif
4826  if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
4827 
4828  export_gate_list_to_binary( parameters, gates_block, pFile, verbosity );
4829 
4830  fclose(pFile);
4831  return;
4832 
4833 }
4834 
4835 
4836 
4844 void
4845 export_gate_list_to_binary(Matrix_real& parameters, Gates_block* gates_block, FILE* pFile, int verbosity) {
4846 
4847  int qbit_num = gates_block->get_qbit_num();
4848  fwrite(&qbit_num, sizeof(int), 1, pFile);
4849 
4850  int parameter_num = gates_block->get_parameter_num();
4851  fwrite(&parameter_num, sizeof(int), 1, pFile);
4852 
4853 
4854  int gates_num = gates_block->get_gate_num();
4855  fwrite(&gates_num, sizeof(int), 1, pFile);
4856 
4857 
4858 
4859  std::vector<Gate*> gates = gates_block->get_gates();
4860  double* parameters_data = parameters.get_data();
4861 
4862  for ( std::vector<Gate*>::iterator it=gates.begin(); it != gates.end(); ++it ) {
4863  Gate* op = *it;
4864 
4865  gate_type gt_type = op->get_type();
4866 
4867  fwrite(&gt_type, sizeof(gate_type), 1, pFile);
4868 
4869  int parameter_num = op->get_parameter_num();
4870 
4871  if (gt_type == CNOT_OPERATION || gt_type == CZ_OPERATION || gt_type == CH_OPERATION || gt_type == SYC_OPERATION) {
4872  int target_qbit = op->get_target_qbit();
4873  int control_qbit = op->get_control_qbit();
4874  fwrite(&target_qbit, sizeof(int), 1, pFile);
4875  fwrite(&control_qbit, sizeof(int), 1, pFile);
4876  }
4877  else if (gt_type == U1_OPERATION || gt_type == U2_OPERATION || gt_type == U3_OPERATION ) {
4878  int target_qbit = op->get_target_qbit();
4879  fwrite(&target_qbit, sizeof(int), 1, pFile);
4880  fwrite(parameters_data, sizeof(double), parameter_num, pFile);
4881  }
4882  else if (gt_type == RX_OPERATION || gt_type == RY_OPERATION || gt_type == RZ_OPERATION ) {
4883  int target_qbit = op->get_target_qbit();
4884  fwrite(&target_qbit, sizeof(int), 1, pFile);
4885 
4886  fwrite(parameters_data, sizeof(double), parameter_num, pFile);
4887  }
4888  else if (gt_type == CRY_OPERATION) {
4889  int target_qbit = op->get_target_qbit();
4890  int control_qbit = op->get_control_qbit();
4891  fwrite(&target_qbit, sizeof(int), 1, pFile);
4892  fwrite(&control_qbit, sizeof(int), 1, pFile);
4893 
4894  fwrite(parameters_data, sizeof(double), parameter_num, pFile);
4895  }
4896 
4897  else if (gt_type == X_OPERATION || gt_type == Y_OPERATION || gt_type == Z_OPERATION || gt_type == S_OPERATION || gt_type == SDG_OPERATION || gt_type == SX_OPERATION || gt_type == H_OPERATION) {
4898  int target_qbit = op->get_target_qbit();
4899  fwrite(&target_qbit, sizeof(int), 1, pFile);
4900  }
4901  else if (gt_type == BLOCK_OPERATION) {
4902  Gates_block* block_op = static_cast<Gates_block*>( op );
4903  Matrix_real parameters_loc(parameters_data, 1, parameter_num);
4904  export_gate_list_to_binary( parameters_loc, block_op, pFile );
4905 
4906  }
4907  else if (gt_type == ADAPTIVE_OPERATION) {
4908  int target_qbit = op->get_target_qbit();
4909  int control_qbit = op->get_control_qbit();
4910  fwrite(&target_qbit, sizeof(int), 1, pFile);
4911  fwrite(&control_qbit, sizeof(int), 1, pFile);
4912 
4913  fwrite(parameters_data, sizeof(double), parameter_num, pFile);
4914  }
4915  else {
4916  std::string err("export_gate_list_to_binary: unimplemented gate");
4917  throw err;
4918  }
4919 
4920 
4921  parameters_data = parameters_data + parameter_num;
4922 
4923  }
4924 
4925 }
4926 
4927 
4935 
4936  std::stringstream sstream;
4937  sstream << "Importing quantum circuit from binary file " << filename << std::endl;
4938  logging log;
4939  log.verbose = verbosity;
4940  log.print(sstream, 2);
4941 
4942  FILE* pFile;
4943  const char* c_filename = filename.c_str();
4944 
4945 #ifdef _WIN32
4946  errno_t err = fopen_s(&pFile, c_filename, "rb");
4947  if (err != 0) {
4948  pFile = NULL;
4949  }
4950 #else
4951  pFile = fopen(c_filename, "rb");
4952 #endif
4953  if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
4954 
4955  Gates_block* ret = import_gate_list_from_binary(parameters, pFile, verbosity);
4956 
4957  fclose(pFile);
4958  return ret;
4959 }
4960 
4961 
4962 
4963 
4971 
4972  std::stringstream sstream;
4973 
4974  int qbit_num;
4975 
4976  fread_wrapper(&qbit_num, sizeof(int), 1, pFile);
4977  sstream << "qbit_num: " << qbit_num << std::endl;
4978  Gates_block* gate_block = new Gates_block(qbit_num);
4979 
4980  int parameter_num;
4981  fread_wrapper(&parameter_num, sizeof(int), 1, pFile);
4982  sstream << "parameter_num: " << parameter_num << std::endl;
4983  parameters = Matrix_real(1, parameter_num);
4984  double* parameters_data = parameters.get_data();
4985 
4986  int gates_num;
4987  fread_wrapper(&gates_num, sizeof(int), 1, pFile);
4988  sstream << "gates_num: " << gates_num << std::endl;
4989 
4990  std::vector<int> gate_block_level_gates_num;
4991  std::vector<Gates_block*> gate_block_levels;
4992  gate_block_level_gates_num.push_back( gates_num );
4993  gate_block_levels.push_back(gate_block);
4994  int current_level = 0;
4995 
4996 
4997 
4998  int iter_max = 100000;
4999  int iter = 0;
5000  while ( gate_block_level_gates_num[0] > 0 && iter < iter_max) {
5001 
5002  gate_type gt_type;
5003  fread_wrapper(&gt_type, sizeof(gate_type), 1, pFile);
5004 
5005  //std::cout << "gate type: " << gt_type << std::endl;
5006 
5007  if (gt_type == CNOT_OPERATION) {
5008  sstream << "importing CNOT gate" << std::endl;
5009 
5010  int target_qbit;
5011  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5012  sstream << "target_qbit: " << target_qbit << std::endl;
5013 
5014  int control_qbit;
5015  fread_wrapper(&control_qbit, sizeof(int), 1, pFile);
5016  sstream << "control_qbit: " << control_qbit << std::endl;
5017 
5018  gate_block_levels[current_level]->add_cnot(target_qbit, control_qbit);
5019  gate_block_level_gates_num[current_level]--;
5020  }
5021  else if (gt_type == CZ_OPERATION) {
5022  sstream << "importing CZ gate" << std::endl;
5023 
5024  int target_qbit;
5025  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5026  sstream << "target_qbit: " << target_qbit << std::endl;
5027 
5028  int control_qbit;
5029  fread_wrapper(&control_qbit, sizeof(int), 1, pFile);
5030  sstream << "control_qbit: " << control_qbit << std::endl;
5031 
5032  gate_block_levels[current_level]->add_cz(target_qbit, control_qbit);
5033  gate_block_level_gates_num[current_level]--;
5034  }
5035  else if (gt_type == CH_OPERATION) {
5036  sstream << "importing CH gate" << std::endl;
5037 
5038  int target_qbit;
5039  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5040  sstream << "target_qbit: " << target_qbit << std::endl;
5041 
5042  int control_qbit;
5043  fread_wrapper(&control_qbit, sizeof(int), 1, pFile);
5044  sstream << "control_qbit: " << control_qbit << std::endl;
5045 
5046  gate_block_levels[current_level]->add_ch(target_qbit, control_qbit);
5047  gate_block_level_gates_num[current_level]--;
5048  }
5049  else if (gt_type == SYC_OPERATION) {
5050  sstream << "importing SYCAMORE gate" << std::endl;
5051 
5052  int target_qbit;
5053  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5054  sstream << "target_qbit: " << target_qbit << std::endl;
5055 
5056  int control_qbit;
5057  fread_wrapper(&control_qbit, sizeof(int), 1, pFile);
5058  sstream << "control_qbit: " << control_qbit << std::endl;
5059 
5060  gate_block_levels[current_level]->add_syc(target_qbit, control_qbit);
5061  gate_block_level_gates_num[current_level]--;
5062  }
5063  else if (gt_type == U1_OPERATION) {
5064  sstream << "importing U1 gate" << std::endl;
5065 
5066  int target_qbit;
5067  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5068  sstream << "target_qbit: " << target_qbit << std::endl;
5069 
5070  fread_wrapper(parameters_data, sizeof(double), 1, pFile);
5071 
5072  parameters_data++;
5073  gate_block_levels[current_level]->add_u1(target_qbit);
5074  gate_block_level_gates_num[current_level]--;
5075  }
5076  else if (gt_type == U2_OPERATION) {
5077  sstream << "importing U2 gate" << std::endl;
5078 
5079  int target_qbit;
5080  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5081  sstream << "target_qbit: " << target_qbit << std::endl;
5082 
5083  fread_wrapper(parameters_data, sizeof(double), 2, pFile);
5084 
5085  parameters_data+=2;
5086  gate_block_levels[current_level]->add_u2(target_qbit);
5087  gate_block_level_gates_num[current_level]--;
5088  }
5089  else if (gt_type == U3_OPERATION) {
5090  sstream << "importing U3 gate" << std::endl;
5091 
5092  int target_qbit;
5093  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5094  sstream << "target_qbit: " << target_qbit << std::endl;
5095 
5096  fread_wrapper(parameters_data, sizeof(double), 3, pFile);
5097 
5098  parameters_data += 3;
5099  gate_block_levels[current_level]->add_u3(target_qbit);
5100  gate_block_level_gates_num[current_level]--;
5101  }
5102  else if (gt_type == RX_OPERATION) {
5103 
5104  sstream << "importing RX gate" << std::endl;
5105 
5106  int target_qbit;
5107  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5108  sstream << "target_qbit: " << target_qbit << std::endl;
5109 
5110  fread_wrapper(parameters_data, sizeof(double), 1, pFile);
5111  parameters_data++;
5112 
5113  gate_block_levels[current_level]->add_rx(target_qbit);
5114  gate_block_level_gates_num[current_level]--;
5115 
5116  }
5117  else if (gt_type == RY_OPERATION) {
5118 
5119  sstream << "importing RY gate" << std::endl;
5120 
5121  int target_qbit;
5122  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5123  sstream << "target_qbit: " << target_qbit << std::endl;
5124 
5125  fread_wrapper(parameters_data, sizeof(double), 1, pFile);
5126  parameters_data++;
5127 
5128  gate_block_levels[current_level]->add_ry(target_qbit);
5129  gate_block_level_gates_num[current_level]--;
5130 
5131  }
5132  else if (gt_type == CRY_OPERATION) {
5133 
5134  sstream << "importing CRY gate" << std::endl;
5135 
5136  int target_qbit;
5137  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5138  sstream << "target_qbit: " << target_qbit << std::endl;
5139 
5140  int control_qbit;
5141  fread_wrapper(&control_qbit, sizeof(int), 1, pFile);
5142  sstream << "control_qbit: " << control_qbit << std::endl;
5143 
5144  fread_wrapper(parameters_data, sizeof(double), 1, pFile);
5145  parameters_data++;
5146 
5147  gate_block_levels[current_level]->add_cry(target_qbit, control_qbit);
5148  gate_block_level_gates_num[current_level]--;
5149 
5150  }
5151  else if (gt_type == RZ_OPERATION) {
5152 
5153  sstream << "importing RZ gate" << std::endl;
5154 
5155  int target_qbit;
5156  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5157  sstream << "target_qbit: " << target_qbit << std::endl;
5158 
5159  fread_wrapper(parameters_data, sizeof(double), 1, pFile);
5160  parameters_data++;
5161 
5162  gate_block_levels[current_level]->add_rz(target_qbit);
5163  gate_block_level_gates_num[current_level]--;
5164 
5165  }
5166  else if (gt_type == H_OPERATION) {
5167 
5168  sstream << "importing Hadamard gate" << std::endl;
5169 
5170  int target_qbit;
5171  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5172  sstream << "target_qbit: " << target_qbit << std::endl;
5173 
5174  gate_block_levels[current_level]->add_h(target_qbit);
5175  gate_block_level_gates_num[current_level]--;
5176 
5177  }
5178  else if (gt_type == X_OPERATION) {
5179 
5180  sstream << "importing X gate" << std::endl;
5181 
5182  int target_qbit;
5183  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5184  sstream << "target_qbit: " << target_qbit << std::endl;
5185 
5186  gate_block_levels[current_level]->add_x(target_qbit);
5187  gate_block_level_gates_num[current_level]--;
5188 
5189  }
5190  else if (gt_type == Y_OPERATION) {
5191 
5192  sstream << "importing Y gate" << std::endl;
5193 
5194  int target_qbit;
5195  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5196  sstream << "target_qbit: " << target_qbit << std::endl;
5197 
5198  gate_block_levels[current_level]->add_y(target_qbit);
5199  gate_block_level_gates_num[current_level]--;
5200 
5201  }
5202  else if (gt_type == Z_OPERATION) {
5203 
5204  sstream << "importing Z gate" << std::endl;
5205 
5206  int target_qbit;
5207  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5208  sstream << "target_qbit: " << target_qbit << std::endl;
5209 
5210  gate_block_levels[current_level]->add_z(target_qbit);
5211  gate_block_level_gates_num[current_level]--;
5212 
5213  }
5214  else if (gt_type == S_OPERATION) {
5215 
5216  sstream << "importing S gate" << std::endl;
5217 
5218  int target_qbit;
5219  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5220  sstream << "target_qbit: " << target_qbit << std::endl;
5221 
5222  gate_block_levels[current_level]->add_s(target_qbit);
5223  gate_block_level_gates_num[current_level]--;
5224 
5225  }
5226  else if (gt_type == SDG_OPERATION) {
5227 
5228  sstream << "importing Sdg gate" << std::endl;
5229 
5230  int target_qbit;
5231  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5232  sstream << "target_qbit: " << target_qbit << std::endl;
5233 
5234  gate_block_levels[current_level]->add_sdg(target_qbit);
5235  gate_block_level_gates_num[current_level]--;
5236 
5237  }
5238  else if (gt_type == T_OPERATION) {
5239 
5240  sstream << "importing T gate" << std::endl;
5241 
5242  int target_qbit;
5243  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5244  sstream << "target_qbit: " << target_qbit << std::endl;
5245 
5246  gate_block_levels[current_level]->add_t(target_qbit);
5247  gate_block_level_gates_num[current_level]--;
5248 
5249  }
5250  else if (gt_type == TDG_OPERATION) {
5251 
5252  sstream << "importing Tdg gate" << std::endl;
5253 
5254  int target_qbit;
5255  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5256  sstream << "target_qbit: " << target_qbit << std::endl;
5257 
5258  gate_block_levels[current_level]->add_t(target_qbit);
5259  gate_block_level_gates_num[current_level]--;
5260 
5261  }
5262  else if (gt_type == SX_OPERATION) {
5263 
5264  sstream << "importing SX gate" << std::endl;
5265 
5266  int target_qbit;
5267  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5268  sstream << "target_qbit: " << target_qbit << std::endl;
5269 
5270  gate_block_levels[current_level]->add_sx(target_qbit);
5271  gate_block_level_gates_num[current_level]--;
5272 
5273  }
5274  else if (gt_type == BLOCK_OPERATION) {
5275 
5276 
5277  sstream << "******* importing gates block ********" << std::endl;
5278 
5279  int qbit_num_loc;
5280  fread_wrapper(&qbit_num_loc, sizeof(int), 1, pFile);
5281  //std::cout << "qbit_num_loc: " << qbit_num_loc << std::endl;
5282  Gates_block* gate_block_inner = new Gates_block(qbit_num_loc);
5283 
5284  int parameter_num_loc;
5285  fread_wrapper(&parameter_num_loc, sizeof(int), 1, pFile);
5286  //std::cout << "parameter_num_loc: " << parameter_num_loc << std::endl;
5287 
5288 
5289  int gates_num_loc;
5290  fread_wrapper(&gates_num_loc, sizeof(int), 1, pFile);
5291  //std::cout << "gates_num_loc: " << gates_num_loc << std::endl;
5292 
5293  gate_block_levels.push_back( gate_block_inner );
5294  gate_block_level_gates_num.push_back(gates_num_loc);
5295  current_level++;
5296  }
5297  else if (gt_type == ADAPTIVE_OPERATION) {
5298 
5299  sstream << "importing adaptive gate" << std::endl;
5300 
5301  int target_qbit;
5302  fread_wrapper(&target_qbit, sizeof(int), 1, pFile);
5303  sstream << "target_qbit: " << target_qbit << std::endl;
5304 
5305  int control_qbit;
5306  fread_wrapper(&control_qbit, sizeof(int), 1, pFile);
5307  sstream << "control_qbit: " << control_qbit << std::endl;
5308 
5309  fread_wrapper(parameters_data, sizeof(double), 1, pFile);
5310  parameters_data++;
5311 
5312  gate_block_levels[current_level]->add_adaptive(target_qbit, control_qbit);
5313  gate_block_level_gates_num[current_level]--;
5314 
5315  }
5316  else {
5317  std::string err("import_gate_list_from_binary: unimplemented gate");
5318  throw err;
5319  }
5320 
5321 
5322  if ( gate_block_level_gates_num[current_level] == 0 ) {
5323  gate_block_levels[ current_level-1 ]->add_gate( static_cast<Gate*>(gate_block_levels[ current_level ]) );
5324  gate_block_levels.pop_back();
5325  gate_block_level_gates_num.pop_back();
5326  current_level--;
5327  gate_block_level_gates_num[current_level]--;
5328  sstream << "finishing gates block" << std::endl;
5329  }
5330 
5331 
5332  iter++;
5333  }
5334 
5335  logging log;
5336  log.verbose = verbosity;
5337  log.print(sstream, 4);
5338 
5339 
5340  if ( iter == iter_max ) {
5341  std::string error("Corrupted input file, reached end of the file before contructing the whole gate structure");
5342  throw error;
5343  }
5344 
5345  return gate_block;
5346 
5347 }
5348 
5349 
5350 
5357 Matrix_real reverse_parameters( const Matrix_real& parameters_in, std::vector<Gate*>::iterator gates_it, int num_of_gates ) {
5358 
5359  if ( parameters_in.cols > 1 && parameters_in.rows > 1 ) {
5360  std::string error("reverse_parameters: Input array should have a single column or a single row.");
5361  throw error;
5362  }
5363 
5364 //return parameters_in.copy();
5365 
5366  // determine the number of parameters
5367  int parameters_num_total = 0;
5368  for (int idx=0; idx<num_of_gates; idx++) {
5369 
5370  // The current gate
5371  Gate* gate = *(gates_it++);
5372  parameters_num_total = parameters_num_total + gate->get_parameter_num();
5373 
5374  }
5375 
5376 
5377  if ( parameters_num_total == 0) {
5378  return Matrix_real(0,0);
5379  }
5380 
5381  //std::cout << "uu" << std::endl;
5382  //parameters_in.print_matrix();
5383 
5384 
5385  Matrix_real parameters_ret(1, parameters_num_total);
5386  int parameter_num_copied = 0;
5387 
5388  // apply the gate operations on the inital matrix
5389  for (int idx=num_of_gates-0; idx>0; idx--) {
5390 
5391  // The current gate
5392  Gate* gate = *(--gates_it);
5393 
5394  int parameter_num_gate = gate->get_parameter_num();
5395 
5396  if ( parameter_num_gate == 0 ) {
5397  continue;
5398  }
5399 
5400  else if (gate->get_type() == BLOCK_OPERATION ) {
5401 
5402  //std::cout << "block: " << parameter_num_gate << " " << parameters_num_total << std::endl;
5403  parameters_num_total = parameters_num_total - gate->get_parameter_num();
5404 
5405  Matrix_real parameters_of_block( parameters_in.get_data()+parameters_num_total, 1, parameter_num_gate );
5406  //parameters_of_block.print_matrix();
5407 
5408  Gates_block* block_gate = static_cast<Gates_block*>( gate );
5409 
5410  std::vector<Gate*> gates_loc = block_gate->get_gates();
5411 
5412  Matrix_real parameters_of_block_reversed = reverse_parameters( parameters_of_block, gates_loc.begin(), static_cast<int>(gates_loc.size()) );
5413 
5414  //parameters_of_block_reversed.print_matrix();
5415 
5416  memcpy( parameters_ret.get_data()+parameter_num_copied, parameters_of_block_reversed.get_data(), parameters_of_block_reversed.size()*sizeof(double) );
5417  parameter_num_copied = parameter_num_copied + parameters_of_block_reversed.size();
5418 
5419  //parameters_ret.print_matrix();
5420 
5421  }
5422 
5423  else {
5424 
5425  //std::cout << parameter_num_gate << std::endl;
5426 
5427  parameters_num_total = parameters_num_total - gate->get_parameter_num();
5428  memcpy( parameters_ret.get_data()+parameter_num_copied, parameters_in.get_data()+parameters_num_total, gate->get_parameter_num()*sizeof(double) );
5429  parameter_num_copied = parameter_num_copied + gate->get_parameter_num();
5430 
5431  }
5432 
5433 
5434  }
5435 
5436 
5437 
5438 
5439  return parameters_ret;
5440 
5441 
5442 
5443 
5444 }
5445 
5446 
5447 
5448 
5455 Matrix_real inverse_reverse_parameters( const Matrix_real& parameters_in, std::vector<Gate*>::iterator gates_it, int num_of_gates ) {
5456 
5457  if ( parameters_in.cols > 1 && parameters_in.rows > 1 ) {
5458  std::string error("reverse_parameters: Input array should have a single column or a single row.");
5459  throw error;
5460  }
5461 
5462 
5463  // determine the number of parameters
5464  int parameters_num_total = 0;
5465  for (int idx=0; idx<num_of_gates; idx++) {
5466 
5467  // The current gate
5468  Gate* gate = *(gates_it++);
5469  parameters_num_total = parameters_num_total + gate->get_parameter_num();
5470 
5471  }
5472 
5473 
5474  if ( parameters_num_total == 0) {
5475  return Matrix_real(0,0);
5476  }
5477 
5478  //std::cout << "uu" << std::endl;
5479  //parameters_in.print_matrix();
5480 
5481 
5482  Matrix_real parameters_ret(1, parameters_num_total);
5483  int parameter_num_copied = 0;
5484 
5485  // apply the gate operations on the inital matrix
5486  for (int idx=num_of_gates-0; idx>0; idx--) {
5487 
5488  // The current gate
5489  Gate* gate = *(--gates_it);
5490 
5491  int parameter_num_gate = gate->get_parameter_num();
5492 
5493  if ( parameter_num_gate == 0 ) {
5494  continue;
5495  }
5496 
5497  else if (gate->get_type() == BLOCK_OPERATION ) {
5498 
5499  //std::cout << "block: " << parameter_num_gate << " " << parameters_num_total << std::endl;
5500  parameters_num_total = parameters_num_total - gate->get_parameter_num();
5501 
5502  Matrix_real parameters_of_block( parameters_in.get_data()+parameters_num_total, 1, parameter_num_gate );
5503  //parameters_of_block.print_matrix();
5504 
5505  Gates_block* block_gate = static_cast<Gates_block*>( gate );
5506 
5507  std::vector<Gate*> gates_loc = block_gate->get_gates();
5508 
5509  Matrix_real parameters_of_block_reversed = reverse_parameters( parameters_of_block, gates_loc.begin(), static_cast<int>(gates_loc.size()) );
5510 
5511  //parameters_of_block_reversed.print_matrix();
5512 
5513  memcpy( parameters_ret.get_data()+parameter_num_copied, parameters_of_block_reversed.get_data(), parameters_of_block_reversed.size()*sizeof(double) );
5514  parameter_num_copied = parameter_num_copied + parameters_of_block_reversed.size();
5515 
5516  //parameters_ret.print_matrix();
5517 
5518  }
5519 
5520  else {
5521 
5522  //std::cout << parameter_num_gate << std::endl;
5523 
5524  parameters_num_total = parameters_num_total - gate->get_parameter_num();
5525  memcpy( parameters_ret.get_data()+parameters_num_total, parameters_in.get_data()+parameter_num_copied, gate->get_parameter_num()*sizeof(double) );
5526  parameter_num_copied = parameter_num_copied + gate->get_parameter_num();
5527 
5528  }
5529 
5530 
5531  }
5532 
5533 
5534 
5535 
5536  return parameters_ret;
5537 
5538 
5539 
5540 
5541 }
void add_crz(int target_qbit, int control_qbit)
Append a CRY gate to the list of gates.
bool is_float32() const
Definition: matrix_any.h:41
std::vector< Gate * > parents
list of parent gates to be applied in the circuit prior to this current gate
Definition: Gate.h:112
Gates_block * get_flat_circuit()
Method to generate a flat circuit.
Gate()
Default constructor of the class.
Definition: Gate.cpp:121
Copyright (C) Miklos Maroti, 2021 SPDX-License-Identifier: Apache-2.0.
Definition: U3.h:19
void add_swap_to_front(const std::vector< int > &target_qbits)
Add a SWAP gate to the front of the list of gates.
virtual void reorder_qubits(std::vector< int > qbit_list) override
Call to reorder the qubits in the matrix of the gates (Obsolete function)
void add_adaptive(int target_qbit, int control_qbit)
Append a Adaptive gate to the list of gates.
void add_cu(int target_qbit, int control_qbit)
Append a CU gate (i.e.
Definition: X.h:11
void add_rxx_to_front(std::vector< int > target_qbits)
Add a RXX gate to the front of the list of gates.
Header file for a class representing a CSWAP (Controlled SWAP) operation.
int get_chained_gates_num()
Call to retrieve the number of gates that should be chained up during the execution of the DFE librar...
Definition: common_DFE.cpp:246
A class representing a RXX gate.
Definition: RXX.h:34
Header file for a class representing a CP gate.
void add_rxx(std::vector< int > target_qbits)
Append a RXX gate to the list of gates.
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 set_control_qbits(const std::vector< int > &control_qbits_in)
Call to set the control qubits for the gate operation.
Definition: Gate.cpp:1097
Class to store single-precision real arrays and properties.
A class representing a CCX (Toffoli) operation.
Definition: CCX.h:35
void release_gate(int idx)
Call to release one gate in the list.
void determine_parents(Gate *gate)
Call to obtain the parent gates in the circuit.
void add_cswap(const std::vector< int > &target_qbits, const std::vector< int > &control_qbits)
Append a CSWAP gate (Controlled SWAP) to the list of gates.
void add_z_to_front(int target_qbit)
Add a Z gate to the front of the list of gates.
void add_child(Gate *child)
Call to add a child gate to the current gate.
Definition: Gate.cpp:1264
void add_h(int target_qbit)
Append a Hadamard gate to the list of gates.
void add_sdg_to_front(int target_qbit)
Add a Sdg gate to the front of the list of gates.
Definition: U2.h:11
virtual Matrix get_matrix()
Retrieve the gate matrix for zero-parameter gates (no parameters).
Definition: Gate.cpp:283
Header file for a class representing a controlled Z rotation gate.
void clear_children()
Call to erase data on children.
Definition: Gate.cpp:1279
Definition: S.h:11
std::map< std::string, int > get_gate_nums()
Call to get the number of the individual gate types in the list of gates.
A class representing a controlled RX gate.
Definition: CRX.h:35
A class representing a CP gate.
Definition: CP.h:35
std::shared_ptr< tbb::spin_mutex > involved_qubits_cache_mutex
Definition: Gates_block.h:61
std::vector< int > target_qbits
Vector of target qubit indices (for multi-qubit gates)
Definition: Gate.h:102
void add_x(int target_qbit)
Append a X gate to the list of gates.
void add_sdg(int target_qbit)
Append a Sdg gate to the list of gates.
int control_qbit
The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations...
Definition: Gate.h:100
virtual std::vector< Matrix > apply_derivate_to(Matrix_real &parameters_mtx_in, Matrix &input, int parallel)
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...
Definition: Gate.cpp:784
virtual Gate * clone()
Call to create a clone of the present class.
Definition: Gate.cpp:1351
void add_gate(Gate *gate)
Append a general gate to the list of gates.
int stride
The column stride of the array. (The array elements in one row are a_0, a_1, ... a_{cols-1}, 0, 0, 0, 0. The number of zeros is stride-cols)
Definition: matrix_base.hpp:46
void set_children(std::vector< Gate *> &children_)
Call to set the children of the current gate.
Definition: Gate.cpp:2523
Header file for a class representing a controlled rotation gate around the Y axis.
void add_u2(int target_qbit)
Append a U2 gate to the list of gates.
void add_cp(int target_qbit, int control_qbit)
Append a CRY gate to the list of gates.
virtual void set_qbit_num(int qbit_num_in)
Set the number of qubits spanning the matrix of the operation.
Definition: Gate.cpp:244
void add_cnot_to_front(int target_qbit, int control_qbit)
Add a C_NOT gate gate to the front of the list of gates.
void add_u1(int target_qbit)
Append a U1 gate to the list of gates.
Structure type representing single-precision complex numbers.
Definition: QGDTypes.h:46
Header file for a class representing a controlled rotation gate around the Y axis.
int target_qbit
The index of the qubit on which the operation acts (target_qbit >= 0)
Definition: Gate.h:98
Matrix_real reverse_parameters(const Matrix_real &parameters_in, std::vector< Gate *>::iterator gates_it, int num_of_gates)
Call to reverse the order of the parameters in an array.
std::vector< Gate * > get_gates()
Call to get the gates stored in the class.
bool is_float32() const
Gates_block * create_remapped_circuit(const std::map< int, int > &qbit_map)
Call to create a new circuit with remapped qubits.
void release_gates()
Call to release the stored gates.
void add_cswap_to_front(const std::vector< int > &target_qbits, const std::vector< int > &control_qbits)
Add a CSWAP gate (Controlled SWAP) to the front of the list of gates.
Matrix & as_float64()
Definition: matrix_any.h:48
void add_general_operation(const Matrix &operation_mtx, const std::vector< int > &target_qbits=std::vector< int >(), const std::vector< int > &control_qbits=std::vector< int >())
Append a GENERAL_OPERATION gate with an explicitly provided matrix.
Header file for a class representing a CH operation.
Header file for a class representing a controlled X rotation gate.
Header file for a class representing a SWAP operation.
void add_r(int target_qbit)
Append a R gate to the list of gates.
Header file for a class responsible for grouping gates into subcircuits. (Subcircuits can be nested) ...
Definition: Tdg.h:11
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 add_cnot(int target_qbit, int control_qbit)
Append a CNOT gate gate to the list of gates.
A class representing a SWAP operation.
Definition: SWAP.h:35
void add_cr(int target_qbit, int control_qbit)
Append a CR gate to the list of gates.
bool is_float64() const
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
Definition: common.cpp:298
Definition: U1.h:11
virtual void set_qbit_num(int qbit_num_in) override
Set the number of qubits spanning the matrix of the gates stored in the block of gates.
void add_cz_to_front(int target_qbit, int control_qbit)
Add a CZ gate gate to the front of the list of gates.
void apply_to_list(Matrix_real &parameters, std::vector< Matrix > &inputs, int parallel) override
Call to apply the gate on the input array/matrix by U3*input.
void add_ryy(std::vector< int > target_qbits)
Append a RYY gate to the list of gates.
bool involved_qubits_cache_valid
Definition: Gates_block.h:57
bool is_float64() const
Definition: matrix_any.h:40
int matrix_size
The size N of the NxN matrix associated with the operations.
Definition: Gate.h:106
Header file for a class representing a CCX (Toffoli) operation.
scalar * get_data() const
Call to get the pointer to the stored data.
static tbb::enumerable_thread_specific< int > gates_block_fusion_depth([](){ return 0;})
void add_rz_to_front(int target_qbit)
Add a RZ gate to the front of the list of gates.
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
Non-owning carrier that references either Matrix (complex128) or Matrix_float (complex64) without dat...
Definition: matrix_any.h:30
void add_r_to_front(int target_qbit)
Add a R gate to the front of the list of gates.
std::shared_ptr< T > get() const
Definition: utils.hpp:51
Class representing a RZZ gate.
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.
int get_gate_num()
Call to get the number of gates grouped in the class.
void add_crx_to_front(int target_qbit, int control_qbit)
Add a CRY gate to the front of the list of gates.
void add_h_to_front(int target_qbit)
Add a Hadamard gate to the front of the list of gates.
void add_ccx_to_front(int target_qbit, const std::vector< int > &control_qbits)
Add a CCX gate (i.e.
A class representing a RYY gate.
Definition: RYY.h:34
Non-owning carrier that can reference either Matrix_real or Matrix_real_float.
std::vector< Gate * > gates
The list of stored gates.
Definition: Gates_block.h:49
A class representing a CROT gate.
Definition: CROT.h:38
void add_x_to_front(int target_qbit)
Add a X gate to the front of the list of gates.
virtual void apply_from_right_inner(Matrix_real &parameter_mtx, const Matrix_real &precomputed_sincos, Matrix &input)
Internal right-apply entry that consumes already precomputed sin/cos values.
Definition: Gate.cpp:977
void add_sx_to_front(int target_qbit)
Add a SX gate to the front of the list of gates.
std::vector< int > involved_target_qubits_cache
Definition: Gates_block.h:60
void add_sxdg(int target_qbit)
Append a SXdg gate to the list of gates.
A class representing a CZ operation.
Definition: CZ.h:36
virtual void apply_to_inner(Matrix_real &parameter_mtx, const Matrix_real &precomputed_sincos, Matrix &input, int parallel)
Internal apply entry that consumes already precomputed sin/cos values.
Definition: Gate.cpp:549
~Gates_block() override
Destructor of the class.
Header file for a class representing a CNOT operation.
A class containing basic methods for setting up the verbosity level.
Definition: logging.h:43
void add_y_to_front(int target_qbit)
Add a Y gate to the front of the list of gates.
Definition: Z.h:11
void add_syc(int target_qbit, int control_qbit)
Append a Sycamore gate (i.e.
Definition: RZ.h:11
gate_type type
The type of the operation (see enumeration gate_type)
Definition: Gate.h:96
double get_second_Renyi_entropy(Matrix_real &parameters_mtx, Matrix &input_state, matrix_base< int > &qbit_list)
Call to evaluate the seconf Rényi entropy.
void add_syc_to_front(int target_qbit, int control_qbit)
Add a Sycamore gate (i.e.
void add_ch(int target_qbit, int control_qbit)
Append a CH gate (i.e.
void add_s_to_front(int target_qbit)
Add a S gate to the front of the list of gates.
A class representing a CRY gate.
Definition: CRY.h:37
virtual void apply_from_right(Matrix &input)
Call to apply the gate on the input array/matrix by input*Gate.
Definition: Gate.cpp:919
void update(std::shared_ptr< T > p)
Definition: utils.hpp:42
std::vector< int > get_control_qbits() const
Call to get the vector of control qubits.
Definition: Gate.cpp:1135
int rows
The number of rows.
Definition: matrix_base.hpp:42
A class representing a CH operation.
Definition: CH.h:36
int cols
The number of columns.
Definition: matrix_base.hpp:44
void add_ch_to_front(int target_qbit, int control_qbit)
Add a CH gate (i.e.
Definition: CU.h:11
#define M_PI
Definition: qgd_math.h:42
void add_u2_to_front(int target_qbit)
Add a U2 gate to the front of the list of gates.
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.
void add_rz(int target_qbit)
Append a RZ gate to the list of gates.
Matrix_float & as_float32()
Definition: matrix_any.h:55
Matrix_real & as_float64()
void add_y(int target_qbit)
Append a Y gate to the list of gates.
void reset_dependency_graph()
Method to reset the dependency graph of the gates in the circuit.
void combine(Gates_block *op_block)
Call to append the gates of an gate block to the current block.
void add_ry(int target_qbit)
Append a RY gate to the list of gates.
virtual void apply_from_right(Matrix_real &parameters_mtx, Matrix &input) override
Call to apply the gate on the input array/matrix by input*CNOT.
void set_control_qbit(int control_qbit_in)
Call to set the control qubit for the gate operation.
Definition: Gate.cpp:1054
void set_parameter_start_idx(int start_idx)
Call to set the starting index of the parameters in the parameter array corresponding to the circuit ...
Definition: Gate.cpp:2500
bool involved_target_qubits_cache_valid
Definition: Gates_block.h:58
Header file for a class representing a CZ operation.
std::vector< int > involved_qubits_cache
Definition: Gates_block.h:59
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
void add_parent(Gate *parent)
Call to add a parent gate to the current gate.
Definition: Gate.cpp:1247
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.
void set_min_fusion(int min_fusion)
void qgd_sincos_batch< double >(const double *input, double *output, std::size_t count, int stride)
Definition: qgd_math.h:138
void add_ccx(int target_qbit, const std::vector< int > &control_qbits)
Append a CCX gate (i.e.
void set_target_qbit(int target_qbit_in)
Call to set the target qubit for the gate operation.
Definition: Gate.cpp:1076
void add_cu_to_front(int target_qbit, int control_qbit)
Add a CU gate (i.e.
A class representing a RZZ gate.
Definition: RZZ.h:34
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_s(int target_qbit)
Append a S gate to the list of gates.
virtual void apply_to_inner(Matrix_real &parameters_mtx, const Matrix_real &precomputed_sincos, Matrix &input, int parallel=0) override
Internal apply entry that consumes already precomputed sin/cos values.
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 clear_parents()
Call to erase data on parents.
Definition: Gate.cpp:1289
void determine_children(Gate *gate)
Call to obtain the child gates in the circuit.
void add_u1_to_front(int target_qbit)
Add a U1 gate to the front of the list of gates.
int extract_gates(Gates_block *op_block)
Call to extract the gates stored in the class.
void set_matrix(Matrix input)
Call to set the stored matrix in the operation.
Definition: Gate.cpp:1036
void add_cry_to_front(int target_qbit, int control_qbit)
Add a CRY gate to the front of the list of gates.
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:136
std::vector< int > get_target_qbits() const
Call to get the vector of target qubits.
Definition: Gate.cpp:1143
bool contains_adaptive_gate()
Call to determine, whether the circuit contains daptive gate or not.
Double-precision complex matrix (float64).
Definition: matrix.h:38
void add_gates_to_front(std::vector< Gate *> gates_in)
Add an array of gates to the front of the list of gates.
void add_gate_to_front(Gate *gate)
Add an gate to the front of the list of gates.
void copy_to(matrix_base< scalar > &target) const
Copy the current matrix storage into a reusable target matrix.
Definition: H.h:11
Definition: Y.h:11
void add_z(int target_qbit)
Append a Z gate to the list of gates.
Header file for a class representing a gate used in adaptive decomposition.
Definition: RY.h:11
void add_ryy_to_front(std::vector< int > target_qbits)
Add a RYY gate to the front of the list of gates.
int size() const
Call to get the number of the allocated elements.
std::vector< int > control_qbits
Vector of control qubit indices (for multi-qubit gates)
Definition: Gate.h:104
void add_cry(int target_qbit, int control_qbit)
Append a CRY gate to the list of gates.
A class representing a controlled RZ gate.
Definition: CRZ.h:35
Gates_block()
Default constructor of the class.
Definition: Gates_block.cpp:82
void add_rx_to_front(int target_qbit)
Add a RX gate to the front of the list of gates.
A class representing a CRY gate.
Definition: CR.h:37
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
void add_tdg_to_front(int target_qbit)
Add a Tdg gate to the front of the list of gates.
A class responsible for grouping two-qubit (CNOT,CZ,CH) and one-qubit gates into layers.
Definition: Gates_block.h:44
void add_tdg(int target_qbit)
Append a Tdg gate to the list of gates.
Header file for AVX-optimized implementations for applying multi-qubit gate kernels to quantum state ...
virtual Matrix gate_kernel(const Matrix_real &precomputed_sincos)
Compute the gate kernel matrix from precomputed trigonometric values.
Definition: Gate.cpp:2546
void add_u3_to_front(int target_qbit)
Add a U3 gate to the front of the list of gates.
std::string name
A string labeling the gate operation.
Definition: Gate.h:92
virtual std::vector< Matrix > apply_to_combined_inner(Matrix_real &parameters_mtx, const Matrix_real &precomputed_sincos, Matrix &input, int parallel) override
Internal combined forward+derivative entry with precomputed sin/cos.
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
void add_cz(int target_qbit, int control_qbit)
Append a CZ gate gate to the list of gates.
Definition: R.h:11
std::vector< Gate * > children
list of child gates to be applied after this current gate
Definition: Gate.h:114
Fixed point data related to a gate operation.
Definition: common_DFE.h:62
bool is_qbit_present(std::vector< int > involved_qubits, int new_qbit, int num_of_qbits)
Call to check whether the given qubit is involved in the sub-circuit or not.
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
std::vector< int > remove_list_intersection(std::vector< int > &list1, std::vector< int > &list2)
Call to remove those integer elements from list1 which are present in list.
Base class for the representation of general gate operations.
Definition: Gate.h:86
Definition: SXdg.h:11
void add_rzz_to_front(std::vector< int > target_qbits)
Add a RZZ gate to the front of the list of gates.
void set_target_qbits(const std::vector< int > &target_qbits_in)
Call to set the target qubits for the gate operation.
Definition: Gate.cpp:1116
std::string get_name()
Call to get the name label of the gate.
Definition: Gate.cpp:2740
Matrix create_identity(int matrix_size)
Call to create an identity matrix.
Definition: common.cpp:182
void add_gate_nums(std::map< std::string, int > &gate_nums)
Call to add the number of the individual gate types in the circuit to the map given in the argument...
int get_limit()
???????????
Definition: Adaptive.cpp:317
void add_ry_to_front(int target_qbit)
Add a RY gate to the front of the list of gates.
Header file for a class representing a controlled rotation gate around the Y axis.
int parameter_num
the number of free parameters of the operation
Definition: Gate.h:108
virtual void apply_to_list(std::vector< Matrix > &inputs, int parallel)
Call to apply the gate on a list of inputs.
Definition: Gate.cpp:320
int min_fusion
maximal number of qubits in partitions
Definition: Gates_block.h:55
void add_rx(int target_qbit)
Append a RX gate to the list of gates.
Matrix_real_float & as_float32()
void set_parents(std::vector< Gate *> &parents_)
Call to set the parents of the current gate.
Definition: Gate.cpp:2511
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.
A class representing a SYC operation.
Definition: SYC.h:36
void insert_gate(Gate *gate, int idx)
Call to insert a gate at a given position.
double real
the real part of a complex number
Definition: QGDTypes.h:40
void reset_parameter_start_indices()
Method to reset the parameter start indices of gate operations incorporated in the circuit...
A class representing a CRY gate.
Definition: Adaptive.h:38
void add_crz_to_front(int target_qbit, int control_qbit)
Add a CRY gate to the front of the list of gates.
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Gate.h:94
void add_t_to_front(int target_qbit)
Add a T gate to the front of the list of gates.
Definition: SX.h:11
void add_crot(int target_qbit, int control_qbit)
Append a CROT gate gate to the list of gates.
void add_crot_to_front(int target_qbit, int control_qbit)
Add a CROT gate gate to the front of the list of gates.
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:1342
virtual Matrix_real extract_parameters(Matrix_real &parameters) override
Call to extract parameters from the parameter array corresponding to the circuit, in which the gate i...
gate_type
Type definition of operation types (also generalized for decomposition classes derived from the class...
Definition: Gate.h:39
void add_crx(int target_qbit, int control_qbit)
Append a CRY gate to the list of gates.
Definition: T.h:11
Gate * get_gate(int idx)
Call to get the gates stored in the class.
void add_cr_to_front(int target_qbit, int control_qbit)
Add a CR gate to the front of the list of gates.
void fread_wrapper(void *buffer, size_t size, size_t count, FILE *stream)
Wrapper function aound fread with error handling.
Definition: common.cpp:75
Definition: RX.h:11
virtual void apply_to(Matrix &input, int parallel)
Call to apply the gate on the input array/matrix.
Definition: Gate.cpp:433
void list_gates(const Matrix_real &parameters, int start_index)
Call to print the list of gates stored in the block of gates for a specific set of parameters...
virtual std::vector< Matrix > apply_to_combined(Matrix_real &parameters_mtx, Matrix &input, int parallel) override
Combined forward + derivative application with shared precomputed trig cache.
Matrix get_reduced_density_matrix(Matrix_real &parameters_mtx, Matrix &input_state, matrix_base< int > &qbit_list_subset)
Call to evaluate the reduced densiy matrix.
void add_adaptive_to_front(int target_qbit, int control_qbit)
Add a Adaptive gate to the front of the list of gates.
void qgd_sincos_batch< float >(const float *input, float *output, std::size_t count, int stride)
Definition: qgd_math.h:130
virtual std::vector< int > get_involved_qubits(bool only_target=false)
Call to get the qubits involved in the gate operation.
Definition: Gate.cpp:1219
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.
int get_parameter_num() override
Call to get the number of free parameters.
virtual void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operation.
Definition: Gate.cpp:1151
void invalidate_structure_cache()
Definition: SDG.h:11
static tbb::enumerable_thread_specific< int > gates_block_derivative_depth([](){ return 0;})
void add_gates(std::vector< Gate *> gates_in)
Append a list of gates to the list of gates.
void add_cp_to_front(int target_qbit, int control_qbit)
Add a CRY gate to the front of the list of gates.
Matrix_real inverse_reverse_parameters(const Matrix_real &parameters_in, std::vector< Gate *>::iterator gates_it, int num_of_gates)
Call to inverse-reverse the order of the parameters in an array.
void add_sxdg_to_front(int target_qbit)
Add a SXdg gate to the front of the list of gates.
void add_t(int target_qbit)
Append a T gate to the list of gates.
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
SmartAtomicPtr< Gates_block > fusion_block
Definition: Gates_block.h:56
virtual void apply_from_right_inner(Matrix_real &parameters_mtx, const Matrix_real &precomputed_sincos, Matrix &input) override
Internal right-apply entry that consumes already precomputed sin/cos values.
virtual std::vector< Matrix > apply_derivate_to(Matrix_real &parameters_mtx, Matrix &input, int parallel) override
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...
A class representing a CSWAP (Controlled SWAP) operation.
Definition: CSWAP.h:35
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
Class representing a RYY gate.
void add_rzz(std::vector< int > target_qbits)
Append a RZZ gate to the list of gates.
void add_sx(int target_qbit)
Append a SX gate to the list of gates.
Matrix_float create_identity_float(int matrix_size)
Call to create a single-precision complex identity matrix.
Definition: common.cpp:203
void add_swap(const std::vector< int > &target_qbits)
Append a SWAP gate to the list of gates.
Header file for a class representing a Sycamore gate.