Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
Gate.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 */
24 #include "Gate.h"
25 #include "common.h"
26 #include "gate_kernel_templates.h"
27 #include "qgd_math.h"
28 #include <algorithm>
29 #include <cmath>
30 #include <sstream>
31 #include <tbb/enumerable_thread_specific.h>
32 
33 #ifdef USE_AVX
37 #endif
38 
39 #include "apply_kernel_to_input.h"
43 
44 namespace {
45 
46 struct GateKernelScratch {
47  Matrix kernel;
48  Matrix inverse_kernel;
49  Matrix aux_kernel;
50 };
51 
52 struct GateKernelScratchFloat {
53  Matrix_float kernel;
54  Matrix_float inverse_kernel;
55  Matrix_float aux_kernel;
56 };
57 
58 inline bool has_inplace_small_kernel(gate_type type) {
59  switch (type) {
60  case U3_OPERATION:
61  case RY_OPERATION:
62  case RX_OPERATION:
63  case RZ_OPERATION:
64  case X_OPERATION:
65  case SX_OPERATION:
66  case Y_OPERATION:
67  case Z_OPERATION:
68  case H_OPERATION:
69  case R_OPERATION:
70  case T_OPERATION:
71  case TDG_OPERATION:
72  case U1_OPERATION:
73  case U2_OPERATION:
74  case S_OPERATION:
75  case SDG_OPERATION:
76  case SXDG_OPERATION:
77  case CU_OPERATION:
78  case CROT_OPERATION:
79  case RXX_OPERATION:
80  case RYY_OPERATION:
81  case RZZ_OPERATION:
82  return true;
83  default:
84  return false;
85  }
86 }
87 
88 template <typename ScratchT>
89 class ReentrantScratchLease {
90 public:
91  ReentrantScratchLease(tbb::enumerable_thread_specific<std::vector<ScratchT>>& scratch_tls_in,
92  tbb::enumerable_thread_specific<int>& depth_tls_in)
93  : depth_tls(depth_tls_in) {
94  int& depth = depth_tls.local();
95  std::vector<ScratchT>& scratch = scratch_tls_in.local();
96  if (scratch.size() <= static_cast<size_t>(depth)) {
97  scratch.resize(static_cast<size_t>(depth) + 1);
98  }
99  ptr = &scratch[static_cast<size_t>(depth++)];
100  }
101 
102  ~ReentrantScratchLease() {
103  depth_tls.local()--;
104  }
105 
106  ScratchT& get() {
107  return *ptr;
108  }
109 
110 private:
111  tbb::enumerable_thread_specific<int>& depth_tls;
112  ScratchT* ptr;
113 };
114 
115 }
116 
122 
123  // A string labeling the gate operation
124  name = "Gate";
125  // number of qubits spanning the matrix of the operation
126  qbit_num = -1;
127  // The size N of the NxN matrix associated with the operations.
128  matrix_size = -1;
129  // The type of the operation (see enumeration gate_type)
130  type = GENERAL_OPERATION;
131  // The index of the qubit on which the operation acts (target_qbit >= 0)
132  target_qbit = -1;
133  // The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations
134  control_qbit = -1;
135  // Vector-based qubit storage
136  target_qbits.clear();
137  control_qbits.clear();
138  // the number of free parameters of the operation
139  parameter_num = 0;
140  // the index in the parameter array (corrensponding to the encapsulated circuit) where the gate parameters begin (if gates are placed into a circuit a single parameter array is used to execute the whole circuit)
141  parameter_start_idx = 0;
142  matrix_alloc_float_valid = false;
143 }
144 
145 
146 
152 Gate::Gate(int qbit_num_in) {
153 
154 
155 
156  // A string labeling the gate operation
157  name = "Gate";
158  // number of qubits spanning the matrix of the operation
159  qbit_num = qbit_num_in;
160  // the size of the matrix
162  // A string describing the type of the operation
163  type = GENERAL_OPERATION;
164  // The index of the qubit on which the operation acts (target_qbit >= 0)
165  target_qbit = -1;
166  // The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations
167  control_qbit = -1;
168  // Vector-based qubit storage
169  target_qbits.clear();
170  control_qbits.clear();
171  // The number of parameters
172  parameter_num = 0;
173  // the index in the parameter array (corrensponding to the encapsulated circuit) where the gate parameters begin (if gates are placed into a circuit a single parameter array is used to execute the whole circuit)
174  parameter_start_idx = 0;
175  matrix_alloc_float_valid = false;
176 }
177 
178 
186 Gate::Gate(int qbit_num_in, const std::vector<int>& target_qbits_in, const std::vector<int>& control_qbits_in) {
187 
188 
189 
190  // A string labeling the gate operation
191  name = "Gate";
192  // number of qubits spanning the matrix of the operation
193  qbit_num = qbit_num_in;
194  // the size of the matrix
196  // A string describing the type of the operation
197  type = GENERAL_OPERATION;
198  // The number of parameters
199  parameter_num = 0;
200  // the index in the parameter array (corrensponding to the encapsulated circuit) where the gate parameters begin (if gates are placed into a circuit a single parameter array is used to execute the whole circuit)
201  parameter_start_idx = 0;
202  matrix_alloc_float_valid = false;
203 
204  // Validate target qubits
205  for (int tq : target_qbits_in) {
206  if (tq >= qbit_num_in) {
207  std::stringstream sstream;
208  sstream << "Gate: Target qubit index " << tq << " is larger than or equal to the number of qubits " << qbit_num_in << std::endl;
209  print(sstream, 0);
210  throw sstream.str();
211  }
212  }
213 
214  // Validate control qubits
215  for (int cq : control_qbits_in) {
216  if (cq >= qbit_num_in) {
217  std::stringstream sstream;
218  sstream << "Gate: Control qubit index " << cq << " is larger than or equal to the number of qubits " << qbit_num_in << std::endl;
219  print(sstream, 0);
220  throw sstream.str();
221  }
222  }
223 
224  target_qbits = target_qbits_in;
225  control_qbits = control_qbits_in;
226  std::sort(target_qbits.begin(), target_qbits.end());
227  std::sort(control_qbits.begin(), control_qbits.end());
228  // Set the legacy single-qubit members to first elements for backward compatibility
229  target_qbit = target_qbits.empty() ? -1 : target_qbits[0];
230  control_qbit = control_qbits.empty() ? -1 : control_qbits[0];
231 }
232 
233 
238 }
239 
244 void Gate::set_qbit_num( int qbit_num_in ) {
245 
246 
247 
248  if ( qbit_num_in <= target_qbit || qbit_num_in <= control_qbit ) {
249  std::string err("Gate::set_qbit_num: The number of qbits is too small, conflicting with either target_qbit os control_qbit");
250  throw err;
251  }
252 
253 
254  // setting the number of qubits
255  qbit_num = qbit_num_in;
256 
257  // update the size of the matrix
259 
260 }
261 
268 Matrix
270 
271  Matrix gate_matrix = create_identity(matrix_size);
272  apply_to(parameters, gate_matrix, parallel);
273  return gate_matrix;
274 
275 }
276 
277 Matrix
279  return get_matrix(parameters, 0);
280 }
281 
282 Matrix
284  Matrix gate_matrix = create_identity(matrix_size);
285  apply_to(gate_matrix, 0);
286  return gate_matrix;
287 }
288 
289 Matrix
290 Gate::get_matrix( int parallel ) {
291  Matrix gate_matrix = create_identity(matrix_size);
292  apply_to(gate_matrix, parallel);
293  return gate_matrix;
294 }
295 
296 
307 
309  apply_to(parameters, gate_matrix, parallel);
310  return gate_matrix;
311 
312 }
313 
319 void
320 Gate::apply_to_list( std::vector<Matrix>& inputs, int parallel ) {
321 
322  if (parallel == 0) {
323  for (Matrix& input : inputs) {
324  apply_to(input, parallel);
325  }
326  return;
327  }
328 
329  int work_batch = 1;
330  tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
331  for (int idx=r.begin(); idx<r.end(); ++idx) {
332 
333  Matrix* input = &inputs[idx];
334 
335  apply_to( *input, parallel );
336 
337  }
338 
339  });
340 
341 
342 
343 
344 }
345 
346 
347 
354 void
355 Gate::apply_to_list( Matrix_real& parameters_mtx, std::vector<Matrix>& inputs, int parallel ) {
356 
357  if (parallel == 0) {
358  for (Matrix& input : inputs) {
359  apply_to(parameters_mtx, input, parallel);
360  }
361  return;
362  }
363 
364  int work_batch = 1;
365  tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
366  for (int idx=r.begin(); idx<r.end(); ++idx) {
367  apply_to( parameters_mtx, inputs[idx], parallel );
368  }
369  });
370 
371 }
372 
373 
379 void
380 Gate::apply_to_list( std::vector<Matrix_float>& inputs, int parallel ) {
381 
382  if (parallel == 0) {
383  for (Matrix_float& input : inputs) {
384  apply_to(input, parallel);
385  }
386  return;
387  }
388 
389  int work_batch = 1;
390  tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
391  for (int idx=r.begin(); idx<r.end(); ++idx) {
392  Matrix_float* input = &inputs[idx];
393  apply_to( *input, parallel );
394  }
395  });
396 
397 }
398 
399 
406 void
407 Gate::apply_to_list( Matrix_real_float& parameters_mtx, std::vector<Matrix_float>& inputs, int parallel ) {
408 
409  if (parallel == 0) {
410  for (Matrix_float& input : inputs) {
411  apply_to(parameters_mtx, input, parallel);
412  }
413  return;
414  }
415 
416  int work_batch = 1;
417  tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
418  for (int idx=r.begin(); idx<r.end(); ++idx) {
419  apply_to( parameters_mtx, inputs[idx], parallel );
420  }
421  });
422 
423 }
424 
425 
426 
432 void
433 Gate::apply_to( Matrix& input, int parallel ) {
434 
435  if (input.rows != matrix_size ) {
436  std::stringstream sstream;
437  sstream << "Gate::apply_to: Wrong matrix size in Gate gate apply."
438  << " name=" << name
439  << " type=" << type
440  << " qbit_num=" << qbit_num
441  << " matrix_size=" << matrix_size
442  << " input.rows=" << input.rows
443  << " input.cols=" << input.cols << std::endl;
444  print(sstream, 0);
445  throw sstream.str();
446  }
447 
448  Matrix_real empty_params(0, 0);
449  if (has_inplace_small_kernel(type)) {
450  static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
451  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
452  ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
453  GateKernelScratch& scratch = scratch_lease.get();
454  gate_kernel_to(empty_params, scratch.kernel);
455  apply_kernel_to(scratch.kernel, input, false, parallel);
456  return;
457  }
458 
459  Matrix kernel;
460  if (type != GENERAL_OPERATION && type != SWAP_OPERATION && type != CSWAP_OPERATION && type != CCX_OPERATION) {
461  kernel = gate_kernel(empty_params);
462  }
463  apply_kernel_to(kernel, input, false, parallel);
464 }
465 
466 
467 void
468 Gate::apply_to( Matrix_float& input, int parallel ) {
469 
470  if (input.rows != matrix_size) {
471  std::string err("Gate::apply_to(Matrix_float&): Wrong matrix size in gate apply.");
472  throw err;
473  }
474 
475  Matrix_real_float empty_params(0, 0);
476  if (has_inplace_small_kernel(type)) {
477  static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
478  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
479  ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
480  GateKernelScratchFloat& scratch = scratch_lease.get();
481  gate_kernel_to(empty_params, scratch.kernel);
482  apply_kernel_to(scratch.kernel, input, false, parallel);
483  return;
484  }
485 
486  Matrix_float kernel;
487  if (type != GENERAL_OPERATION && type != SWAP_OPERATION && type != CSWAP_OPERATION && type != CCX_OPERATION) {
488  kernel = gate_kernel(empty_params);
489  }
490  apply_kernel_to(kernel, input, false, parallel);
491 }
492 
493 
500 void
501 Gate::apply_to( Matrix_real& parameter_mtx, Matrix& input, int parallel ) {
502 
503  if (input.rows != matrix_size) {
504  std::stringstream sstream;
505  sstream << "Gate::apply_to(Matrix_real&, Matrix&): Wrong matrix size in gate apply."
506  << " name=" << name
507  << " type=" << type
508  << " qbit_num=" << qbit_num
509  << " matrix_size=" << matrix_size
510  << " input.rows=" << input.rows
511  << " input.cols=" << input.cols
512  << " parameter_num=" << parameter_num
513  << " provided_params=" << parameter_mtx.size() << std::endl;
514  print(sstream, 0);
515  throw sstream.str();
516  }
517 
518  // For zero-parameter gates delegate to the no-param virtual.
519  if (parameter_num == 0) {
520  apply_to(input, parallel);
521  return;
522  }
523 
524  Matrix_real precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
525  apply_to_inner(parameter_mtx, precomputed_sincos, input, parallel);
526 }
527 
528 
529 void
530 Gate::apply_to( Matrix_real_float& parameter_mtx, Matrix_float& input, int parallel ) {
531 
532  if (input.rows != matrix_size) {
533  std::string err("Gate::apply_to(Matrix_real_float&, Matrix_float&): Wrong matrix size in gate apply.");
534  throw err;
535  }
536 
537  // For zero-parameter gates delegate to the no-parameter overload.
538  if (parameter_num == 0) {
539  apply_to(input, parallel);
540  return;
541  }
542 
543  Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
544  apply_to_inner(parameter_mtx, precomputed_sincos, input, parallel);
545 }
546 
547 
548 void
549 Gate::apply_to_inner( Matrix_real& parameter_mtx, const Matrix_real& precomputed_sincos, Matrix& input, int parallel ) {
550 
551  if (parameter_num == 0) {
552  apply_to(input, parallel);
553  return;
554  }
555 
556  if (has_inplace_small_kernel(type)) {
557  static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
558  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
559  ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
560  GateKernelScratch& scratch = scratch_lease.get();
561  gate_kernel_to(precomputed_sincos, scratch.kernel);
562  inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
563  apply_kernel_to(scratch.kernel, input, false, parallel, &scratch.inverse_kernel);
564  return;
565  }
566 
567  Matrix kernel = gate_kernel(precomputed_sincos);
568  Matrix inverse_kernel = inverse_gate_kernel(precomputed_sincos);
569  apply_kernel_to(kernel, input, false, parallel, &inverse_kernel);
570 }
571 
572 
573 void
574 Gate::apply_to_inner( Matrix_real_float& parameter_mtx, const Matrix_real_float& precomputed_sincos, Matrix_float& input, int parallel ) {
575 
576  if (parameter_num == 0) {
577  apply_to(input, parallel);
578  return;
579  }
580 
581  if (has_inplace_small_kernel(type)) {
582  static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
583  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
584  ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
585  GateKernelScratchFloat& scratch = scratch_lease.get();
586  gate_kernel_to(precomputed_sincos, scratch.kernel);
587  inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
588  apply_kernel_to(scratch.kernel, input, false, parallel, &scratch.inverse_kernel);
589  return;
590  }
591 
592  Matrix_float kernel = gate_kernel(precomputed_sincos);
593  Matrix_float inverse_kernel = inverse_gate_kernel(precomputed_sincos);
594  apply_kernel_to(kernel, input, false, parallel, &inverse_kernel);
595 }
596 
597 
598 void
599 Gate::apply_to( Matrix_any& input, int parallel ) {
600 
601  if (input.is_float64()) {
602  apply_to(input.as_float64(), parallel);
603  return;
604  }
605 
606  apply_to(input.as_float32(), parallel);
607 }
608 
609 
610 void
611 Gate::apply_to( Matrix_real_any& parameter_mtx, Matrix_any& input, int parallel ) {
612 
613  if (parameter_mtx.is_float64() && input.is_float64()) {
614  apply_to(parameter_mtx.as_float64(), input.as_float64(), parallel);
615  return;
616  }
617 
618  if (parameter_mtx.is_float32() && input.is_float32()) {
619  apply_to(parameter_mtx.as_float32(), input.as_float32(), parallel);
620  return;
621  }
622 
623  std::string err("Gate::apply_to(Matrix_real_any&, Matrix_any&): precision mismatch between parameters and input");
624  throw err;
625 }
626 
627 
628 
635 std::vector<Matrix>
636 Gate::apply_derivative_to_precomputed(const Matrix_real& precomputed_sincos, Matrix& input, int parallel) {
637 
638  std::vector<Matrix> ret;
639  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, ret);
640  return ret;
641 }
642 
643 
644 void
645 Gate::apply_derivative_to_precomputed(const Matrix_real& precomputed_sincos, Matrix& input, int parallel, std::vector<Matrix>& output, size_t offset, bool resize_output) {
646 
647  const int parameter_count = get_parameter_num();
648  if (parameter_count <= 0) {
649  if (resize_output) {
650  output.clear();
651  }
652  return;
653  }
654 
655  const size_t required_size = offset + static_cast<size_t>(parameter_count);
656  if (resize_output) {
657  output.resize(required_size);
658  }
659  else if (output.size() < required_size) {
660  std::string err("Gate::apply_derivative_to_precomputed: output vector is too small.");
661  throw err;
662  }
663 
664  auto calculate_derivative = [&](int param_idx, Matrix& result) {
665  if (has_inplace_small_kernel(type)) {
666  static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
667  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
668  ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
669  GateKernelScratch& scratch = scratch_lease.get();
670  derivative_kernel_to(precomputed_sincos, param_idx, scratch.kernel);
671  derivative_aux_kernel_to(precomputed_sincos, param_idx, scratch.aux_kernel);
672  input.copy_to(result);
673  if (scratch.aux_kernel.size() > 0) {
674  apply_kernel_to(scratch.kernel, result, true, parallel, &scratch.aux_kernel);
675  }
676  else {
677  apply_kernel_to(scratch.kernel, result, true, parallel);
678  }
679  return;
680  }
681 
682  Matrix u3 = derivative_kernel(precomputed_sincos, param_idx);
683  Matrix u3_aux = derivative_aux_kernel(precomputed_sincos, param_idx);
684 
685  input.copy_to(result);
686  if (u3_aux.size() > 0) {
687  apply_kernel_to(u3, result, true, parallel, &u3_aux);
688  }
689  else {
690  apply_kernel_to(u3, result, true, parallel);
691  }
692  };
693 
694  if (parallel == 0 || parameter_count == 1) {
695  for (int param_idx = 0; param_idx < parameter_count; ++param_idx) {
696  calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
697  }
698  }
699  else {
700  tbb::parallel_for(tbb::blocked_range<int>(0, parameter_count, 1), [&](tbb::blocked_range<int> r) {
701  for (int param_idx = r.begin(); param_idx < r.end(); ++param_idx) {
702  calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
703  }
704  });
705  }
706 }
707 
708 
709 std::vector<Matrix_float>
710 Gate::apply_derivative_to_precomputed(const Matrix_real_float& precomputed_sincos, Matrix_float& input, int parallel) {
711 
712  std::vector<Matrix_float> ret;
713  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, ret);
714  return ret;
715 }
716 
717 
718 void
719 Gate::apply_derivative_to_precomputed(const Matrix_real_float& precomputed_sincos, Matrix_float& input, int parallel, std::vector<Matrix_float>& output, size_t offset, bool resize_output) {
720 
721  const int parameter_count = get_parameter_num();
722  if (parameter_count <= 0) {
723  if (resize_output) {
724  output.clear();
725  }
726  return;
727  }
728 
729  const size_t required_size = offset + static_cast<size_t>(parameter_count);
730  if (resize_output) {
731  output.resize(required_size);
732  }
733  else if (output.size() < required_size) {
734  std::string err("Gate::apply_derivative_to_precomputed(Matrix_real_float): output vector is too small.");
735  throw err;
736  }
737 
738  auto calculate_derivative = [&](int param_idx, Matrix_float& result) {
739  if (has_inplace_small_kernel(type)) {
740  static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
741  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
742  ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
743  GateKernelScratchFloat& scratch = scratch_lease.get();
744  derivative_kernel_to(precomputed_sincos, param_idx, scratch.kernel);
745  derivative_aux_kernel_to(precomputed_sincos, param_idx, scratch.aux_kernel);
746  input.copy_to(result);
747  if (scratch.aux_kernel.size() > 0) {
748  apply_kernel_to(scratch.kernel, result, true, parallel, &scratch.aux_kernel);
749  }
750  else {
751  apply_kernel_to(scratch.kernel, result, true, parallel);
752  }
753  return;
754  }
755 
756  Matrix_float u3 = derivative_kernel(precomputed_sincos, param_idx);
757  Matrix_float u3_aux = derivative_aux_kernel(precomputed_sincos, param_idx);
758 
759  input.copy_to(result);
760  if (u3_aux.size() > 0) {
761  apply_kernel_to(u3, result, true, parallel, &u3_aux);
762  }
763  else {
764  apply_kernel_to(u3, result, true, parallel);
765  }
766  };
767 
768  if (parallel == 0 || parameter_count == 1) {
769  for (int param_idx = 0; param_idx < parameter_count; ++param_idx) {
770  calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
771  }
772  }
773  else {
774  tbb::parallel_for(tbb::blocked_range<int>(0, parameter_count, 1), [&](tbb::blocked_range<int> r) {
775  for (int param_idx = r.begin(); param_idx < r.end(); ++param_idx) {
776  calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
777  }
778  });
779  }
780 }
781 
782 
783 std::vector<Matrix>
784 Gate::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input, int parallel ) {
785 
786  Matrix_real precomputed_sincos = precompute_sincos(parameters_mtx_in);
787  return apply_derivative_to_precomputed(precomputed_sincos, input, parallel);
788 }
789 
790 
791 void
792 Gate::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input, int parallel, std::vector<Matrix>& output ) {
793 
794  Matrix_real precomputed_sincos = precompute_sincos(parameters_mtx_in);
795  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output);
796 }
797 
798 
799 std::vector<Matrix_float>
800 Gate::apply_derivate_to( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel ) {
801 
802  Matrix_real_float precomputed_sincos = precompute_sincos(parameters_mtx_in);
803  return apply_derivative_to_precomputed(precomputed_sincos, input, parallel);
804 }
805 
806 
807 void
808 Gate::apply_derivate_to( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel, std::vector<Matrix_float>& output ) {
809 
810  Matrix_real_float precomputed_sincos = precompute_sincos(parameters_mtx_in);
811  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output);
812 }
813 
814 
815 std::vector<Matrix>
816 Gate::apply_to_combined( Matrix_real& parameters_mtx_in, Matrix& input, int parallel ) {
817 
818  Matrix_real precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
819  return apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
820 }
821 
822 
823 void
824 Gate::apply_to_combined( Matrix_real& parameters_mtx_in, Matrix& input, int parallel, std::vector<Matrix>& output ) {
825 
826  Matrix_real precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
827  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, output);
828 }
829 
830 
831 std::vector<Matrix>
832 Gate::apply_to_combined_inner( Matrix_real& parameters_mtx_in, const Matrix_real& precomputed_sincos, Matrix& input, int parallel ) {
833 
834  std::vector<Matrix> ret;
835  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, ret);
836  return ret;
837 }
838 
839 
840 void
841 Gate::apply_to_combined_inner( Matrix_real& parameters_mtx_in, const Matrix_real& precomputed_sincos, Matrix& input, int parallel, std::vector<Matrix>& output ) {
842 
843  const size_t output_size = static_cast<size_t>(get_parameter_num()) + 1;
844  output.resize(output_size);
845  if (parallel == 0) {
846  input.copy_to(output[0]);
847  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
848  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1, false);
849  }
850  else {
851  tbb::parallel_invoke(
852  [&]() {
853  input.copy_to(output[0]);
854  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
855  },
856  [&]() {
857  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1, false);
858  }
859  );
860  }
861 }
862 
863 
864 std::vector<Matrix_float>
865 Gate::apply_to_combined( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel ) {
866 
867  Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
868  return apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
869 }
870 
871 
872 void
873 Gate::apply_to_combined( Matrix_real_float& parameters_mtx_in, Matrix_float& input, int parallel, std::vector<Matrix_float>& output ) {
874 
875  Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
876  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, output);
877 }
878 
879 
880 std::vector<Matrix_float>
881 Gate::apply_to_combined_inner( Matrix_real_float& parameters_mtx_in, const Matrix_real_float& precomputed_sincos, Matrix_float& input, int parallel ) {
882 
883  std::vector<Matrix_float> ret;
884  apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, ret);
885  return ret;
886 }
887 
888 
889 void
890 Gate::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 ) {
891 
892  const size_t output_size = static_cast<size_t>(get_parameter_num()) + 1;
893  output.resize(output_size);
894  if (parallel == 0) {
895  input.copy_to(output[0]);
896  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
897  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1, false);
898  }
899  else {
900  tbb::parallel_invoke(
901  [&]() {
902  input.copy_to(output[0]);
903  apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
904  },
905  [&]() {
906  apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1, false);
907  }
908  );
909  }
910 }
911 
912 
913 
918 void
920 
921  if (input.cols != matrix_size ) {
922  std::string err("Gate::apply_from_right(Matrix&): Wrong matrix size in gate apply.");
923  throw err;
924  }
925 
926  Matrix empty_kernel;
927  apply_kernel_from_right(empty_kernel, input);
928 
929 }
930 
931 
932 void
934 
935  if (input.cols != matrix_size ) {
936  std::string err("Gate::apply_from_right(Matrix_float&): Wrong matrix size in gate apply.");
937  throw err;
938  }
939 
940  Matrix_float empty_kernel;
941  apply_kernel_from_right(empty_kernel, input);
942 }
943 
949 void
950 Gate::apply_from_right( Matrix_real& parameter_mtx, Matrix& input ) {
951 
952  if (input.cols != matrix_size ) {
953  std::string err("Gate::apply_from_right(Matrix_real&, Matrix&): Wrong matrix size in gate apply.");
954  throw err;
955  }
956 
957  Matrix_real precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
958  apply_from_right_inner(parameter_mtx, precomputed_sincos, input);
959 
960 }
961 
962 
963 void
965 
966  if (input.cols != matrix_size ) {
967  std::string err("Gate::apply_from_right(Matrix_real_float&, Matrix_float&): Wrong matrix size in gate apply.");
968  throw err;
969  }
970 
971  Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
972  apply_from_right_inner(parameter_mtx, precomputed_sincos, input);
973 }
974 
975 
976 void
977 Gate::apply_from_right_inner( Matrix_real& parameter_mtx, const Matrix_real& precomputed_sincos, Matrix& input ) {
978 
979  if (has_inplace_small_kernel(type)) {
980  static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
981  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
982  ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
983  GateKernelScratch& scratch = scratch_lease.get();
984  gate_kernel_to(precomputed_sincos, scratch.kernel);
985  inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
986  apply_kernel_from_right(scratch.kernel, input, &scratch.inverse_kernel);
987  return;
988  }
989 
990  Matrix kernel = gate_kernel(precomputed_sincos);
991  Matrix inv_kernel = inverse_gate_kernel(precomputed_sincos);
992  apply_kernel_from_right(kernel, input, &inv_kernel);
993 }
994 
995 
996 void
997 Gate::apply_from_right_inner( Matrix_real_float& parameter_mtx, const Matrix_real_float& precomputed_sincos, Matrix_float& input ) {
998 
999  if (has_inplace_small_kernel(type)) {
1000  static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
1001  static tbb::enumerable_thread_specific<int> depth_tls([](){ return 0; });
1002  ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
1003  GateKernelScratchFloat& scratch = scratch_lease.get();
1004  gate_kernel_to(precomputed_sincos, scratch.kernel);
1005  inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
1006  apply_kernel_from_right(scratch.kernel, input, &scratch.inverse_kernel);
1007  return;
1008  }
1009 
1010  Matrix_float kernel = gate_kernel(precomputed_sincos);
1011  Matrix_float inv_kernel = inverse_gate_kernel(precomputed_sincos);
1012  apply_kernel_from_right(kernel, input, &inv_kernel);
1013 }
1014 
1015 
1018 
1019  return precompute_sincos(parameters);
1020 }
1021 
1022 
1025 
1026  return precompute_sincos(parameters);
1027 }
1028 
1029 
1035 void
1037  matrix_alloc = input;
1038  matrix_alloc_float = Matrix_float();
1039  matrix_alloc_float_valid = false;
1040 }
1041 
1042 void
1044  matrix_alloc_float = input;
1045  matrix_alloc_float_valid = true;
1046  matrix_alloc = Matrix();
1047 }
1048 
1049 
1054 void Gate::set_control_qbit(int control_qbit_in){
1055 
1056  if ( control_qbit_in >= qbit_num ) {
1057  std::string err("Gate::set_target_qbit: Wrong value of the control qbit: of out of the range given by qbit_num");
1058  throw err;
1059  }
1060 
1061  control_qbit = control_qbit_in;
1062 
1063  // Synchronize with vector storage
1064  if (control_qbits.empty()) {
1065  control_qbits.push_back(control_qbit_in);
1066  } else {
1067  control_qbits[0] = control_qbit_in;
1068  }
1069 }
1070 
1071 
1076 void Gate::set_target_qbit(int target_qbit_in){
1077 
1078  if ( target_qbit_in >= qbit_num ) {
1079  std::string err("Gate::set_target_qbit: Wrong value of the target qbit: out of the range given by qbit_num");
1080  throw err;
1081  }
1082 
1083  target_qbit = target_qbit_in;
1084 
1085  // Synchronize with vector storage
1086  if (target_qbits.empty()) {
1087  target_qbits.push_back(target_qbit_in);
1088  } else {
1089  target_qbits[0] = target_qbit_in;
1090  }
1091 }
1092 
1097 void Gate::set_control_qbits(const std::vector<int>& control_qbits_in) {
1098  // Validate control qubits
1099  for (int cq : control_qbits_in) {
1100  if (cq >= qbit_num) {
1101  std::stringstream sstream;
1102  sstream << "Gate::set_control_qbits: Control qubit index " << cq << " is out of range" << std::endl;
1103  print(sstream, 0);
1104  throw sstream.str();
1105  }
1106  }
1107 
1108  control_qbits = control_qbits_in;
1109  control_qbit = control_qbits.empty() ? -1 : control_qbits[0];
1110 }
1111 
1116 void Gate::set_target_qbits(const std::vector<int>& target_qbits_in) {
1117  // Validate target qubits
1118  for (int tq : target_qbits_in) {
1119  if (tq >= qbit_num) {
1120  std::stringstream sstream;
1121  sstream << "Gate::set_target_qbits: Target qubit index " << tq << " is out of range" << std::endl;
1122  print(sstream, 0);
1123  throw sstream.str();
1124  }
1125  }
1126 
1127  target_qbits = target_qbits_in;
1128  target_qbit = target_qbits.empty() ? -1 : target_qbits[0];
1129 }
1130 
1135 std::vector<int> Gate::get_control_qbits() const {
1136  return control_qbits;
1137 }
1138 
1143 std::vector<int> Gate::get_target_qbits() const {
1144  return target_qbits;
1145 }
1146 
1151 void Gate::reorder_qubits( std::vector<int> qbit_list ) {
1152 
1153  // check the number of qubits
1154  if ((int)qbit_list.size() != qbit_num ) {
1155  std::string err("Gate::reorder_qubits: Wrong number of qubits.");
1156  throw err;
1157  }
1158 
1159  int control_qbit_new = control_qbit;
1160  int target_qbit_new = target_qbit;
1161 
1162  // setting the new value for the target qubit
1163  for (int idx=0; idx<qbit_num; idx++) {
1164  if (target_qbit == qbit_list[idx]) {
1165  target_qbit_new = idx;
1166  }
1167  if (control_qbit == qbit_list[idx]) {
1168  control_qbit_new = idx;
1169  }
1170  }
1171 
1172  control_qbit = control_qbit_new;
1173  target_qbit = target_qbit_new;
1174 
1175  // Reorder control qubits vector
1176  for (size_t i = 0; i < control_qbits.size(); i++) {
1177  int old_qbit = control_qbits[i];
1178  for (int idx = 0; idx < qbit_num; idx++) {
1179  if (old_qbit == qbit_list[idx]) {
1180  control_qbits[i] = idx;
1181  break;
1182  }
1183  }
1184  }
1185 
1186  // Reorder target qubits vector
1187  for (size_t i = 0; i < target_qbits.size(); i++) {
1188  int old_qbit = target_qbits[i];
1189  for (int idx = 0; idx < qbit_num; idx++) {
1190  if (old_qbit == qbit_list[idx]) {
1191  target_qbits[i] = idx;
1192  break;
1193  }
1194  }
1195  }
1196 }
1197 
1198 
1204  return target_qbit;
1205 }
1206 
1212  return control_qbit;
1213 }
1214 
1219 std::vector<int> Gate::get_involved_qubits(bool only_target) {
1220 
1221  std::vector<int> involved_qbits;
1222 
1223  // Use vector storage if available, otherwise fall back to single qubits
1224  if (!target_qbits.empty()) {
1225  involved_qbits.insert(involved_qbits.end(), target_qbits.begin(), target_qbits.end());
1226  } else if (target_qbit != -1) {
1227  involved_qbits.push_back(target_qbit);
1228  }
1229 
1230  if (!only_target) {
1231  if (!control_qbits.empty()) {
1232  involved_qbits.insert(involved_qbits.end(), control_qbits.begin(), control_qbits.end());
1233  } else if (control_qbit != -1) {
1234  involved_qbits.push_back(control_qbit);
1235  }
1236  }
1237 
1238  return involved_qbits;
1239 
1240 }
1241 
1242 
1247 void Gate::add_parent( Gate* parent ) {
1248 
1249  // check if parent already present in th elist of parents
1250  if ( std::count(parents.begin(), parents.end(), parent) > 0 ) {
1251  return;
1252  }
1253 
1254  parents.push_back( parent );
1255 
1256 }
1257 
1258 
1259 
1264 void Gate::add_child( Gate* child ) {
1265 
1266  // check if parent already present in th elist of parents
1267  if ( std::count(children.begin(), children.end(), child) > 0 ) {
1268  return;
1269  }
1270 
1271  children.push_back( child );
1272 
1273 }
1274 
1275 
1280 
1281  children.clear();
1282 
1283 }
1284 
1285 
1290 
1291  parents.clear();
1292 
1293 }
1294 
1295 
1296 
1301 std::vector<Gate*> Gate::get_parents() {
1302 
1303  return parents;
1304 
1305 }
1306 
1307 
1312 std::vector<Gate*> Gate::get_children() {
1313 
1314  return children;
1315 
1316 }
1317 
1318 
1319 
1325  return parameter_num;
1326 }
1327 
1328 
1334  return type;
1335 }
1336 
1337 
1343  return qbit_num;
1344 }
1345 
1346 
1352 
1353  Gate* ret = new Gate( qbit_num );
1354  if (matrix_alloc_float_valid) {
1355  ret->set_matrix( matrix_alloc_float );
1356  }
1357  else {
1358  ret->set_matrix( matrix_alloc );
1359  }
1360 
1361  ret->set_parameter_start_idx( get_parameter_start_idx() );
1362  ret->set_parents( parents );
1363  ret->set_children( children );
1364 
1365  return ret;
1366 }
1367 
1368 
1369 
1370 Matrix
1371 Gate::derivative_kernel(const Matrix_real& precomputed_sincos, int param_idx) {
1372 
1373  if (param_idx < 0 || param_idx >= get_parameter_num() || precomputed_sincos.cols < 2) {
1374  return Matrix();
1375  }
1376 
1377  static tbb::enumerable_thread_specific<Matrix_real> shifted_tls;
1378  Matrix_real& shifted = shifted_tls.local();
1379  precomputed_sincos.copy_to(shifted);
1380  const int offset = param_idx * shifted.stride;
1381  const double sin_theta = shifted[offset + 0];
1382  const double cos_theta = shifted[offset + 1];
1383 
1384  // sin(theta + pi/2) = cos(theta), cos(theta + pi/2) = -sin(theta)
1385  shifted[offset + 0] = cos_theta;
1386  shifted[offset + 1] = -sin_theta;
1387 
1388  return gate_kernel(shifted);
1389 }
1390 
1391 
1393 Gate::derivative_kernel(const Matrix_real_float& precomputed_sincos, int param_idx) {
1394 
1395  if (param_idx < 0 || param_idx >= get_parameter_num() || precomputed_sincos.cols < 2) {
1396  return Matrix_float();
1397  }
1398 
1399  static tbb::enumerable_thread_specific<Matrix_real_float> shifted_tls;
1400  Matrix_real_float& shifted = shifted_tls.local();
1401  precomputed_sincos.copy_to(shifted);
1402  const int offset = param_idx * shifted.stride;
1403  const float sin_theta = shifted[offset + 0];
1404  const float cos_theta = shifted[offset + 1];
1405 
1406  // sin(theta + pi/2) = cos(theta), cos(theta + pi/2) = -sin(theta)
1407  shifted[offset + 0] = cos_theta;
1408  shifted[offset + 1] = -sin_theta;
1409 
1410  return gate_kernel(shifted);
1411 }
1412 
1413 
1414 Matrix
1415 Gate::derivative_aux_kernel(const Matrix_real& precomputed_sincos, int param_idx) {
1416  (void)precomputed_sincos;
1417  (void)param_idx;
1418  return Matrix();
1419 }
1420 
1421 
1423 Gate::derivative_aux_kernel(const Matrix_real_float& precomputed_sincos, int param_idx) {
1424  (void)precomputed_sincos;
1425  (void)param_idx;
1426  return Matrix_float();
1427 }
1428 
1429 
1432 
1433  if (parameter_num <= 0) {
1434  return Matrix_real(0, 0);
1435  }
1436 
1437  if ((int)parameters.size() < parameter_num) {
1438  std::string err(name + "::precompute_sincos(double): not enough parameters supplied.");
1439  throw err;
1440  }
1441 
1442  Matrix_real sincos(parameter_num, 2);
1443  qgd_sincos_batch<double>(parameters.get_data(), sincos.get_data(), parameter_num, sincos.stride);
1444 
1445  return sincos;
1446 }
1447 
1448 
1451 
1452  if (parameter_num <= 0) {
1453  return Matrix_real_float(0, 0);
1454  }
1455 
1456  if ((int)parameters.size() < parameter_num) {
1457  std::string err(name + "::precompute_sincos(float): not enough parameters supplied.");
1458  throw err;
1459  }
1460 
1461  Matrix_real_float sincos(parameter_num, 2);
1462  qgd_sincos_batch<float>(parameters.get_data(), sincos.get_data(), parameter_num, sincos.stride);
1463 
1464  return sincos;
1465 }
1466 
1467 
1468 
1477 void
1478 Gate::apply_kernel_to(Matrix& u3_1qbit, Matrix& input, bool deriv, int parallel, const Matrix* alt_kernel) {
1479 
1480  (void)deriv;
1481 
1482  if (type == SWAP_OPERATION || type == CSWAP_OPERATION) {
1483  switch (parallel) {
1484  case 0:
1485  apply_SWAP_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1486  case 1:
1487  apply_SWAP_kernel_to_input_omp(input, target_qbits, control_qbits, matrix_size); break;
1488  case 2:
1489  apply_SWAP_kernel_to_input_tbb(input, target_qbits, control_qbits, matrix_size); break;
1490  default:
1491  apply_SWAP_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1492  }
1493  return;
1494  }
1495 
1496  if (type == CCX_OPERATION) {
1497  switch (parallel) {
1498  case 0:
1499  apply_X_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1500  case 1:
1501  apply_X_kernel_to_input_omp(input, target_qbits, control_qbits, matrix_size); break;
1502  case 2:
1503  apply_X_kernel_to_input_tbb(input, target_qbits, control_qbits, matrix_size); break;
1504  default:
1505  apply_X_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1506  }
1507  return;
1508  }
1509 
1510  if (type == CNOT_OPERATION) {
1511  std::vector<int> tqbits = {target_qbit};
1512  std::vector<int> cqbits = {control_qbit};
1513  switch (parallel) {
1514  case 0:
1515  apply_X_kernel_to_input(input, tqbits, cqbits, matrix_size); break;
1516  case 1:
1517  apply_X_kernel_to_input_omp(input, tqbits, cqbits, matrix_size); break;
1518  case 2:
1519  apply_X_kernel_to_input_tbb(input, tqbits, cqbits, matrix_size); break;
1520  default:
1521  apply_X_kernel_to_input(input, tqbits, cqbits, matrix_size); break;
1522  }
1523  return;
1524  }
1525 
1526  if (type == CZ_OPERATION) {
1527  switch (parallel) {
1528  case 0:
1530  case 1:
1532  case 2:
1534  default:
1536  }
1537  return;
1538  }
1539 
1540  if (type == CH_OPERATION) {
1541  switch (parallel) {
1542  case 0:
1544  case 1:
1546  case 2:
1548  default:
1550  }
1551  return;
1552  }
1553 
1554  if (type == SYC_OPERATION) {
1555  switch (parallel) {
1556  case 0:
1558  case 1:
1560  case 2:
1562  default:
1564  }
1565  return;
1566  }
1567 
1568  if (type == CROT_OPERATION && alt_kernel != nullptr) {
1569  Matrix branch0 = alt_kernel->copy();
1570  Matrix branch1 = u3_1qbit.copy();
1571 #ifdef USE_AVX
1572  if (parallel) {
1574  } else {
1575  apply_crot_kernel_to_matrix_input_AVX(branch0, branch1, input, target_qbit, control_qbit, input.rows);
1576  }
1577 #else
1578  apply_crot_kernel_to_matrix_input(branch0, branch1, input, target_qbit, control_qbit, input.rows);
1579 #endif
1580  return;
1581  }
1582 
1583  if (type == GENERAL_OPERATION) {
1584  if (matrix_alloc_float_valid && matrix_alloc.rows == 0 && matrix_alloc_float.rows > 0) {
1585  matrix_alloc = matrix_alloc_float.to_float64();
1586  }
1587  const std::vector<int> involved_qbits = get_involved_qubits();
1588  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
1589  const bool is_state_vector = (input.cols == 1);
1590  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
1591  const bool has_full_matrix = matrix_alloc.rows == matrix_size && matrix_alloc.cols == matrix_size;
1592  const bool has_local_matrix = involved_qbit_num > 0
1593  && matrix_alloc.rows == Power_of_2(involved_qbit_num)
1594  && matrix_alloc.cols == Power_of_2(involved_qbit_num);
1595  const bool can_use_large_kernel = !has_any_control
1596  && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1597  || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1598 
1599  if (has_full_matrix) {
1600  if (can_use_large_kernel) {
1601  switch (parallel) {
1602  case 0:
1603  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1604  break;
1605  case 1:
1606 #ifdef USE_AVX
1607  apply_large_kernel_to_input_AVX_OpenMP(matrix_alloc, input, involved_qbits, matrix_size);
1608 #else
1609  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1610 #endif
1611  break;
1612  case 2:
1613 #ifdef USE_AVX
1614  apply_large_kernel_to_input_AVX_TBB(matrix_alloc, input, involved_qbits, matrix_size);
1615 #else
1616  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1617 #endif
1618  break;
1619  default:
1620  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1621  }
1622  }
1623  else {
1624  Matrix transformed = dot(matrix_alloc, input);
1625  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex16));
1626  }
1627  return;
1628  }
1629 
1630  if (has_local_matrix && matrix_alloc.rows == 2 && matrix_alloc.cols == 2 && involved_qbit_num == 1) {
1631  u3_1qbit = matrix_alloc;
1632  }
1633  else if (has_local_matrix && can_use_large_kernel) {
1634  switch (parallel) {
1635  case 0:
1636  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1637  break;
1638  case 1:
1639 #ifdef USE_AVX
1640  apply_large_kernel_to_input_AVX_OpenMP(matrix_alloc, input, involved_qbits, matrix_size);
1641 #else
1642  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1643 #endif
1644  break;
1645  case 2:
1646 #ifdef USE_AVX
1647  apply_large_kernel_to_input_AVX_TBB(matrix_alloc, input, involved_qbits, matrix_size);
1648 #else
1649  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1650 #endif
1651  break;
1652  default:
1653  apply_large_kernel_to_input(matrix_alloc, input, involved_qbits, matrix_size);
1654  }
1655  return;
1656  }
1657  else {
1658  std::string err("Gate::apply_kernel_to(Matrix&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
1659  throw err;
1660  }
1661  }
1662 
1663  if (u3_1qbit.rows != 2 || u3_1qbit.cols != 2) {
1664  const std::vector<int> involved_qbits = get_involved_qubits();
1665  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
1666  const bool is_state_vector = (input.cols == 1);
1667  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
1668  const bool can_use_large_kernel = !has_any_control
1669  && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1670  || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1671 
1672  if (can_use_large_kernel) {
1673  switch (parallel) {
1674  case 0:
1675  apply_large_kernel_to_input(u3_1qbit, input, involved_qbits, matrix_size);
1676  break;
1677  case 1:
1678 #ifdef USE_AVX
1679  apply_large_kernel_to_input_AVX_OpenMP(u3_1qbit, input, involved_qbits, matrix_size);
1680 #else
1681  apply_large_kernel_to_input(u3_1qbit, input, involved_qbits, matrix_size);
1682 #endif
1683  break;
1684  case 2:
1685 #ifdef USE_AVX
1686  apply_large_kernel_to_input_AVX_TBB(u3_1qbit, input, involved_qbits, matrix_size);
1687 #else
1688  apply_large_kernel_to_input(u3_1qbit, input, involved_qbits, matrix_size);
1689 #endif
1690  break;
1691  default:
1692  apply_large_kernel_to_input(u3_1qbit, input, involved_qbits, matrix_size);
1693  }
1694  return;
1695  }
1696 
1697  if (u3_1qbit.rows == matrix_size && u3_1qbit.cols == matrix_size) {
1698  Matrix transformed = dot(u3_1qbit, input);
1699  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex16));
1700  return;
1701  }
1702 
1703  std::string err("Gate::apply_kernel_to(Matrix&): unsupported non-2x2 kernel dispatch for this configuration.");
1704  throw err;
1705  }
1706 
1707 #ifdef USE_AVX
1708 
1709  // apply kernel on state vector
1710  if ( input.cols == 1 && (qbit_num<14 || !parallel) ) {
1712  return;
1713  }
1714  else if ( input.cols == 1 ) {
1715  if ( parallel == 1 ) {
1717  }
1718  else if ( parallel == 2 ) {
1720  }
1721  else {
1722  std::string err("Gate::apply_kernel_to: the argument parallel should be either 0,1 or 2. Set 0 for sequential execution (default), 1 for parallel execution with OpenMP and 2 for parallel with TBB");
1723  throw err;
1724  }
1725  return;
1726  }
1727 
1728 
1729 
1730  // unitary transform kernels
1731  if ( qbit_num < 4 ) {
1733  return;
1734  }
1735  else if ( qbit_num < 10 || !parallel) {
1737  return;
1738  }
1739  else {
1741  return;
1742  }
1743 
1744 
1745 #else
1746 
1747  // apply kernel on state vector
1748  if ( input.cols == 1 && (qbit_num<10 || !parallel) ) {
1750  return;
1751  }
1752  else if ( input.cols == 1 ) {
1754  return;
1755  }
1756 
1757 
1758  // apply kernel on unitary
1759  apply_kernel_to_input(u3_1qbit, input, deriv, target_qbit, control_qbit, matrix_size);
1760 
1761 
1762 
1763 
1764 
1765 #endif // USE_AVX
1766 
1767 
1768 }
1769 
1770 
1771 void
1772 Gate::apply_kernel_to(Matrix_float& u3_1qbit, Matrix_float& input, bool deriv, int parallel, const Matrix_float* alt_kernel) {
1773 
1774  (void)deriv;
1775 
1776  if (type == SWAP_OPERATION || type == CSWAP_OPERATION) {
1777  switch (parallel) {
1778  case 0:
1779  apply_SWAP_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1780  case 1:
1781  apply_SWAP_kernel_to_input_omp(input, target_qbits, control_qbits, matrix_size); break;
1782  case 2:
1783  apply_SWAP_kernel_to_input_tbb(input, target_qbits, control_qbits, matrix_size); break;
1784  default:
1785  apply_SWAP_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1786  }
1787  return;
1788  }
1789 
1790  if (type == CCX_OPERATION) {
1791  switch (parallel) {
1792  case 0:
1793  apply_X_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1794  case 1:
1795  apply_X_kernel_to_input_omp(input, target_qbits, control_qbits, matrix_size); break;
1796  case 2:
1797  apply_X_kernel_to_input_tbb(input, target_qbits, control_qbits, matrix_size); break;
1798  default:
1799  apply_X_kernel_to_input(input, target_qbits, control_qbits, matrix_size); break;
1800  }
1801  return;
1802  }
1803 
1804  if (type == CNOT_OPERATION) {
1805  std::vector<int> tqbits = {target_qbit};
1806  std::vector<int> cqbits = {control_qbit};
1807  switch (parallel) {
1808  case 0:
1809  apply_X_kernel_to_input(input, tqbits, cqbits, matrix_size); break;
1810  case 1:
1811  apply_X_kernel_to_input_omp(input, tqbits, cqbits, matrix_size); break;
1812  case 2:
1813  apply_X_kernel_to_input_tbb(input, tqbits, cqbits, matrix_size); break;
1814  default:
1815  apply_X_kernel_to_input(input, tqbits, cqbits, matrix_size); break;
1816  }
1817  return;
1818  }
1819 
1820  if (type == CZ_OPERATION) {
1821  switch (parallel) {
1822  case 0:
1824  case 1:
1826  case 2:
1828  default:
1830  }
1831  return;
1832  }
1833 
1834  if (type == CH_OPERATION) {
1835  switch (parallel) {
1836  case 0:
1838  case 1:
1840  case 2:
1842  default:
1844  }
1845  return;
1846  }
1847 
1848  if (type == SYC_OPERATION) {
1849  switch (parallel) {
1850  case 0:
1852  case 1:
1854  case 2:
1856  default:
1858  }
1859  return;
1860  }
1861 
1862  if (type == CROT_OPERATION && alt_kernel != nullptr) {
1863  Matrix_float branch0 = alt_kernel->copy();
1864  Matrix_float branch1 = u3_1qbit.copy();
1865  if (input.cols != 1) {
1866  for (int col_idx = 0; col_idx < input.cols; ++col_idx) {
1867  Matrix_float col_mtx(input.rows, 1);
1868  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1869  col_mtx[row_idx * col_mtx.stride] = input[row_idx * input.stride + col_idx];
1870  }
1871 
1872  Matrix_float col_branch0 = branch0.copy();
1873  Matrix_float col_branch1 = branch1.copy();
1874 #ifdef USE_AVX
1875  if (parallel) {
1876  apply_crot_kernel_to_matrix_input_AVX_parallel32(col_branch0, col_branch1, col_mtx, target_qbit, control_qbit, col_mtx.rows);
1877  } else {
1878  apply_crot_kernel_to_matrix_input_AVX32(col_branch0, col_branch1, col_mtx, target_qbit, control_qbit, col_mtx.rows);
1879  }
1880 #else
1881  apply_crot_kernel_to_matrix_input(col_branch0, col_branch1, col_mtx, target_qbit, control_qbit, col_mtx.rows);
1882 #endif
1883 
1884  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1885  input[row_idx * input.stride + col_idx] = col_mtx[row_idx * col_mtx.stride];
1886  }
1887  }
1888  } else {
1889 #ifdef USE_AVX
1890  if (parallel) {
1892  } else {
1893  apply_crot_kernel_to_matrix_input_AVX32(branch0, branch1, input, target_qbit, control_qbit, input.rows);
1894  }
1895 #else
1896  apply_crot_kernel_to_matrix_input(branch0, branch1, input, target_qbit, control_qbit, input.rows);
1897 #endif
1898  }
1899  return;
1900  }
1901 
1902  if (type == GENERAL_OPERATION) {
1903  const std::vector<int> involved_qbits = get_involved_qubits();
1904  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
1905  const bool is_state_vector = (input.cols == 1);
1906  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
1907  Matrix_float matrix_alloc32;
1908  if (matrix_alloc_float_valid) {
1909  matrix_alloc32 = matrix_alloc_float;
1910  }
1911  else {
1912  matrix_alloc32 = matrix_alloc.to_float32();
1913  }
1914  const bool has_full_matrix = matrix_alloc32.rows == matrix_size && matrix_alloc32.cols == matrix_size;
1915  const bool has_local_matrix = involved_qbit_num > 0
1916  && matrix_alloc32.rows == Power_of_2(involved_qbit_num)
1917  && matrix_alloc32.cols == Power_of_2(involved_qbit_num);
1918  const bool can_use_large_kernel = !has_any_control
1919  && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1920  || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1921 
1922  if (has_full_matrix) {
1923  if (can_use_large_kernel) {
1924 #ifdef USE_AVX
1925  switch (parallel) {
1926  case 0:
1927  apply_large_kernel_to_input_AVX32(matrix_alloc32, input, involved_qbits, matrix_size);
1928  break;
1929  case 1:
1930  apply_large_kernel_to_input_AVX_OpenMP32(matrix_alloc32, input, involved_qbits, matrix_size);
1931  break;
1932  case 2:
1933  apply_large_kernel_to_input_AVX_TBB32(matrix_alloc32, input, involved_qbits, matrix_size);
1934  break;
1935  default:
1936  apply_large_kernel_to_input_AVX32(matrix_alloc32, input, involved_qbits, matrix_size);
1937  }
1938 #else
1939  apply_large_kernel_to_input(matrix_alloc32, input, involved_qbits, matrix_size);
1940 #endif
1941  }
1942  else {
1943  Matrix_float transformed = dot(matrix_alloc32, input);
1944  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex8));
1945  }
1946  return;
1947  }
1948 
1949  if (has_local_matrix && matrix_alloc32.rows == 2 && matrix_alloc32.cols == 2 && involved_qbit_num == 1) {
1950  u3_1qbit = matrix_alloc32;
1951  }
1952  else if (has_local_matrix && can_use_large_kernel) {
1953 #ifdef USE_AVX
1954  switch (parallel) {
1955  case 0:
1956  apply_large_kernel_to_input_AVX32(matrix_alloc32, input, involved_qbits, matrix_size);
1957  break;
1958  case 1:
1959  apply_large_kernel_to_input_AVX_OpenMP32(matrix_alloc32, input, involved_qbits, matrix_size);
1960  break;
1961  case 2:
1962  apply_large_kernel_to_input_AVX_TBB32(matrix_alloc32, input, involved_qbits, matrix_size);
1963  break;
1964  default:
1965  apply_large_kernel_to_input_AVX32(matrix_alloc32, input, involved_qbits, matrix_size);
1966  }
1967 #else
1968  apply_large_kernel_to_input(matrix_alloc32, input, involved_qbits, matrix_size);
1969 #endif
1970  return;
1971  }
1972  else {
1973  std::string err("Gate::apply_kernel_to(Matrix_float&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
1974  throw err;
1975  }
1976  }
1977 
1978  if (u3_1qbit.rows != 2 || u3_1qbit.cols != 2) {
1979  const std::vector<int> involved_qbits = get_involved_qubits();
1980  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
1981  const bool is_state_vector = (input.cols == 1);
1982  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
1983  const bool can_use_large_kernel = !has_any_control
1984  && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1985  || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1986 
1987  if (can_use_large_kernel) {
1988 #ifdef USE_AVX
1989  switch (parallel) {
1990  case 0:
1991  apply_large_kernel_to_input_AVX32(u3_1qbit, input, involved_qbits, matrix_size);
1992  break;
1993  case 1:
1994  apply_large_kernel_to_input_AVX_OpenMP32(u3_1qbit, input, involved_qbits, matrix_size);
1995  break;
1996  case 2:
1997  apply_large_kernel_to_input_AVX_TBB32(u3_1qbit, input, involved_qbits, matrix_size);
1998  break;
1999  default:
2000  apply_large_kernel_to_input_AVX32(u3_1qbit, input, involved_qbits, matrix_size);
2001  }
2002 #else
2003  apply_large_kernel_to_input(u3_1qbit, input, involved_qbits, matrix_size);
2004 #endif
2005  return;
2006  }
2007 
2008  if (u3_1qbit.rows == matrix_size && u3_1qbit.cols == matrix_size) {
2009  Matrix_float transformed = dot(u3_1qbit, input);
2010  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex8));
2011  return;
2012  }
2013 
2014  std::string err("Gate::apply_kernel_to(Matrix_float&): unsupported non-2x2 kernel dispatch for this configuration.");
2015  throw err;
2016  }
2017 
2018 #ifdef USE_AVX
2019 
2020  // apply kernel on state vector
2021  if ( input.cols == 1 && (qbit_num<14 || !parallel) ) {
2023  return;
2024  }
2025  else if ( input.cols == 1 ) {
2026  if ( parallel == 1 ) {
2028  }
2029  else if ( parallel == 2 ) {
2031  }
2032  else {
2033  std::string err("Gate::apply_kernel_to(Matrix_float&): the argument parallel should be either 0,1 or 2.");
2034  throw err;
2035  }
2036  return;
2037  }
2038 
2039  // unitary transform kernels
2040  if ( qbit_num < 4 ) {
2042  return;
2043  }
2044  else if ( qbit_num < 10 || !parallel) {
2046  return;
2047  }
2048  else {
2050  return;
2051  }
2052 
2053 #else
2054 
2055  // apply kernel on state vector
2056  if ( input.cols == 1 && (qbit_num < 10 || !parallel) ) {
2058  return;
2059  }
2060  else if ( input.cols == 1 ) {
2062  return;
2063  }
2064 
2065  apply_kernel_to_input(u3_1qbit, input, deriv, target_qbit, control_qbit, matrix_size);
2066 
2067 #endif // USE_AVX
2068 
2069 }
2070 
2071 
2072 
2073 
2074 
2081 void
2082 Gate::apply_kernel_from_right( Matrix& u3_1qbit, Matrix& input, const Matrix* alt_kernel ) {
2083 
2084  if (type == SWAP_OPERATION || type == CSWAP_OPERATION) {
2085  if (qbit_num < 10) {
2086  apply_SWAP_kernel_from_right(input, target_qbits, control_qbits, matrix_size);
2087  } else {
2088  apply_SWAP_kernel_from_right_tbb(input, target_qbits, control_qbits, matrix_size);
2089  }
2090  return;
2091  }
2092 
2093  if (type == CCX_OPERATION) {
2094  if (qbit_num < 10) {
2095  apply_X_kernel_from_right(input, target_qbits, control_qbits, matrix_size);
2096  } else {
2097  apply_X_kernel_from_right_tbb(input, target_qbits, control_qbits, matrix_size);
2098  }
2099  return;
2100  }
2101 
2102  if (type == CNOT_OPERATION) {
2103  std::vector<int> tqbits = {target_qbit};
2104  std::vector<int> cqbits = {control_qbit};
2105  if (qbit_num < 10) {
2106  apply_X_kernel_from_right(input, tqbits, cqbits, matrix_size);
2107  } else {
2108  apply_X_kernel_from_right_tbb(input, tqbits, cqbits, matrix_size);
2109  }
2110  return;
2111  }
2112 
2113  if (type == CZ_OPERATION) {
2114  if (qbit_num < 10) {
2116  } else {
2118  }
2119  return;
2120  }
2121 
2122  if (type == CH_OPERATION) {
2123  if (qbit_num < 10) {
2125  } else {
2127  }
2128  return;
2129  }
2130 
2131  if (type == SYC_OPERATION) {
2133  return;
2134  }
2135 
2136  if (type == CROT_OPERATION && alt_kernel != nullptr) {
2137  Matrix branch0 = alt_kernel->copy();
2138  Matrix branch1 = u3_1qbit.copy();
2139 #ifdef USE_AVX
2140  if (qbit_num < 10) {
2142  } else {
2144  }
2145 #else
2147 #endif
2148  return;
2149  }
2150 
2151  if (type == GENERAL_OPERATION) {
2152  if (matrix_alloc_float_valid && matrix_alloc.rows == 0 && matrix_alloc_float.rows > 0) {
2153  matrix_alloc = matrix_alloc_float.to_float64();
2154  }
2155 
2156  const std::vector<int> involved_qbits = get_involved_qubits();
2157  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
2158  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
2159  const bool has_full_matrix = matrix_alloc.rows == matrix_size && matrix_alloc.cols == matrix_size;
2160  const bool has_local_matrix = involved_qbit_num > 0
2161  && matrix_alloc.rows == Power_of_2(involved_qbit_num)
2162  && matrix_alloc.cols == Power_of_2(involved_qbit_num);
2163  const bool can_use_large_kernel_from_right = !has_any_control
2164  && input.cols != 1
2165  && involved_qbit_num >= 2
2166  && involved_qbit_num <= 5;
2167 
2168  if (has_full_matrix) {
2169  if (can_use_large_kernel_from_right) {
2170 #ifdef USE_AVX
2171  if (involved_qbit_num == 2 && qbit_num < 10) {
2172  apply_large_kernel_from_right_AVX(matrix_alloc, input, involved_qbits, matrix_size);
2173  }
2174  else if (involved_qbit_num == 2) {
2175  apply_large_kernel_from_right_AVX_TBB(matrix_alloc, input, involved_qbits, matrix_size);
2176  }
2177  else {
2178  apply_large_kernel_from_right(matrix_alloc, input, involved_qbits, matrix_size);
2179  }
2180 #else
2181  apply_large_kernel_from_right(matrix_alloc, input, involved_qbits, matrix_size);
2182 #endif
2183  }
2184  else {
2185  Matrix transformed = dot(input, matrix_alloc);
2186  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex16));
2187  }
2188  return;
2189  }
2190 
2191  if (has_local_matrix && matrix_alloc.rows == 2 && matrix_alloc.cols == 2 && involved_qbit_num == 1) {
2192  u3_1qbit = matrix_alloc;
2193  }
2194  else if (has_local_matrix && can_use_large_kernel_from_right) {
2195 #ifdef USE_AVX
2196  if (involved_qbit_num == 2 && qbit_num < 10) {
2197  apply_large_kernel_from_right_AVX(matrix_alloc, input, involved_qbits, matrix_size);
2198  }
2199  else if (involved_qbit_num == 2) {
2200  apply_large_kernel_from_right_AVX_TBB(matrix_alloc, input, involved_qbits, matrix_size);
2201  }
2202  else {
2203  apply_large_kernel_from_right(matrix_alloc, input, involved_qbits, matrix_size);
2204  }
2205 #else
2206  apply_large_kernel_from_right(matrix_alloc, input, involved_qbits, matrix_size);
2207 #endif
2208  return;
2209  }
2210  else {
2211  std::string err("Gate::apply_kernel_from_right(Matrix&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
2212  throw err;
2213  }
2214  }
2215 
2216  if (u3_1qbit.rows == 0 || u3_1qbit.cols == 0) {
2217  Matrix gate_matrix = create_identity(matrix_size);
2218  apply_to(gate_matrix, 0);
2219  Matrix ret = dot(input, gate_matrix);
2220  memcpy(input.get_data(), ret.get_data(), ret.size() * sizeof(QGD_Complex16));
2221  return;
2222  }
2223 
2224  if (type == CROT_OPERATION || u3_1qbit.rows != 2 || u3_1qbit.cols != 2) {
2225  const std::vector<int> involved_qbits = get_involved_qubits();
2226  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
2227  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
2228  const bool can_use_large_kernel_from_right = !has_any_control
2229  && input.cols != 1
2230  && involved_qbit_num >= 2
2231  && involved_qbit_num <= 5;
2232 
2233  if (u3_1qbit.rows != 2 || u3_1qbit.cols != 2) {
2234  if (can_use_large_kernel_from_right) {
2235 #ifdef USE_AVX
2236  if (involved_qbit_num == 2 && qbit_num < 10) {
2237  apply_large_kernel_from_right_AVX(u3_1qbit, input, involved_qbits, matrix_size);
2238  }
2239  else if (involved_qbit_num == 2) {
2240  apply_large_kernel_from_right_AVX_TBB(u3_1qbit, input, involved_qbits, matrix_size);
2241  }
2242  else {
2243  apply_large_kernel_from_right(u3_1qbit, input, involved_qbits, matrix_size);
2244  }
2245 #else
2246  apply_large_kernel_from_right(u3_1qbit, input, involved_qbits, matrix_size);
2247 #endif
2248  return;
2249  }
2250 
2251  if (u3_1qbit.rows == matrix_size && u3_1qbit.cols == matrix_size) {
2252  Matrix transformed = dot(input, u3_1qbit);
2253  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex16));
2254  return;
2255  }
2256  }
2257 
2258  Matrix gate_matrix = create_identity(matrix_size);
2259  Matrix kernel_copy = u3_1qbit.copy();
2260  if (alt_kernel != nullptr) {
2261  Matrix alt_copy = alt_kernel->copy();
2262  apply_kernel_to(kernel_copy, gate_matrix, false, 0, &alt_copy);
2263  } else {
2264  apply_kernel_to(kernel_copy, gate_matrix, false, 0);
2265  }
2266  Matrix ret = dot(input, gate_matrix);
2267  memcpy(input.get_data(), ret.get_data(), ret.size() * sizeof(QGD_Complex16));
2268  return;
2269  }
2270 
2271 
2272 #ifdef USE_AVX
2273  if (qbit_num < 4) {
2275  } else if (qbit_num < 10) {
2277  } else {
2279  }
2280 #else
2282 #endif
2283 
2284 
2285 }
2286 
2287 
2288 void
2289 Gate::apply_kernel_from_right( Matrix_float& u3_1qbit, Matrix_float& input, const Matrix_float* alt_kernel ) {
2290 
2291  if (type == SWAP_OPERATION || type == CSWAP_OPERATION) {
2292  if (qbit_num < 10) {
2293  apply_SWAP_kernel_from_right(input, target_qbits, control_qbits, matrix_size);
2294  } else {
2295  apply_SWAP_kernel_from_right_tbb(input, target_qbits, control_qbits, matrix_size);
2296  }
2297  return;
2298  }
2299 
2300  if (type == CCX_OPERATION) {
2301  if (qbit_num < 10) {
2302  apply_X_kernel_from_right(input, target_qbits, control_qbits, matrix_size);
2303  } else {
2304  apply_X_kernel_from_right_tbb(input, target_qbits, control_qbits, matrix_size);
2305  }
2306  return;
2307  }
2308 
2309  if (type == CNOT_OPERATION) {
2310  std::vector<int> tqbits = {target_qbit};
2311  std::vector<int> cqbits = {control_qbit};
2312  if (qbit_num < 10) {
2313  apply_X_kernel_from_right(input, tqbits, cqbits, matrix_size);
2314  } else {
2315  apply_X_kernel_from_right_tbb(input, tqbits, cqbits, matrix_size);
2316  }
2317  return;
2318  }
2319 
2320  if (type == CZ_OPERATION) {
2321  if (qbit_num < 10) {
2323  } else {
2325  }
2326  return;
2327  }
2328 
2329  if (type == CH_OPERATION) {
2330  if (qbit_num < 10) {
2332  } else {
2334  }
2335  return;
2336  }
2337 
2338  if (type == SYC_OPERATION) {
2340  return;
2341  }
2342 
2343  if (type == CROT_OPERATION && alt_kernel != nullptr) {
2344  Matrix_float branch0 = alt_kernel->copy();
2345  Matrix_float branch1 = u3_1qbit.copy();
2346 #ifdef USE_AVX
2347  if (qbit_num < 10) {
2349  } else {
2351  }
2352 #else
2354 #endif
2355  return;
2356  }
2357 
2358  if (type == GENERAL_OPERATION) {
2359  const std::vector<int> involved_qbits = get_involved_qubits();
2360  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
2361  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
2362  Matrix_float matrix_alloc32;
2363  if (matrix_alloc_float_valid) {
2364  matrix_alloc32 = matrix_alloc_float;
2365  }
2366  else {
2367  matrix_alloc32 = matrix_alloc.to_float32();
2368  }
2369  const bool has_full_matrix = matrix_alloc32.rows == matrix_size && matrix_alloc32.cols == matrix_size;
2370  const bool has_local_matrix = involved_qbit_num > 0
2371  && matrix_alloc32.rows == Power_of_2(involved_qbit_num)
2372  && matrix_alloc32.cols == Power_of_2(involved_qbit_num);
2373  const bool can_use_large_kernel_from_right = !has_any_control
2374  && input.cols != 1
2375  && involved_qbit_num >= 2
2376  && involved_qbit_num <= 5;
2377 
2378  if (has_full_matrix) {
2379  if (can_use_large_kernel_from_right) {
2380 #ifdef USE_AVX
2381  if (involved_qbit_num == 2 && qbit_num < 10) {
2382  apply_large_kernel_from_right_AVX32(matrix_alloc32, input, involved_qbits, matrix_size);
2383  }
2384  else if (involved_qbit_num == 2) {
2385  apply_large_kernel_from_right_AVX_TBB32(matrix_alloc32, input, involved_qbits, matrix_size);
2386  }
2387  else {
2388  apply_large_kernel_from_right(matrix_alloc32, input, involved_qbits, matrix_size);
2389  }
2390 #else
2391  apply_large_kernel_from_right(matrix_alloc32, input, involved_qbits, matrix_size);
2392 #endif
2393  }
2394  else {
2395  Matrix_float transformed = dot(input, matrix_alloc32);
2396  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex8));
2397  }
2398  return;
2399  }
2400 
2401  if (has_local_matrix && matrix_alloc32.rows == 2 && matrix_alloc32.cols == 2 && involved_qbit_num == 1) {
2402  u3_1qbit = matrix_alloc32;
2403  }
2404  else if (has_local_matrix && can_use_large_kernel_from_right) {
2405 #ifdef USE_AVX
2406  if (involved_qbit_num == 2 && qbit_num < 10) {
2407  apply_large_kernel_from_right_AVX32(matrix_alloc32, input, involved_qbits, matrix_size);
2408  }
2409  else if (involved_qbit_num == 2) {
2410  apply_large_kernel_from_right_AVX_TBB32(matrix_alloc32, input, involved_qbits, matrix_size);
2411  }
2412  else {
2413  apply_large_kernel_from_right(matrix_alloc32, input, involved_qbits, matrix_size);
2414  }
2415 #else
2416  apply_large_kernel_from_right(matrix_alloc32, input, involved_qbits, matrix_size);
2417 #endif
2418  return;
2419  }
2420  else {
2421  std::string err("Gate::apply_kernel_from_right(Matrix_float&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
2422  throw err;
2423  }
2424  }
2425 
2426  if (u3_1qbit.rows == 0 || u3_1qbit.cols == 0) {
2428  apply_to(gate_matrix, 0);
2429  Matrix_float ret = dot(input, gate_matrix);
2430  memcpy(input.get_data(), ret.get_data(), ret.size() * sizeof(QGD_Complex8));
2431  return;
2432  }
2433 
2434  if (type == CROT_OPERATION || u3_1qbit.rows != 2 || u3_1qbit.cols != 2) {
2435  const std::vector<int> involved_qbits = get_involved_qubits();
2436  const int involved_qbit_num = static_cast<int>(involved_qbits.size());
2437  const bool has_any_control = (control_qbit >= 0) || !control_qbits.empty();
2438  const bool can_use_large_kernel_from_right = !has_any_control
2439  && input.cols != 1
2440  && involved_qbit_num >= 2
2441  && involved_qbit_num <= 5;
2442 
2443  if (u3_1qbit.rows != 2 || u3_1qbit.cols != 2) {
2444  if (can_use_large_kernel_from_right) {
2445 #ifdef USE_AVX
2446  if (involved_qbit_num == 2 && qbit_num < 10) {
2447  apply_large_kernel_from_right_AVX32(u3_1qbit, input, involved_qbits, matrix_size);
2448  }
2449  else if (involved_qbit_num == 2) {
2450  apply_large_kernel_from_right_AVX_TBB32(u3_1qbit, input, involved_qbits, matrix_size);
2451  }
2452  else {
2453  apply_large_kernel_from_right(u3_1qbit, input, involved_qbits, matrix_size);
2454  }
2455 #else
2456  apply_large_kernel_from_right(u3_1qbit, input, involved_qbits, matrix_size);
2457 #endif
2458  return;
2459  }
2460 
2461  if (u3_1qbit.rows == matrix_size && u3_1qbit.cols == matrix_size) {
2462  Matrix_float transformed = dot(input, u3_1qbit);
2463  memcpy(input.get_data(), transformed.get_data(), transformed.size() * sizeof(QGD_Complex8));
2464  return;
2465  }
2466  }
2467 
2469  Matrix_float kernel_copy = u3_1qbit.copy();
2470  if (alt_kernel != nullptr) {
2471  Matrix_float alt_copy = alt_kernel->copy();
2472  apply_kernel_to(kernel_copy, gate_matrix, false, 0, &alt_copy);
2473  } else {
2474  apply_kernel_to(kernel_copy, gate_matrix, false, 0);
2475  }
2476  Matrix_float ret = dot(input, gate_matrix);
2477  memcpy(input.get_data(), ret.get_data(), ret.size() * sizeof(QGD_Complex8));
2478  return;
2479  }
2480 
2481 #ifdef USE_AVX
2482  if (qbit_num < 4) {
2484  } else if (qbit_num < 10) {
2486  } else {
2488  }
2489 #else
2491 #endif
2492 
2493 }
2494 
2499 void
2501 
2502  parameter_start_idx = start_idx;
2503 
2504 }
2505 
2510 void
2511 Gate::set_parents( std::vector<Gate*>& parents_ ) {
2512 
2513  parents = parents_;
2514 
2515 }
2516 
2517 
2522 void
2523 Gate::set_children( std::vector<Gate*>& children_ ) {
2524 
2525  children = children_;
2526 
2527 }
2528 
2529 
2534 int
2536 
2537  return parameter_start_idx;
2538 
2539 }
2540 
2541 
2545 Matrix
2546 Gate::gate_kernel(const Matrix_real& /*precomputed_sincos*/) {
2547  std::string err(name + "::gate_kernel(double) not implemented");
2548  throw err;
2549  return Matrix(0, 0);
2550 }
2551 
2553 Gate::gate_kernel(const Matrix_real_float& /*precomputed_sincos*/) {
2554  std::string err(name + "::gate_kernel(float) not implemented");
2555  throw err;
2556  return Matrix_float(0, 0);
2557 }
2558 
2559 Matrix
2560 Gate::inverse_gate_kernel(const Matrix_real& precomputed_sincos) {
2561  Matrix fwd = gate_kernel(precomputed_sincos);
2562  Matrix inv(fwd.rows, fwd.cols);
2563  for (int row_idx = 0; row_idx < fwd.rows; ++row_idx) {
2564  const int row_offset = row_idx * fwd.stride;
2565  for (int col_idx = 0; col_idx < fwd.cols; ++col_idx) {
2566  const QGD_Complex16& src = fwd[row_offset + col_idx];
2567  QGD_Complex16& dst = inv[col_idx * inv.stride + row_idx];
2568  dst.real = src.real;
2569  dst.imag = -src.imag;
2570  }
2571  }
2572  return inv;
2573 }
2574 
2576 Gate::inverse_gate_kernel(const Matrix_real_float& precomputed_sincos) {
2577  Matrix_float fwd = gate_kernel(precomputed_sincos);
2578  Matrix_float inv(fwd.rows, fwd.cols);
2579  for (int row_idx = 0; row_idx < fwd.rows; ++row_idx) {
2580  const int row_offset = row_idx * fwd.stride;
2581  for (int col_idx = 0; col_idx < fwd.cols; ++col_idx) {
2582  const QGD_Complex8& src = fwd[row_offset + col_idx];
2583  QGD_Complex8& dst = inv[col_idx * inv.stride + row_idx];
2584  dst.real = src.real;
2585  dst.imag = -src.imag;
2586  }
2587  }
2588  return inv;
2589 }
2590 
2591 
2592 void
2593 Gate::gate_kernel_to(const Matrix_real& precomputed_sincos, Matrix& output) {
2594  output = gate_kernel(precomputed_sincos);
2595 }
2596 
2597 
2598 void
2600  output = gate_kernel(precomputed_sincos);
2601 }
2602 
2603 
2604 void
2606  output = inverse_gate_kernel(precomputed_sincos);
2607 }
2608 
2609 
2610 void
2612  output = inverse_gate_kernel(precomputed_sincos);
2613 }
2614 
2615 
2616 void
2617 Gate::derivative_kernel_to(const Matrix_real& precomputed_sincos, int param_idx, Matrix& output) {
2618  output = derivative_kernel(precomputed_sincos, param_idx);
2619 }
2620 
2621 
2622 void
2623 Gate::derivative_kernel_to(const Matrix_real_float& precomputed_sincos, int param_idx, Matrix_float& output) {
2624  output = derivative_kernel(precomputed_sincos, param_idx);
2625 }
2626 
2627 
2628 void
2629 Gate::derivative_aux_kernel_to(const Matrix_real& precomputed_sincos, int param_idx, Matrix& output) {
2630  output = derivative_aux_kernel(precomputed_sincos, param_idx);
2631 }
2632 
2633 
2634 void
2635 Gate::derivative_aux_kernel_to(const Matrix_real_float& precomputed_sincos, int param_idx, Matrix_float& output) {
2636  output = derivative_aux_kernel(precomputed_sincos, param_idx);
2637 }
2638 
2643 std::vector<double>
2645  return {};
2646 }
2647 
2648 
2649 Matrix
2650 Gate::calc_one_qubit_u3(double ThetaOver2, double Phi, double Lambda) {
2651  double sin_theta, cos_theta;
2652  double sin_phi, cos_phi;
2653  double sin_lambda, cos_lambda;
2654  qgd_sincos<double>(ThetaOver2, &sin_theta, &cos_theta);
2655  qgd_sincos<double>(Phi, &sin_phi, &cos_phi);
2656  qgd_sincos<double>(Lambda, &sin_lambda, &cos_lambda);
2657  return calc_one_qubit_u3_from_trig<Matrix, double>(sin_theta, cos_theta, sin_phi, cos_phi, sin_lambda, cos_lambda);
2658 }
2659 
2660 
2662 Gate::calc_one_qubit_u3(float ThetaOver2, float Phi, float Lambda) {
2663  float sin_theta, cos_theta;
2664  float sin_phi, cos_phi;
2665  float sin_lambda, cos_lambda;
2666  qgd_sincos<float>(ThetaOver2, &sin_theta, &cos_theta);
2667  qgd_sincos<float>(Phi, &sin_phi, &cos_phi);
2668  qgd_sincos<float>(Lambda, &sin_lambda, &cos_lambda);
2669  return calc_one_qubit_u3_from_trig<Matrix_float, float>(sin_theta, cos_theta, sin_phi, cos_phi, sin_lambda, cos_lambda);
2670 }
2671 
2672 
2678 Matrix_real
2680 
2681  const std::vector<double> mults = get_parameter_multipliers();
2682 
2683  if (mults.empty()) {
2684  return Matrix_real(0, 0);
2685  }
2686 
2687  const int n = static_cast<int>(mults.size());
2688  if ( get_parameter_start_idx() + n > (int)parameters.size() ) {
2689  std::string err(name + "::extract_parameters: Can't extract parameters, input array has not enough elements.");
2690  throw err;
2691  }
2692 
2693  Matrix_real extracted_parameters(1, n);
2694  const int start = get_parameter_start_idx();
2695  for (int i = 0; i < n; ++i) {
2696  const double m = mults[i];
2697  extracted_parameters[i] = std::fmod(m * parameters[start + i], m * 2.0 * M_PI);
2698  }
2699 
2700  return extracted_parameters;
2701 
2702 }
2703 
2704 
2711 
2712  const std::vector<double> mults = get_parameter_multipliers();
2713 
2714  if (mults.empty()) {
2715  return Matrix_real_float(0, 0);
2716  }
2717 
2718  const int n = static_cast<int>(mults.size());
2719  if ( get_parameter_start_idx() + n > (int)parameters.size() ) {
2720  std::string err(name + "::extract_parameters: Can't extract parameters, input array has not enough elements.");
2721  throw err;
2722  }
2723 
2724  Matrix_real_float extracted_parameters(1, n);
2725  const int start = get_parameter_start_idx();
2726  for (int i = 0; i < n; ++i) {
2727  const float m = static_cast<float>(mults[i]);
2728  extracted_parameters[i] = std::fmod(m * parameters[start + i], m * static_cast<float>(2.0 * M_PI));
2729  }
2730 
2731  return extracted_parameters;
2732 
2733 }
2734 
2739 std::string
2741 
2742  return name;
2743 
2744 }
void qgd_sincos< float >(float x, float *s, float *c)
Definition: qgd_math.h:106
bool is_float32() const
Definition: matrix_any.h:41
Matrix_real compute_precomputed_sincos(const Matrix_real &parameters) const
Public wrapper to precompute gate-local sin/cos pairs.
Definition: Gate.cpp:1017
Matrix dot(Matrix &A, Matrix &B)
Call to calculate the product of two complex matrices by calling method zgemm3m from the CBLAS librar...
Definition: dot.cpp:38
Gate()
Default constructor of the class.
Definition: Gate.cpp:121
void apply_kernel_to_input_AVX_parallel32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_large_kernel_from_right_AVX32(Matrix_float &unitary, Matrix_float &input, std::vector< int > involved_qbits, const int &matrix_size)
void apply_H_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Applies the H (Hadamard) gate kernel to the input matrix.
void apply_X_kernel_to_input(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
Applies the X gate kernel to the input matrix.
virtual Matrix inverse_gate_kernel(const Matrix_real &precomputed_sincos)
Definition: Gate.cpp:2560
parameter_num
[set adaptive gate structure]
Header file for a class for the representation of general gate operations.
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
void apply_large_kernel_from_right_AVX_TBB32(Matrix_float &unitary, Matrix_float &input, std::vector< int > involved_qbits, const int &matrix_size)
Class to store single-precision real arrays and properties.
void qgd_sincos< double >(double x, double *s, double *c)
Definition: qgd_math.h:118
virtual void gate_kernel_to(const Matrix_real &precomputed_sincos, Matrix &output)
Definition: Gate.cpp:2593
void apply_large_kernel_to_input_AVX_TBB(Matrix &unitary, Matrix &input, std::vector< int > involved_qbits, const int &matrix_size)
Apply multi-qubit gate kernel to an input matrix using AVX optimization and TBB parallelization.
virtual Matrix derivative_aux_kernel(const Matrix_real &precomputed_sincos, int param_idx)
Definition: Gate.cpp:1415
void add_child(Gate *child)
Call to add a child gate to the current gate.
Definition: Gate.cpp:1264
virtual Matrix get_matrix()
Retrieve the gate matrix for zero-parameter gates (no parameters).
Definition: Gate.cpp:283
void clear_children()
Call to erase data on children.
Definition: Gate.cpp:1279
void apply_large_kernel_from_right_AVX_TBB(Matrix &unitary, Matrix &input, std::vector< int > involved_qbits, const int &matrix_size)
void apply_kernel_to_input_AVX_small32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_kernel_from_right_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel: apply 2x2 gate from the right (input = input * U), row-major f32.
void apply_X_kernel_to_input_tbb(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_crot_kernel_to_matrix_input_from_right_AVX32(Matrix_float &u3_1qbit1, Matrix_float &u3_qbit2, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_kernel_from_right(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Apply a 2x2 kernel from the right: input = input * U.
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
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
void apply_kernel_from_right(Matrix &u3_1qbit, Matrix &input, const Matrix *alt_kernel=nullptr)
Call to apply the gate kernel on the input state or unitary from right (no AVX support) ...
Definition: Gate.cpp:2082
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 apply_H_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_crot_kernel_to_matrix_input_AVX_parallel32(Matrix_float &u3_1qbit1, Matrix_float &u3_1qbit2, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
float real
real part
Definition: QGDTypes.h:47
void apply_SYC_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Structure type representing single-precision complex numbers.
Definition: QGDTypes.h:46
void apply_kernel_to_input_AVX_small(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel to apply single qubit gate kernel on an input matrix (efficient for small inputs) ...
bool is_float32() const
void apply_kernel_to_input_AVX(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel to apply single qubit gate kernel on an input matrix (single threaded) ...
Matrix & as_float64()
Definition: matrix_any.h:48
void apply_kernel_to_state_vector_input_parallel_OpenMP_AVX(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel on a state vector (parallelized with OpenMP)
void apply_kernel_to_state_vector_input_AVX(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel on a state vector.
void apply_large_kernel_to_input(Matrix &unitary, Matrix &input, std::vector< int > involved_qbits, const int &matrix_size)
void apply_X_kernel_from_right(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
virtual ~Gate()
Destructor of the class.
Definition: Gate.cpp:237
void apply_X_kernel_to_input_omp(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
virtual std::vector< Matrix > apply_to_combined_inner(Matrix_real &parameters_mtx_in, const Matrix_real &precomputed_sincos, Matrix &input, int parallel)
Internal combined forward+derivative entry with precomputed sin/cos.
Definition: Gate.cpp:832
void apply_crot_kernel_to_matrix_input(Matrix &u3_1qbit1, Matrix &u3_1qbit2, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_kernel_to_state_vector_input_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
bool is_float64() const
void apply_kernel_to_state_vector_input_parallel_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
float imag
imaginary part
Definition: QGDTypes.h:48
void apply_kernel_to_state_vector_input_parallel_AVX(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel on a state vector (parallelized with Intel TBB)
void apply_crot_kernel_to_matrix_input_AVX(Matrix &u3_1qbit1, Matrix &u3_1qbit2, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Call to apply crot gate kernel on an input matrix using AVX.
void apply_SYC_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
bool is_float64() const
Definition: matrix_any.h:40
scalar * get_data() const
Call to get the pointer to the stored data.
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 apply_SWAP_kernel_to_input(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
Applies the SWAP gate kernel to the input matrix.
void apply_kernel_from_right_AVX(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel: apply 2x2 gate from the right (input = input * U), row-major f64.
Non-owning carrier that can reference either Matrix_real or Matrix_real_float.
void apply_Z_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Applies the Z gate kernel to the input matrix.
void apply_kernel_to_input_AVX_parallel(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel to apply single qubit gate kernel on an input matrix.
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
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
void apply_SYC_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Z_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_crot_kernel_to_matrix_input_from_right(Matrix &u3_1qbit1, Matrix &u3_1qbit2, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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 apply_SYC_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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
int cols
The number of columns.
Definition: matrix_base.hpp:44
matrix_size
[load Umtx]
Definition: example.py:58
void apply_kernel_to_input(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Call to apply kernel to apply single qubit gate kernel on an input matrix.
void apply_X_kernel_from_right_tbb(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
#define M_PI
Definition: qgd_math.h:42
virtual void derivative_kernel_to(const Matrix_real &precomputed_sincos, int param_idx, Matrix &output)
Definition: Gate.cpp:2617
void apply_Z_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
gate_type get_type()
Call to get the type of the operation.
Definition: Gate.cpp:1333
void apply_crot_kernel_to_matrix_input_AVX32(Matrix_float &u3_1qbit1, Matrix_float &u3_qbit2, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Matrix_float & as_float32()
Definition: matrix_any.h:55
Matrix_real & as_float64()
virtual void inverse_gate_kernel_to(const Matrix_real &precomputed_sincos, Matrix &output)
Definition: Gate.cpp:2605
void set_control_qbit(int control_qbit_in)
Call to set the control qubit for the gate operation.
Definition: Gate.cpp:1054
void apply_kernel_from_right_AVX_parallel(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel: apply 2x2 gate from the right (input = input * U), f64.
void apply_crot_kernel_to_matrix_input_from_right_AVX_parallel32(Matrix_float &u3_1qbit1, Matrix_float &u3_1qbit2, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_kernel_from_right_AVX_small(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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
std::vector< Gate * > get_parents()
Call to get the parents of the current gate.
Definition: Gate.cpp:1301
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
void apply_SWAP_kernel_from_right(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void qgd_sincos_batch< double >(const double *input, double *output, std::size_t count, int stride)
Definition: qgd_math.h:138
void set_target_qbit(int target_qbit_in)
Call to set the target qubit for the gate operation.
Definition: Gate.cpp:1076
void apply_Z_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
virtual Matrix_real extract_parameters(Matrix_real &parameters)
Call to extract parameters from the parameter array corresponding to the circuit, in which the gate i...
Definition: Gate.cpp:2679
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 copy_to(Matrix_real &target) const
Copy the matrix to a reusable double-precision target matrix.
void set_matrix(Matrix input)
Call to set the stored matrix in the operation.
Definition: Gate.cpp:1036
std::vector< Gate * > get_children()
Call to get the children of the current gate.
Definition: Gate.cpp:1312
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
void apply_kernel_to_state_vector_input_parallel_OpenMP_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Double-precision complex matrix (float64).
Definition: matrix.h:38
void copy_to(matrix_base< scalar > &target) const
Copy the current matrix storage into a reusable target matrix.
void apply_H_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_large_kernel_to_input_AVX_TBB32(Matrix_float &unitary, Matrix_float &input, std::vector< int > involved_qbits, const int &matrix_size)
int size() const
Call to get the number of the allocated elements.
void apply_large_kernel_to_input_AVX_OpenMP32(Matrix_float &unitary, Matrix_float &input, std::vector< int > involved_qbits, const int &matrix_size)
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
void apply_large_kernel_to_input_AVX32(Matrix_float &unitary, Matrix_float &input, std::vector< int > involved_qbits, const int &matrix_size)
void apply_crot_kernel_to_matrix_input_AVX_parallel(Matrix &u3_1qbit1, Matrix &u3_1qbit2, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Call to apply crot gate kernel on an input matrix using AVX and TBB.
Header file for AVX-optimized implementations for applying multi-qubit gate kernels to quantum state ...
void apply_kernel_from_right_AVX_parallel32(Matrix_float &u3_1qbit, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel: apply 2x2 gate from the right (input = input * U), f32.
virtual Matrix gate_kernel(const Matrix_real &precomputed_sincos)
Compute the gate kernel matrix from precomputed trigonometric values.
Definition: Gate.cpp:2546
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
virtual std::vector< Matrix > apply_to_combined(Matrix_real &parameters_mtx_in, Matrix &input, int parallel)
Combined forward + derivative application with shared precomputed trig cache.
Definition: Gate.cpp:816
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
void apply_kernel_from_right_AVX_small32(Matrix_float &u3_1qbit, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Base class for the representation of general gate operations.
Definition: Gate.h:86
void apply_large_kernel_from_right_AVX(Matrix &unitary, Matrix &input, std::vector< int > involved_qbits, const int &matrix_size)
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
void apply_kernel_to(Matrix &u3_1qbit, Matrix &input, bool deriv=false, int parallel=0, const Matrix *alt_kernel=nullptr)
Call to apply the gate kernel on the input state or unitary with optional AVX support.
Definition: Gate.cpp:1478
void apply_SWAP_kernel_to_input_tbb(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
Matrix create_identity(int matrix_size)
Call to create an identity matrix.
Definition: common.cpp:182
virtual Matrix derivative_kernel(const Matrix_real &precomputed_sincos, int param_idx)
Definition: Gate.cpp:1371
std::vector< Matrix > apply_derivative_to_precomputed(const Matrix_real &precomputed_sincos, 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:636
void apply_SWAP_kernel_to_input_omp(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_crot_kernel_to_matrix_input_from_right_AVX_parallel(Matrix &u3_1qbit1, Matrix &u3_1qbit2, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Header file for commonly used functions and wrappers to CBLAS functions.
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
Matrix_real_float & as_float32()
void apply_large_kernel_to_input_AVX_OpenMP(Matrix &unitary, Matrix &input, std::vector< int > involved_qbits, const int &matrix_size)
Apply multi-qubit gate kernel to an input matrix using AVX optimization and OpenMP parallelization...
void set_parents(std::vector< Gate *> &parents_)
Call to set the parents of the current gate.
Definition: Gate.cpp:2511
void apply_Z_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
double real
the real part of a complex number
Definition: QGDTypes.h:40
virtual void derivative_aux_kernel_to(const Matrix_real &precomputed_sincos, int param_idx, Matrix &output)
Definition: Gate.cpp:2629
void apply_kernel_to_state_vector_input(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Call to apply a gate kernel on a state vector.
Matrix_real precompute_sincos(const Matrix_real &parameters) const
Precompute sin/cos pairs for each gate-local parameter.
Definition: Gate.cpp:1431
int get_qbit_num()
Call to get the number of qubits composing the unitary.
Definition: Gate.cpp:1342
gate_type
Type definition of operation types (also generalized for decomposition classes derived from the class...
Definition: Gate.h:39
void apply_large_kernel_from_right(Matrix &unitary, Matrix &input, std::vector< int > involved_qbits, const int &matrix_size)
virtual void apply_to(Matrix &input, int parallel)
Call to apply the gate on the input array/matrix.
Definition: Gate.cpp:433
void apply_kernel_to_state_vector_input_parallel(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Call to apply a gate kernel on a state vector.
virtual std::vector< double > get_parameter_multipliers() const
Returns the per-parameter multipliers relative to 2π used by extract_parameters. ...
Definition: Gate.cpp:2644
void apply_SWAP_kernel_from_right_tbb(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
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
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 apply_kernel_to_input_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Matrix_float copy() const
Call to create a copy of the matrix.
Definition: matrix_float.h:60
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
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
Matrix_float create_identity_float(int matrix_size)
Call to create a single-precision complex identity matrix.
Definition: common.cpp:203
void apply_crot_kernel_to_matrix_input_from_right_AVX(Matrix &u3_1qbit1, Matrix &u3_1qbit2, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)