Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
apply_dedicated_gate_kernel_to_input.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 */
25 //#include <immintrin.h>
26 #include "tbb/tbb.h"
27 #include <omp.h>
28 #include <type_traits>
29 #include <utility>
30 
31 template<typename MatrixT>
32 using KernelDedicatedComplexT = typename std::remove_reference<decltype(std::declval<MatrixT&>()[0])>::type;
33 
34 
35 
36 
37 
45 template<typename MatrixT>
46 void apply_X_kernel_to_input_impl(MatrixT& input, const std::vector<int>& target_qbits,
47  const std::vector<int>& control_qbits,
48  const int& matrix_size) {
49 
50  // Validate target qubits - X gate requires exactly 1 target qubit
51  if (target_qbits.size() != 1) {
52  throw std::runtime_error("X gate kernel requires exactly 1 target qubit, got " +
53  std::to_string(target_qbits.size()));
54  }
55 
56  int target_qbit = target_qbits[0];
57  int index_step_target = 1 << target_qbit;
58  int current_idx = 0;
59 
60  for (int current_idx_pair = current_idx + index_step_target;
61  current_idx_pair < matrix_size;
62  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
63 
64  for(int idx = 0; idx < index_step_target; idx++) {
65 
66  int current_idx_loc = current_idx + idx;
67  int current_idx_pair_loc = current_idx_pair + idx;
68 
69  // Check all control qubits are active
70  bool all_controls_active = true;
71  for (int control_qbit : control_qbits) {
72  if (!((current_idx_loc >> control_qbit) & 1)) {
73  all_controls_active = false;
74  break;
75  }
76  }
77 
78  // Apply X gate only when ALL controls are active
79  if (all_controls_active) {
80 
81  long long row_offset = (long long)current_idx_loc * input.stride;
82  long long row_offset_pair = (long long)current_idx_pair_loc * input.stride;
83 
84  std::swap_ranges(
85  input.get_data() + row_offset,
86  input.get_data() + row_offset + input.cols,
87  input.get_data() + row_offset_pair
88  );
89  }
90  }
91  current_idx = current_idx + (index_step_target << 1);
92  }
93 }
94 
95 template<typename MatrixT>
96 void apply_X_kernel_from_right_impl(MatrixT& input, const std::vector<int>& target_qbits,
97  const std::vector<int>& control_qbits,
98  const int& matrix_size) {
99 
100  if (target_qbits.size() != 1) {
101  throw std::runtime_error("X gate kernel requires exactly 1 target qubit, got " +
102  std::to_string(target_qbits.size()));
103  }
104 
105  int target_qbit = target_qbits[0];
106  int index_step_target = 1 << target_qbit;
107 
108  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
109  long long row_offset = (long long)row_idx * input.stride;
110  int current_idx = 0;
111 
112  for (int current_idx_pair = current_idx + index_step_target;
113  current_idx_pair < matrix_size;
114  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
115 
116  for (int idx = 0; idx < index_step_target; ++idx) {
117  int current_idx_loc = current_idx + idx;
118  int current_idx_pair_loc = current_idx_pair + idx;
119 
120  bool all_controls_active = true;
121  for (int control_qbit : control_qbits) {
122  if (!((current_idx_loc >> control_qbit) & 1)) {
123  all_controls_active = false;
124  break;
125  }
126  }
127 
128  if (all_controls_active) {
129  std::swap(input[row_offset + current_idx_loc], input[row_offset + current_idx_pair_loc]);
130  }
131  }
132 
133  current_idx = current_idx + (index_step_target << 1);
134  }
135  }
136 }
137 
138 template<typename MatrixT>
139 void apply_Y_kernel_to_input_impl(MatrixT& input, const int& target_qbit,
140  const int& control_qbit,
141  const int& matrix_size) {
142 
143  int index_step_target = 1 << target_qbit;
144  int current_idx = 0;
145 
146  for (int current_idx_pair = current_idx + index_step_target;
147  current_idx_pair < matrix_size;
148  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
149 
150  for(int idx = 0; idx < index_step_target; idx++) {
151 
152  int current_idx_loc = current_idx + idx;
153  int current_idx_pair_loc = current_idx_pair + idx;
154 
155  // Apply Y gate only when BOTH controls are active
156  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
157 
158  int row_offset = current_idx_loc * input.stride;
159  int row_offset_pair = current_idx_pair_loc * input.stride;
160 
161  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
162  int index = row_offset + col_idx;
163  int index_pair = row_offset_pair + col_idx;
164 
165  KernelDedicatedComplexT<MatrixT> element = input[index];
166  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
167 
168  // Y gate transformation
169  input[index].real = element_pair.imag;
170  input[index].imag = -element_pair.real;
171 
172  input[index_pair].real = -element.imag;
173  input[index_pair].imag = element.real;
174  }
175  }
176  }
177  current_idx = current_idx + (index_step_target << 1);
178  }
179 }
180 
181 template<typename MatrixT>
182 void apply_Y_kernel_from_right_impl(MatrixT& input, const int& target_qbit,
183  const int& control_qbit,
184  const int& matrix_size) {
185 
186  int index_step_target = 1 << target_qbit;
187 
188  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
189  long long row_offset = (long long)row_idx * input.stride;
190  int current_idx = 0;
191 
192  for (int current_idx_pair = current_idx + index_step_target;
193  current_idx_pair < matrix_size;
194  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
195 
196  for (int idx = 0; idx < index_step_target; ++idx) {
197  int current_idx_loc = current_idx + idx;
198  int current_idx_pair_loc = current_idx_pair + idx;
199 
200  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
201  int index = row_offset + current_idx_loc;
202  int index_pair = row_offset + current_idx_pair_loc;
203 
204  KernelDedicatedComplexT<MatrixT> element = input[index];
205  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
206 
207  input[index].real = -element_pair.imag;
208  input[index].imag = element_pair.real;
209 
210  input[index_pair].real = element.imag;
211  input[index_pair].imag = -element.real;
212  }
213  }
214 
215  current_idx = current_idx + (index_step_target << 1);
216  }
217  }
218 }
219 
220 template<typename MatrixT>
221 void apply_Z_kernel_to_input_impl(MatrixT& input, const int& target_qbit,
222  const int& control_qbit,
223  const int& matrix_size) {
224 
225  int index_step_target = 1 << target_qbit;
226  int current_idx = 0;
227 
228  for (int current_idx_pair = current_idx + index_step_target;
229  current_idx_pair < matrix_size;
230  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
231 
232  for(int idx = 0; idx < index_step_target; idx++) {
233 
234  int current_idx_loc = current_idx + idx;
235  int current_idx_pair_loc = current_idx_pair + idx;
236 
237  // Apply Z gate only when BOTH controls are active
238  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
239 
240  int row_offset_pair = current_idx_pair_loc * input.stride;
241 
242  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
243  int index_pair = row_offset_pair + col_idx;
244 
245  // Z gate transformation
246  input[index_pair].real = -input[index_pair].real;
247  input[index_pair].imag = -input[index_pair].imag;
248  }
249  }
250  }
251  current_idx = current_idx + (index_step_target << 1);
252  }
253 }
254 
255 template<typename MatrixT>
256 void apply_Z_kernel_from_right_impl(MatrixT& input, const int& target_qbit,
257  const int& control_qbit,
258  const int& matrix_size) {
259 
260  int index_step_target = 1 << target_qbit;
261 
262  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
263  long long row_offset = (long long)row_idx * input.stride;
264  int current_idx = 0;
265 
266  for (int current_idx_pair = current_idx + index_step_target;
267  current_idx_pair < matrix_size;
268  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
269 
270  for (int idx = 0; idx < index_step_target; ++idx) {
271  int current_idx_loc = current_idx + idx;
272  int current_idx_pair_loc = current_idx_pair + idx;
273 
274  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
275  int index_pair = row_offset + current_idx_pair_loc;
276  input[index_pair].real = -input[index_pair].real;
277  input[index_pair].imag = -input[index_pair].imag;
278  }
279  }
280 
281  current_idx = current_idx + (index_step_target << 1);
282  }
283  }
284 }
285 
286 template<typename MatrixT>
287 void apply_H_kernel_to_input_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
288  int index_step_target = 1 << target_qbit;
289  int current_idx = 0;
290  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
291 
292  for (int current_idx_pair = current_idx + index_step_target;
293  current_idx_pair < matrix_size;
294  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
295 
296  for (int idx = 0; idx < index_step_target; idx++) {
297  int current_idx_loc = current_idx + idx;
298  int current_idx_pair_loc = current_idx_pair + idx;
299 
300  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
301  int row_offset = current_idx_loc * input.stride;
302  int row_offset_pair = current_idx_pair_loc * input.stride;
303 
304  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
305  int index = row_offset + col_idx;
306  int index_pair = row_offset_pair + col_idx;
307 
308  KernelDedicatedComplexT<MatrixT> element = input[index];
309  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
310 
311  // H gate transformation
312  input[index].real = inv_sqrt2 * (element.real + element_pair.real);
313  input[index].imag = inv_sqrt2 * (element.imag + element_pair.imag);
314 
315  input[index_pair].real = inv_sqrt2 * (element.real - element_pair.real);
316  input[index_pair].imag = inv_sqrt2 * (element.imag - element_pair.imag);
317  }
318  }
319  }
320  current_idx = current_idx + (index_step_target << 1);
321  }
322 }
323 
324 template<typename MatrixT>
325 void apply_H_kernel_from_right_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
326  int index_step_target = 1 << target_qbit;
327  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
328 
329  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
330  long long row_offset = (long long)row_idx * input.stride;
331  int current_idx = 0;
332 
333  for (int current_idx_pair = current_idx + index_step_target;
334  current_idx_pair < matrix_size;
335  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
336 
337  for (int idx = 0; idx < index_step_target; ++idx) {
338  int current_idx_loc = current_idx + idx;
339  int current_idx_pair_loc = current_idx_pair + idx;
340 
341  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
342  int index = row_offset + current_idx_loc;
343  int index_pair = row_offset + current_idx_pair_loc;
344 
345  KernelDedicatedComplexT<MatrixT> element = input[index];
346  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
347 
348  input[index].real = inv_sqrt2 * (element.real + element_pair.real);
349  input[index].imag = inv_sqrt2 * (element.imag + element_pair.imag);
350 
351  input[index_pair].real = inv_sqrt2 * (element.real - element_pair.real);
352  input[index_pair].imag = inv_sqrt2 * (element.imag - element_pair.imag);
353  }
354  }
355 
356  current_idx = current_idx + (index_step_target << 1);
357  }
358  }
359 }
360 
361 template<typename MatrixT>
362 void apply_S_kernel_to_input_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
363  int index_step_target = 1 << target_qbit;
364  int current_idx = 0;
365 
366  for (int current_idx_pair = current_idx + index_step_target;
367  current_idx_pair < matrix_size;
368  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
369 
370  for (int idx = 0; idx < index_step_target; idx++) {
371  int current_idx_loc = current_idx + idx;
372  int current_idx_pair_loc = current_idx_pair + idx;
373 
374  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
375  int row_offset_pair = current_idx_pair_loc * input.stride;
376 
377  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
378  int index_pair = row_offset_pair + col_idx;
379 
380  // S gate transformation (multiply by i)
381  double real = input[index_pair].real;
382  double imag = input[index_pair].imag;
383  input[index_pair].real = -imag;
384  input[index_pair].imag = real;
385  }
386  }
387  }
388  current_idx = current_idx + (index_step_target << 1);
389  }
390 }
391 
392 template<typename MatrixT>
393 void apply_S_kernel_from_right_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
394  int index_step_target = 1 << target_qbit;
395 
396  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
397  long long row_offset = (long long)row_idx * input.stride;
398  int current_idx = 0;
399 
400  for (int current_idx_pair = current_idx + index_step_target;
401  current_idx_pair < matrix_size;
402  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
403 
404  for (int idx = 0; idx < index_step_target; ++idx) {
405  int current_idx_loc = current_idx + idx;
406  int current_idx_pair_loc = current_idx_pair + idx;
407 
408  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
409  int index_pair = row_offset + current_idx_pair_loc;
410  double real = input[index_pair].real;
411  double imag = input[index_pair].imag;
412  input[index_pair].real = -imag;
413  input[index_pair].imag = real;
414  }
415  }
416 
417  current_idx = current_idx + (index_step_target << 1);
418  }
419  }
420 }
421 
422 template<typename MatrixT>
423 void apply_T_kernel_to_input_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
424  int index_step_target = 1 << target_qbit;
425  int current_idx = 0;
426  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
427 
428  for (int current_idx_pair = current_idx + index_step_target;
429  current_idx_pair < matrix_size;
430  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
431 
432  for (int idx = 0; idx < index_step_target; idx++) {
433  int current_idx_loc = current_idx + idx;
434  int current_idx_pair_loc = current_idx_pair + idx;
435 
436  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
437  int row_offset_pair = current_idx_pair_loc * input.stride;
438 
439  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
440  int index_pair = row_offset_pair + col_idx;
441 
442  // T gate transformation (multiply by exp(i*pi/4) = 1/sqrt(2) + i/sqrt(2))
443  double real = input[index_pair].real;
444  double imag = input[index_pair].imag;
445  input[index_pair].real = inv_sqrt2 * (real - imag);
446  input[index_pair].imag = inv_sqrt2 * (real + imag);
447  }
448  }
449  }
450  current_idx = current_idx + (index_step_target << 1);
451  }
452 }
453 
454 template<typename MatrixT>
455 void apply_T_kernel_from_right_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
456  int index_step_target = 1 << target_qbit;
457  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
458 
459  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
460  long long row_offset = (long long)row_idx * input.stride;
461  int current_idx = 0;
462 
463  for (int current_idx_pair = current_idx + index_step_target;
464  current_idx_pair < matrix_size;
465  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
466 
467  for (int idx = 0; idx < index_step_target; ++idx) {
468  int current_idx_loc = current_idx + idx;
469  int current_idx_pair_loc = current_idx_pair + idx;
470 
471  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
472  int index_pair = row_offset + current_idx_pair_loc;
473  double real = input[index_pair].real;
474  double imag = input[index_pair].imag;
475  input[index_pair].real = inv_sqrt2 * (real - imag);
476  input[index_pair].imag = inv_sqrt2 * (real + imag);
477  }
478  }
479 
480  current_idx = current_idx + (index_step_target << 1);
481  }
482  }
483 }
484 
485 template<typename MatrixT>
486 void apply_SWAP_kernel_to_input_impl(MatrixT& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size){
487 
488  // Validate target qubits - SWAP gate requires exactly 2 target qubits
489  if (target_qbits.size() != 2) {
490  throw std::runtime_error("SWAP gate kernel requires exactly 2 target qubits, got " +
491  std::to_string(target_qbits.size()));
492  }
493 
494  int target_qbit1 = target_qbits[0];
495  int target_qbit2 = target_qbits[1];
496 
497  std::vector<int> non_involved_qbits;
498  int qbit_num = (int)std::log2(matrix_size);
499  for (int idx=0; idx<qbit_num; idx++){
500  bool is_target = (idx == target_qbit1 || idx == target_qbit2);
501  bool is_control = std::find(control_qbits.begin(), control_qbits.end(), idx) != control_qbits.end();
502  if (!is_target && !is_control){
503  non_involved_qbits.push_back(idx);
504  }
505  }
506 
507  // Build control qubit mask
508  int control_mask = 0;
509  for (int control_qbit : control_qbits) {
510  control_mask |= (1 << control_qbit);
511  }
512 
513  for (int block_idx=0; block_idx < matrix_size >> (qbit_num - non_involved_qbits.size()); block_idx++){
514  int base = 0;
515  for (size_t qdx=0; qdx<non_involved_qbits.size();qdx++){
516  if ((block_idx >> qdx) & 1) {
517  base |= (1<<non_involved_qbits[qdx]);
518  }
519  }
520  base |= control_mask;
521  int swap_idx = base|(1<<target_qbit1);
522  int swap_idx_pair = base|(1<<target_qbit2);
523 
524  std::swap_ranges(
525  input.get_data() + swap_idx*input.stride,
526  input.get_data() + swap_idx*input.stride + input.cols,
527  input.get_data() + swap_idx_pair*input.stride
528  );
529  }
530 }
531 
532 
533 template<typename MatrixT>
534 void apply_SWAP_kernel_from_right_impl(MatrixT& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) {
535 
536  if (target_qbits.size() != 2) {
537  throw std::runtime_error("SWAP gate kernel requires exactly 2 target qubits, got " +
538  std::to_string(target_qbits.size()));
539  }
540 
541  int target_qbit1 = target_qbits[0];
542  int target_qbit2 = target_qbits[1];
543 
544  std::vector<int> non_involved_qbits;
545  int qbit_num = (int)std::log2(matrix_size);
546  for (int idx = 0; idx < qbit_num; idx++) {
547  bool is_target = (idx == target_qbit1 || idx == target_qbit2);
548  bool is_control = std::find(control_qbits.begin(), control_qbits.end(), idx) != control_qbits.end();
549  if (!is_target && !is_control) {
550  non_involved_qbits.push_back(idx);
551  }
552  }
553 
554  int control_mask = 0;
555  for (int control_qbit : control_qbits) {
556  control_mask |= (1 << control_qbit);
557  }
558 
559  int total_blocks = matrix_size >> (qbit_num - non_involved_qbits.size());
560 
561  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
562  long long row_offset = (long long)row_idx * input.stride;
563 
564  for (int block_idx = 0; block_idx < total_blocks; ++block_idx) {
565  int base = 0;
566  for (size_t qdx = 0; qdx < non_involved_qbits.size(); qdx++) {
567  if ((block_idx >> qdx) & 1) {
568  base |= (1 << non_involved_qbits[qdx]);
569  }
570  }
571  base |= control_mask;
572  int swap_idx = base | (1 << target_qbit1);
573  int swap_idx_pair = base | (1 << target_qbit2);
574 
575  std::swap(input[row_offset + swap_idx], input[row_offset + swap_idx_pair]);
576  }
577  }
578 }
579 
580 
581 template<typename MatrixT>
582 void apply_SYC_kernel_to_input_impl(MatrixT& input, const int& target_qbit,
583  const int& control_qbit,
584  const int& matrix_size) {
585 
586  int index_step_target = 1 << target_qbit;
587  int index_step_control = 1 << control_qbit;
588 
589  int loop_size = index_step_target < index_step_control ? index_step_target : index_step_control;
590  int iterations = control_qbit < target_qbit ? Power_of_2(target_qbit - control_qbit - 1) : Power_of_2(control_qbit - target_qbit - 1);
591 
592  int idx00 = 0;
593  int idx01 = index_step_target;
594  int idx10 = index_step_control;
595  int idx11 = index_step_target + index_step_control;
596 
597  const double phase_real = std::sqrt(3.0) / 2.0;
598  const double phase_imag = -0.5;
599 
600  while (idx11 < matrix_size) {
601  for (int jdx = 0; jdx < iterations; ++jdx) {
602  for (int idx = 0; idx < loop_size; ++idx) {
603  int idx01_loc = idx01 + idx;
604  int idx10_loc = idx10 + idx;
605  int idx11_loc = idx11 + idx;
606 
607  int offset01 = idx01_loc * input.stride;
608  int offset10 = idx10_loc * input.stride;
609  int offset11 = idx11_loc * input.stride;
610 
611  for (int col_idx = 0; col_idx < input.cols; ++col_idx) {
612  KernelDedicatedComplexT<MatrixT> element01 = input[offset01 + col_idx];
613  KernelDedicatedComplexT<MatrixT> element10 = input[offset10 + col_idx];
614 
615  input[offset01 + col_idx].real = element10.imag;
616  input[offset01 + col_idx].imag = -element10.real;
617 
618  input[offset10 + col_idx].real = element01.imag;
619  input[offset10 + col_idx].imag = -element01.real;
620 
621  KernelDedicatedComplexT<MatrixT> element11 = input[offset11 + col_idx];
622  input[offset11 + col_idx].real = phase_real * element11.real - phase_imag * element11.imag;
623  input[offset11 + col_idx].imag = phase_real * element11.imag + phase_imag * element11.real;
624  }
625  }
626 
627  idx00 += 2 * loop_size;
628  idx01 += 2 * loop_size;
629  idx10 += 2 * loop_size;
630  idx11 += 2 * loop_size;
631  }
632 
633  idx00 += 2 * loop_size * iterations;
634  idx01 += 2 * loop_size * iterations;
635  idx10 += 2 * loop_size * iterations;
636  idx11 += 2 * loop_size * iterations;
637  }
638 }
639 
640 
641 template<typename MatrixT>
642 void apply_SYC_kernel_from_right_impl(MatrixT& input, const int& target_qbit,
643  const int& control_qbit,
644  const int& matrix_size) {
645 
646  int index_step_target = 1 << target_qbit;
647  int index_step_control = 1 << control_qbit;
648 
649  int loop_size = index_step_target < index_step_control ? index_step_target : index_step_control;
650  int iterations = control_qbit < target_qbit ? Power_of_2(target_qbit - control_qbit - 1) : Power_of_2(control_qbit - target_qbit - 1);
651 
652  const double phase_real = std::sqrt(3.0) / 2.0;
653  const double phase_imag = -0.5;
654 
655  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
656  int offset = row_idx * input.stride;
657 
658  int idx00 = 0;
659  int idx01 = index_step_target;
660  int idx10 = index_step_control;
661  int idx11 = index_step_target + index_step_control;
662 
663  while (idx11 < matrix_size) {
664  for (int jdx = 0; jdx < iterations; ++jdx) {
665  for (int idx = 0; idx < loop_size; ++idx) {
666  int idx01_loc = idx01 + idx;
667  int idx10_loc = idx10 + idx;
668  int idx11_loc = idx11 + idx;
669 
670  KernelDedicatedComplexT<MatrixT> element01 = input[offset + idx01_loc];
671  KernelDedicatedComplexT<MatrixT> element10 = input[offset + idx10_loc];
672  input[offset + idx01_loc].real = element10.imag;
673  input[offset + idx01_loc].imag = -element10.real;
674 
675  input[offset + idx10_loc].real = element01.imag;
676  input[offset + idx10_loc].imag = -element01.real;
677 
678  KernelDedicatedComplexT<MatrixT> element11 = input[offset + idx11_loc];
679  input[offset + idx11_loc].real = phase_real * element11.real - phase_imag * element11.imag;
680  input[offset + idx11_loc].imag = phase_real * element11.imag + phase_imag * element11.real;
681  }
682 
683  idx00 += 2 * loop_size;
684  idx01 += 2 * loop_size;
685  idx10 += 2 * loop_size;
686  idx11 += 2 * loop_size;
687  }
688 
689  idx00 += 2 * loop_size * iterations;
690  idx01 += 2 * loop_size * iterations;
691  idx10 += 2 * loop_size * iterations;
692  idx11 += 2 * loop_size * iterations;
693  }
694  }
695 }
696 
697 // TBB Parallelized versions
698 
699 template<typename MatrixT>
700 void apply_X_kernel_to_input_tbb_impl(MatrixT& input, const std::vector<int>& target_qbits,
701  const std::vector<int>& control_qbits,
702  const int& matrix_size) {
703  // Validate target qubits - X gate requires exactly 1 target qubit
704  if (target_qbits.size() != 1) {
705  throw std::runtime_error("X gate kernel requires exactly 1 target qubit, got " +
706  std::to_string(target_qbits.size()));
707  }
708 
709  int target_qbit = target_qbits[0];
710  int index_step_target = 1 << target_qbit;
711  int total_blocks = matrix_size >> (target_qbit + 1);
712 
713  tbb::parallel_for(tbb::blocked_range<int>(0, total_blocks, 1024),
714  [&](const tbb::blocked_range<int>& range) {
715  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
716  int current_idx = block_idx * (index_step_target << 1);
717  int current_idx_pair = current_idx + index_step_target;
718 
719  for(int idx = 0; idx < index_step_target; idx++) {
720  int current_idx_loc = current_idx + idx;
721  int current_idx_pair_loc = current_idx_pair + idx;
722 
723  // Check all control qubits are active
724  bool all_controls_active = true;
725  for (int control_qbit : control_qbits) {
726  if (!((current_idx_loc >> control_qbit) & 1)) {
727  all_controls_active = false;
728  break;
729  }
730  }
731 
732  if (all_controls_active) {
733  long long row_offset = (long long)current_idx_loc * input.stride;
734  long long row_offset_pair = (long long)current_idx_pair_loc * input.stride;
735 
736  std::swap_ranges(
737  input.get_data() + row_offset,
738  input.get_data() + row_offset + input.cols,
739  input.get_data() + row_offset_pair
740  );
741  }
742  }
743  }
744  }
745  );
746 }
747 
748 template<typename MatrixT>
749 void apply_X_kernel_from_right_tbb_impl(MatrixT& input, const std::vector<int>& target_qbits,
750  const std::vector<int>& control_qbits,
751  const int& matrix_size) {
752  if (target_qbits.size() != 1) {
753  throw std::runtime_error("X gate kernel requires exactly 1 target qubit, got " +
754  std::to_string(target_qbits.size()));
755  }
756 
757  int target_qbit = target_qbits[0];
758  int index_step_target = 1 << target_qbit;
759 
760  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
761  [&](const tbb::blocked_range<int>& range) {
762  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
763  long long row_offset = (long long)row_idx * input.stride;
764  int current_idx = 0;
765 
766  for (int current_idx_pair = current_idx + index_step_target;
767  current_idx_pair < matrix_size;
768  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
769 
770  for (int idx = 0; idx < index_step_target; ++idx) {
771  int current_idx_loc = current_idx + idx;
772  int current_idx_pair_loc = current_idx_pair + idx;
773 
774  bool all_controls_active = true;
775  for (int control_qbit : control_qbits) {
776  if (!((current_idx_loc >> control_qbit) & 1)) {
777  all_controls_active = false;
778  break;
779  }
780  }
781 
782  if (all_controls_active) {
783  std::swap(input[row_offset + current_idx_loc], input[row_offset + current_idx_pair_loc]);
784  }
785  }
786 
787  current_idx = current_idx + (index_step_target << 1);
788  }
789  }
790  }
791  );
792 }
793 
794 template<typename MatrixT>
795 void apply_Y_kernel_to_input_tbb_impl(MatrixT& input, const int& target_qbit,
796  const int& control_qbit,
797  const int& matrix_size) {
798  int index_step_target = 1 << target_qbit;
799 
800  tbb::parallel_for(tbb::blocked_range<int>(0, matrix_size >> 1, 1024),
801  [&](const tbb::blocked_range<int>& range) {
802  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
803  int current_idx = block_idx * (index_step_target << 1);
804  int current_idx_pair = current_idx + index_step_target;
805 
806  if (current_idx_pair >= matrix_size) continue;
807 
808  for(int idx = 0; idx < index_step_target; idx++) {
809  int current_idx_loc = current_idx + idx;
810  int current_idx_pair_loc = current_idx_pair + idx;
811 
812  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
813  int row_offset = current_idx_loc * input.stride;
814  int row_offset_pair = current_idx_pair_loc * input.stride;
815 
816  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
817  int index = row_offset + col_idx;
818  int index_pair = row_offset_pair + col_idx;
819 
820  KernelDedicatedComplexT<MatrixT> element = input[index];
821  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
822 
823  input[index].real = element_pair.imag;
824  input[index].imag = -element_pair.real;
825 
826  input[index_pair].real = -element.imag;
827  input[index_pair].imag = element.real;
828  }
829  }
830  }
831  }
832  }
833  );
834 }
835 
836 template<typename MatrixT>
837 void apply_Y_kernel_from_right_tbb_impl(MatrixT& input, const int& target_qbit,
838  const int& control_qbit,
839  const int& matrix_size) {
840  int index_step_target = 1 << target_qbit;
841 
842  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
843  [&](const tbb::blocked_range<int>& range) {
844  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
845  long long row_offset = (long long)row_idx * input.stride;
846  int current_idx = 0;
847  for (int current_idx_pair = current_idx + index_step_target;
848  current_idx_pair < matrix_size;
849  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
850  for (int idx = 0; idx < index_step_target; ++idx) {
851  int current_idx_loc = current_idx + idx;
852  int current_idx_pair_loc = current_idx_pair + idx;
853  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
854  int index = row_offset + current_idx_loc;
855  int index_pair = row_offset + current_idx_pair_loc;
856  KernelDedicatedComplexT<MatrixT> element = input[index];
857  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
858  input[index].real = -element_pair.imag;
859  input[index].imag = element_pair.real;
860  input[index_pair].real = element.imag;
861  input[index_pair].imag = -element.real;
862  }
863  }
864  current_idx = current_idx + (index_step_target << 1);
865  }
866  }
867  }
868  );
869 }
870 
871 template<typename MatrixT>
872 void apply_Z_kernel_to_input_tbb_impl(MatrixT& input, const int& target_qbit,
873  const int& control_qbit,
874  const int& matrix_size) {
875  int index_step_target = 1 << target_qbit;
876 
877  tbb::parallel_for(tbb::blocked_range<int>(0, matrix_size >> 1, 1024),
878  [&](const tbb::blocked_range<int>& range) {
879  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
880  int current_idx = block_idx * (index_step_target << 1);
881  int current_idx_pair = current_idx + index_step_target;
882 
883  if (current_idx_pair >= matrix_size) continue;
884 
885  for(int idx = 0; idx < index_step_target; idx++) {
886  int current_idx_loc = current_idx + idx;
887  int current_idx_pair_loc = current_idx_pair + idx;
888 
889  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
890  int row_offset_pair = current_idx_pair_loc * input.stride;
891 
892  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
893  int index_pair = row_offset_pair + col_idx;
894 
895  input[index_pair].real = -input[index_pair].real;
896  input[index_pair].imag = -input[index_pair].imag;
897  }
898  }
899  }
900  }
901  }
902  );
903 }
904 
905 template<typename MatrixT>
906 void apply_Z_kernel_from_right_tbb_impl(MatrixT& input, const int& target_qbit,
907  const int& control_qbit,
908  const int& matrix_size) {
909  int index_step_target = 1 << target_qbit;
910 
911  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
912  [&](const tbb::blocked_range<int>& range) {
913  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
914  long long row_offset = (long long)row_idx * input.stride;
915  int current_idx = 0;
916  for (int current_idx_pair = current_idx + index_step_target;
917  current_idx_pair < matrix_size;
918  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
919  for (int idx = 0; idx < index_step_target; ++idx) {
920  int current_idx_loc = current_idx + idx;
921  int current_idx_pair_loc = current_idx_pair + idx;
922  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
923  int index_pair = row_offset + current_idx_pair_loc;
924  input[index_pair].real = -input[index_pair].real;
925  input[index_pair].imag = -input[index_pair].imag;
926  }
927  }
928  current_idx = current_idx + (index_step_target << 1);
929  }
930  }
931  }
932  );
933 }
934 
935 template<typename MatrixT>
936 void apply_H_kernel_to_input_tbb_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
937  int index_step_target = 1 << target_qbit;
938  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
939 
940  tbb::parallel_for(tbb::blocked_range<int>(0, matrix_size >> 1, 1024),
941  [&](const tbb::blocked_range<int>& range) {
942  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
943  int current_idx = block_idx * (index_step_target << 1);
944  int current_idx_pair = current_idx + index_step_target;
945 
946  if (current_idx_pair >= matrix_size) continue;
947 
948  for (int idx = 0; idx < index_step_target; idx++) {
949  int current_idx_loc = current_idx + idx;
950  int current_idx_pair_loc = current_idx_pair + idx;
951 
952  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
953  int row_offset = current_idx_loc * input.stride;
954  int row_offset_pair = current_idx_pair_loc * input.stride;
955 
956  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
957  int index = row_offset + col_idx;
958  int index_pair = row_offset_pair + col_idx;
959 
960  KernelDedicatedComplexT<MatrixT> element = input[index];
961  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
962 
963  input[index].real = inv_sqrt2 * (element.real + element_pair.real);
964  input[index].imag = inv_sqrt2 * (element.imag + element_pair.imag);
965 
966  input[index_pair].real = inv_sqrt2 * (element.real - element_pair.real);
967  input[index_pair].imag = inv_sqrt2 * (element.imag - element_pair.imag);
968  }
969  }
970  }
971  }
972  }
973  );
974 }
975 
976 template<typename MatrixT>
977 void apply_H_kernel_from_right_tbb_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
978  int index_step_target = 1 << target_qbit;
979  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
980 
981  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
982  [&](const tbb::blocked_range<int>& range) {
983  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
984  long long row_offset = (long long)row_idx * input.stride;
985  int current_idx = 0;
986  for (int current_idx_pair = current_idx + index_step_target;
987  current_idx_pair < matrix_size;
988  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
989  for (int idx = 0; idx < index_step_target; ++idx) {
990  int current_idx_loc = current_idx + idx;
991  int current_idx_pair_loc = current_idx_pair + idx;
992  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
993  int index = row_offset + current_idx_loc;
994  int index_pair = row_offset + current_idx_pair_loc;
995  KernelDedicatedComplexT<MatrixT> element = input[index];
996  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
997  input[index].real = inv_sqrt2 * (element.real + element_pair.real);
998  input[index].imag = inv_sqrt2 * (element.imag + element_pair.imag);
999  input[index_pair].real = inv_sqrt2 * (element.real - element_pair.real);
1000  input[index_pair].imag = inv_sqrt2 * (element.imag - element_pair.imag);
1001  }
1002  }
1003  current_idx = current_idx + (index_step_target << 1);
1004  }
1005  }
1006  }
1007  );
1008 }
1009 
1010 template<typename MatrixT>
1011 void apply_S_kernel_to_input_tbb_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1012  int index_step_target = 1 << target_qbit;
1013 
1014  tbb::parallel_for(tbb::blocked_range<int>(0, matrix_size >> 1, 1024),
1015  [&](const tbb::blocked_range<int>& range) {
1016  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
1017  int current_idx = block_idx * (index_step_target << 1);
1018  int current_idx_pair = current_idx + index_step_target;
1019 
1020  if (current_idx_pair >= matrix_size) continue;
1021 
1022  for (int idx = 0; idx < index_step_target; idx++) {
1023  int current_idx_loc = current_idx + idx;
1024  int current_idx_pair_loc = current_idx_pair + idx;
1025 
1026  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1027  int row_offset_pair = current_idx_pair_loc * input.stride;
1028 
1029  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1030  int index_pair = row_offset_pair + col_idx;
1031 
1032  double real = input[index_pair].real;
1033  double imag = input[index_pair].imag;
1034  input[index_pair].real = -imag;
1035  input[index_pair].imag = real;
1036  }
1037  }
1038  }
1039  }
1040  }
1041  );
1042 }
1043 
1044 template<typename MatrixT>
1045 void apply_S_kernel_from_right_tbb_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1046  int index_step_target = 1 << target_qbit;
1047 
1048  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
1049  [&](const tbb::blocked_range<int>& range) {
1050  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
1051  long long row_offset = (long long)row_idx * input.stride;
1052  int current_idx = 0;
1053  for (int current_idx_pair = current_idx + index_step_target;
1054  current_idx_pair < matrix_size;
1055  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1056  for (int idx = 0; idx < index_step_target; ++idx) {
1057  int current_idx_loc = current_idx + idx;
1058  int current_idx_pair_loc = current_idx_pair + idx;
1059  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1060  int index_pair = row_offset + current_idx_pair_loc;
1061  double real = input[index_pair].real;
1062  double imag = input[index_pair].imag;
1063  input[index_pair].real = -imag;
1064  input[index_pair].imag = real;
1065  }
1066  }
1067  current_idx = current_idx + (index_step_target << 1);
1068  }
1069  }
1070  }
1071  );
1072 }
1073 
1074 template<typename MatrixT>
1075 void apply_T_kernel_to_input_tbb_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1076  int index_step_target = 1 << target_qbit;
1077  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
1078 
1079  tbb::parallel_for(tbb::blocked_range<int>(0, matrix_size >> 1, 1024),
1080  [&](const tbb::blocked_range<int>& range) {
1081  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
1082  int current_idx = block_idx * (index_step_target << 1);
1083  int current_idx_pair = current_idx + index_step_target;
1084 
1085  if (current_idx_pair >= matrix_size) continue;
1086 
1087  for (int idx = 0; idx < index_step_target; idx++) {
1088  int current_idx_loc = current_idx + idx;
1089  int current_idx_pair_loc = current_idx_pair + idx;
1090 
1091  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1092  int row_offset_pair = current_idx_pair_loc * input.stride;
1093 
1094  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1095  int index_pair = row_offset_pair + col_idx;
1096 
1097  double real = input[index_pair].real;
1098  double imag = input[index_pair].imag;
1099  input[index_pair].real = inv_sqrt2 * (real - imag);
1100  input[index_pair].imag = inv_sqrt2 * (real + imag);
1101  }
1102  }
1103  }
1104  }
1105  }
1106  );
1107 }
1108 
1109 template<typename MatrixT>
1110 void apply_T_kernel_from_right_tbb_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1111  int index_step_target = 1 << target_qbit;
1112  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
1113 
1114  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
1115  [&](const tbb::blocked_range<int>& range) {
1116  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
1117  long long row_offset = (long long)row_idx * input.stride;
1118  int current_idx = 0;
1119  for (int current_idx_pair = current_idx + index_step_target;
1120  current_idx_pair < matrix_size;
1121  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1122  for (int idx = 0; idx < index_step_target; ++idx) {
1123  int current_idx_loc = current_idx + idx;
1124  int current_idx_pair_loc = current_idx_pair + idx;
1125  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1126  int index_pair = row_offset + current_idx_pair_loc;
1127  double real = input[index_pair].real;
1128  double imag = input[index_pair].imag;
1129  input[index_pair].real = inv_sqrt2 * (real - imag);
1130  input[index_pair].imag = inv_sqrt2 * (real + imag);
1131  }
1132  }
1133  current_idx = current_idx + (index_step_target << 1);
1134  }
1135  }
1136  }
1137  );
1138 }
1139 
1140 template<typename MatrixT>
1141 void apply_SWAP_kernel_to_input_tbb_impl(MatrixT& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) {
1142  // Validate target qubits - SWAP gate requires exactly 2 target qubits
1143  if (target_qbits.size() != 2) {
1144  throw std::runtime_error("SWAP gate kernel requires exactly 2 target qubits, got " +
1145  std::to_string(target_qbits.size()));
1146  }
1147 
1148  int target_qbit1 = target_qbits[0];
1149  int target_qbit2 = target_qbits[1];
1150 
1151  std::vector<int> non_involved_qbits;
1152  int qbit_num = (int)std::log2(matrix_size);
1153  for (int idx=0; idx<qbit_num; idx++){
1154  bool is_target = (idx == target_qbit1 || idx == target_qbit2);
1155  bool is_control = std::find(control_qbits.begin(), control_qbits.end(), idx) != control_qbits.end();
1156  if (!is_target && !is_control){
1157  non_involved_qbits.push_back(idx);
1158  }
1159  }
1160 
1161  // Build control qubit mask
1162  int control_mask = 0;
1163  for (int control_qbit : control_qbits) {
1164  control_mask |= (1 << control_qbit);
1165  }
1166 
1167  int total_blocks = matrix_size >> (qbit_num - non_involved_qbits.size());
1168 
1169  tbb::parallel_for(tbb::blocked_range<int>(0, total_blocks, 64),
1170  [&](const tbb::blocked_range<int>& range) {
1171  for (int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
1172  int base = 0;
1173  for (size_t qdx=0; qdx<non_involved_qbits.size();qdx++){
1174  if ((block_idx >> qdx) & 1) {
1175  base |= (1<<non_involved_qbits[qdx]);
1176  }
1177  }
1178  base |= control_mask;
1179  int swap_idx = base|(1<<target_qbit1);
1180  int swap_idx_pair = base|(1<<target_qbit2);
1181 
1182  std::swap_ranges(
1183  input.get_data() + swap_idx*input.stride,
1184  input.get_data() + swap_idx*input.stride + input.cols,
1185  input.get_data() + swap_idx_pair*input.stride
1186  );
1187  }
1188  }
1189  );
1190 }
1191 
1192 
1193 template<typename MatrixT>
1194 void apply_SWAP_kernel_from_right_tbb_impl(MatrixT& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) {
1195  if (target_qbits.size() != 2) {
1196  throw std::runtime_error("SWAP gate kernel requires exactly 2 target qubits, got " +
1197  std::to_string(target_qbits.size()));
1198  }
1199 
1200  int target_qbit1 = target_qbits[0];
1201  int target_qbit2 = target_qbits[1];
1202 
1203  std::vector<int> non_involved_qbits;
1204  int qbit_num = (int)std::log2(matrix_size);
1205  for (int idx = 0; idx < qbit_num; idx++) {
1206  bool is_target = (idx == target_qbit1 || idx == target_qbit2);
1207  bool is_control = std::find(control_qbits.begin(), control_qbits.end(), idx) != control_qbits.end();
1208  if (!is_target && !is_control) {
1209  non_involved_qbits.push_back(idx);
1210  }
1211  }
1212 
1213  int control_mask = 0;
1214  for (int control_qbit : control_qbits) {
1215  control_mask |= (1 << control_qbit);
1216  }
1217 
1218  int total_blocks = matrix_size >> (qbit_num - non_involved_qbits.size());
1219 
1220  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
1221  [&](const tbb::blocked_range<int>& range) {
1222  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
1223  long long row_offset = (long long)row_idx * input.stride;
1224  for (int block_idx = 0; block_idx < total_blocks; ++block_idx) {
1225  int base = 0;
1226  for (size_t qdx = 0; qdx < non_involved_qbits.size(); qdx++) {
1227  if ((block_idx >> qdx) & 1) {
1228  base |= (1 << non_involved_qbits[qdx]);
1229  }
1230  }
1231  base |= control_mask;
1232  int swap_idx = base | (1 << target_qbit1);
1233  int swap_idx_pair = base | (1 << target_qbit2);
1234 
1235  std::swap(input[row_offset + swap_idx], input[row_offset + swap_idx_pair]);
1236  }
1237  }
1238  }
1239  );
1240 }
1241 
1242 
1243 template<typename MatrixT>
1244 void apply_SYC_kernel_to_input_tbb_impl(MatrixT& input, const int& target_qbit,
1245  const int& control_qbit,
1246  const int& matrix_size) {
1247 
1248  int index_step_target = 1 << target_qbit;
1249  int index_step_control = 1 << control_qbit;
1250 
1251  int loop_size = index_step_target < index_step_control ? index_step_target : index_step_control;
1252  int iterations = control_qbit < target_qbit ? Power_of_2(target_qbit - control_qbit - 1) : Power_of_2(control_qbit - target_qbit - 1);
1253 
1254  int idx00 = 0;
1255  int idx01 = index_step_target;
1256  int idx10 = index_step_control;
1257  int idx11 = index_step_target + index_step_control;
1258 
1259  const double phase_real = std::sqrt(3.0) / 2.0;
1260  const double phase_imag = -0.5;
1261 
1262  while (idx11 < matrix_size) {
1263  for (int jdx = 0; jdx < iterations; ++jdx) {
1264  tbb::parallel_for(0, loop_size, 1, [&](int idx) {
1265  int idx01_loc = idx01 + idx;
1266  int idx10_loc = idx10 + idx;
1267  int idx11_loc = idx11 + idx;
1268 
1269  int offset01 = idx01_loc * input.stride;
1270  int offset10 = idx10_loc * input.stride;
1271  int offset11 = idx11_loc * input.stride;
1272 
1273  for (int col_idx = 0; col_idx < input.cols; ++col_idx) {
1274  KernelDedicatedComplexT<MatrixT> element01 = input[offset01 + col_idx];
1275  KernelDedicatedComplexT<MatrixT> element10 = input[offset10 + col_idx];
1276 
1277  input[offset01 + col_idx].real = element10.imag;
1278  input[offset01 + col_idx].imag = -element10.real;
1279 
1280  input[offset10 + col_idx].real = element01.imag;
1281  input[offset10 + col_idx].imag = -element01.real;
1282 
1283  KernelDedicatedComplexT<MatrixT> element11 = input[offset11 + col_idx];
1284  input[offset11 + col_idx].real = phase_real * element11.real - phase_imag * element11.imag;
1285  input[offset11 + col_idx].imag = phase_real * element11.imag + phase_imag * element11.real;
1286  }
1287  });
1288 
1289  idx00 += 2 * loop_size;
1290  idx01 += 2 * loop_size;
1291  idx10 += 2 * loop_size;
1292  idx11 += 2 * loop_size;
1293  }
1294 
1295  idx00 += 2 * loop_size * iterations;
1296  idx01 += 2 * loop_size * iterations;
1297  idx10 += 2 * loop_size * iterations;
1298  idx11 += 2 * loop_size * iterations;
1299  }
1300 }
1301 
1302 template<typename MatrixT>
1303 void apply_SYC_kernel_from_right_tbb_impl(MatrixT& input, const int& target_qbit,
1304  const int& control_qbit,
1305  const int& matrix_size) {
1306 
1307  int index_step_target = 1 << target_qbit;
1308  int index_step_control = 1 << control_qbit;
1309  int loop_size = index_step_target < index_step_control ? index_step_target : index_step_control;
1310  int iterations = control_qbit < target_qbit ? Power_of_2(target_qbit - control_qbit - 1) : Power_of_2(control_qbit - target_qbit - 1);
1311  const double phase_real = std::sqrt(3.0) / 2.0;
1312  const double phase_imag = -0.5;
1313 
1314  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, 32),
1315  [&](const tbb::blocked_range<int>& range) {
1316  for (int row_idx = range.begin(); row_idx != range.end(); ++row_idx) {
1317  int offset = row_idx * input.stride;
1318  int idx00 = 0;
1319  int idx01 = index_step_target;
1320  int idx10 = index_step_control;
1321  int idx11 = index_step_target + index_step_control;
1322 
1323  while (idx11 < matrix_size) {
1324  for (int jdx = 0; jdx < iterations; ++jdx) {
1325  for (int idx = 0; idx < loop_size; ++idx) {
1326  int idx01_loc = idx01 + idx;
1327  int idx10_loc = idx10 + idx;
1328  int idx11_loc = idx11 + idx;
1329 
1330  KernelDedicatedComplexT<MatrixT> element01 = input[offset + idx01_loc];
1331  KernelDedicatedComplexT<MatrixT> element10 = input[offset + idx10_loc];
1332  input[offset + idx01_loc].real = element10.imag;
1333  input[offset + idx01_loc].imag = -element10.real;
1334  input[offset + idx10_loc].real = element01.imag;
1335  input[offset + idx10_loc].imag = -element01.real;
1336 
1337  KernelDedicatedComplexT<MatrixT> element11 = input[offset + idx11_loc];
1338  input[offset + idx11_loc].real = phase_real * element11.real - phase_imag * element11.imag;
1339  input[offset + idx11_loc].imag = phase_real * element11.imag + phase_imag * element11.real;
1340  }
1341 
1342  idx00 += 2 * loop_size;
1343  idx01 += 2 * loop_size;
1344  idx10 += 2 * loop_size;
1345  idx11 += 2 * loop_size;
1346  }
1347 
1348  idx00 += 2 * loop_size * iterations;
1349  idx01 += 2 * loop_size * iterations;
1350  idx10 += 2 * loop_size * iterations;
1351  idx11 += 2 * loop_size * iterations;
1352  }
1353  }
1354  }
1355  );
1356 }
1357 
1358 // OpenMP Parallelized versions
1359 
1360 template<typename MatrixT>
1361 void apply_X_kernel_to_input_omp_impl(MatrixT& input, const std::vector<int>& target_qbits,
1362  const std::vector<int>& control_qbits,
1363  const int& matrix_size) {
1364  // Validate target qubits - X gate requires exactly 1 target qubit
1365  if (target_qbits.size() != 1) {
1366  throw std::runtime_error("X gate kernel requires exactly 1 target qubit, got " +
1367  std::to_string(target_qbits.size()));
1368  }
1369 
1370  int target_qbit = target_qbits[0];
1371  int index_step_target = 1 << target_qbit;
1372  int total_blocks = matrix_size >> (target_qbit + 1);
1373 
1374  #pragma omp parallel for schedule(static)
1375  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1376  int current_idx = block_idx * (index_step_target << 1);
1377  int current_idx_pair = current_idx + index_step_target;
1378 
1379  for(int idx = 0; idx < index_step_target; idx++) {
1380  int current_idx_loc = current_idx + idx;
1381  int current_idx_pair_loc = current_idx_pair + idx;
1382 
1383  // Check all control qubits are active
1384  bool all_controls_active = true;
1385  for (int control_qbit : control_qbits) {
1386  if (!((current_idx_loc >> control_qbit) & 1)) {
1387  all_controls_active = false;
1388  break;
1389  }
1390  }
1391 
1392  if (all_controls_active) {
1393  long long row_offset = (long long)current_idx_loc * input.stride;
1394  long long row_offset_pair = (long long)current_idx_pair_loc * input.stride;
1395 
1396  std::swap_ranges(
1397  input.get_data() + row_offset,
1398  input.get_data() + row_offset + input.cols,
1399  input.get_data() + row_offset_pair
1400  );
1401  }
1402  }
1403  }
1404 }
1405 
1406 template<typename MatrixT>
1407 void apply_X_kernel_from_right_omp_impl(MatrixT& input, const std::vector<int>& target_qbits,
1408  const std::vector<int>& control_qbits,
1409  const int& matrix_size) {
1410  if (target_qbits.size() != 1) {
1411  throw std::runtime_error("X gate kernel requires exactly 1 target qubit, got " +
1412  std::to_string(target_qbits.size()));
1413  }
1414 
1415  int target_qbit = target_qbits[0];
1416  int index_step_target = 1 << target_qbit;
1417 
1418  #pragma omp parallel for schedule(static)
1419  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1420  long long row_offset = (long long)row_idx * input.stride;
1421  int current_idx = 0;
1422 
1423  for (int current_idx_pair = current_idx + index_step_target;
1424  current_idx_pair < matrix_size;
1425  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1426 
1427  for (int idx = 0; idx < index_step_target; ++idx) {
1428  int current_idx_loc = current_idx + idx;
1429  int current_idx_pair_loc = current_idx_pair + idx;
1430 
1431  bool all_controls_active = true;
1432  for (int control_qbit : control_qbits) {
1433  if (!((current_idx_loc >> control_qbit) & 1)) {
1434  all_controls_active = false;
1435  break;
1436  }
1437  }
1438 
1439  if (all_controls_active) {
1440  std::swap(input[row_offset + current_idx_loc], input[row_offset + current_idx_pair_loc]);
1441  }
1442  }
1443 
1444  current_idx = current_idx + (index_step_target << 1);
1445  }
1446  }
1447 }
1448 
1449 template<typename MatrixT>
1450 void apply_Y_kernel_to_input_omp_impl(MatrixT& input, const int& target_qbit,
1451  const int& control_qbit,
1452  const int& matrix_size) {
1453  int index_step_target = 1 << target_qbit;
1454  int total_blocks = matrix_size >> 1;
1455 
1456  #pragma omp parallel for schedule(static)
1457  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1458  int current_idx = block_idx * (index_step_target << 1);
1459  int current_idx_pair = current_idx + index_step_target;
1460 
1461  if (current_idx_pair >= matrix_size) continue;
1462 
1463  for(int idx = 0; idx < index_step_target; idx++) {
1464  int current_idx_loc = current_idx + idx;
1465  int current_idx_pair_loc = current_idx_pair + idx;
1466 
1467  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1468  int row_offset = current_idx_loc * input.stride;
1469  int row_offset_pair = current_idx_pair_loc * input.stride;
1470 
1471  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1472  int index = row_offset + col_idx;
1473  int index_pair = row_offset_pair + col_idx;
1474 
1475  KernelDedicatedComplexT<MatrixT> element = input[index];
1476  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
1477 
1478  input[index].real = element_pair.imag;
1479  input[index].imag = -element_pair.real;
1480 
1481  input[index_pair].real = -element.imag;
1482  input[index_pair].imag = element.real;
1483  }
1484  }
1485  }
1486  }
1487 }
1488 
1489 template<typename MatrixT>
1490 void apply_Y_kernel_from_right_omp_impl(MatrixT& input, const int& target_qbit,
1491  const int& control_qbit,
1492  const int& matrix_size) {
1493  int index_step_target = 1 << target_qbit;
1494 
1495  #pragma omp parallel for schedule(static)
1496  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1497  long long row_offset = (long long)row_idx * input.stride;
1498  int current_idx = 0;
1499  for (int current_idx_pair = current_idx + index_step_target;
1500  current_idx_pair < matrix_size;
1501  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1502  for (int idx = 0; idx < index_step_target; ++idx) {
1503  int current_idx_loc = current_idx + idx;
1504  int current_idx_pair_loc = current_idx_pair + idx;
1505  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1506  int index = row_offset + current_idx_loc;
1507  int index_pair = row_offset + current_idx_pair_loc;
1508  KernelDedicatedComplexT<MatrixT> element = input[index];
1509  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
1510  input[index].real = -element_pair.imag;
1511  input[index].imag = element_pair.real;
1512  input[index_pair].real = element.imag;
1513  input[index_pair].imag = -element.real;
1514  }
1515  }
1516  current_idx = current_idx + (index_step_target << 1);
1517  }
1518  }
1519 }
1520 
1521 template<typename MatrixT>
1522 void apply_Z_kernel_to_input_omp_impl(MatrixT& input, const int& target_qbit,
1523  const int& control_qbit,
1524  const int& matrix_size) {
1525  int index_step_target = 1 << target_qbit;
1526  int total_blocks = matrix_size >> 1;
1527 
1528  #pragma omp parallel for schedule(static)
1529  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1530  int current_idx = block_idx * (index_step_target << 1);
1531  int current_idx_pair = current_idx + index_step_target;
1532 
1533  if (current_idx_pair >= matrix_size) continue;
1534 
1535  for(int idx = 0; idx < index_step_target; idx++) {
1536  int current_idx_loc = current_idx + idx;
1537  int current_idx_pair_loc = current_idx_pair + idx;
1538 
1539  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1540  int row_offset_pair = current_idx_pair_loc * input.stride;
1541 
1542  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1543  int index_pair = row_offset_pair + col_idx;
1544 
1545  input[index_pair].real = -input[index_pair].real;
1546  input[index_pair].imag = -input[index_pair].imag;
1547  }
1548  }
1549  }
1550  }
1551 }
1552 
1553 template<typename MatrixT>
1554 void apply_Z_kernel_from_right_omp_impl(MatrixT& input, const int& target_qbit,
1555  const int& control_qbit,
1556  const int& matrix_size) {
1557  int index_step_target = 1 << target_qbit;
1558 
1559  #pragma omp parallel for schedule(static)
1560  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1561  long long row_offset = (long long)row_idx * input.stride;
1562  int current_idx = 0;
1563  for (int current_idx_pair = current_idx + index_step_target;
1564  current_idx_pair < matrix_size;
1565  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1566  for (int idx = 0; idx < index_step_target; ++idx) {
1567  int current_idx_loc = current_idx + idx;
1568  int current_idx_pair_loc = current_idx_pair + idx;
1569  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1570  int index_pair = row_offset + current_idx_pair_loc;
1571  input[index_pair].real = -input[index_pair].real;
1572  input[index_pair].imag = -input[index_pair].imag;
1573  }
1574  }
1575  current_idx = current_idx + (index_step_target << 1);
1576  }
1577  }
1578 }
1579 
1580 template<typename MatrixT>
1581 void apply_H_kernel_to_input_omp_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1582  int index_step_target = 1 << target_qbit;
1583  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
1584  int total_blocks = matrix_size >> 1;
1585 
1586  #pragma omp parallel for schedule(static)
1587  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1588  int current_idx = block_idx * (index_step_target << 1);
1589  int current_idx_pair = current_idx + index_step_target;
1590 
1591  if (current_idx_pair >= matrix_size) continue;
1592 
1593  for (int idx = 0; idx < index_step_target; idx++) {
1594  int current_idx_loc = current_idx + idx;
1595  int current_idx_pair_loc = current_idx_pair + idx;
1596 
1597  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1598  int row_offset = current_idx_loc * input.stride;
1599  int row_offset_pair = current_idx_pair_loc * input.stride;
1600 
1601  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1602  int index = row_offset + col_idx;
1603  int index_pair = row_offset_pair + col_idx;
1604 
1605  KernelDedicatedComplexT<MatrixT> element = input[index];
1606  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
1607 
1608  input[index].real = inv_sqrt2 * (element.real + element_pair.real);
1609  input[index].imag = inv_sqrt2 * (element.imag + element_pair.imag);
1610 
1611  input[index_pair].real = inv_sqrt2 * (element.real - element_pair.real);
1612  input[index_pair].imag = inv_sqrt2 * (element.imag - element_pair.imag);
1613  }
1614  }
1615  }
1616  }
1617 }
1618 
1619 template<typename MatrixT>
1620 void apply_H_kernel_from_right_omp_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1621  int index_step_target = 1 << target_qbit;
1622  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
1623 
1624  #pragma omp parallel for schedule(static)
1625  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1626  long long row_offset = (long long)row_idx * input.stride;
1627  int current_idx = 0;
1628  for (int current_idx_pair = current_idx + index_step_target;
1629  current_idx_pair < matrix_size;
1630  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1631  for (int idx = 0; idx < index_step_target; ++idx) {
1632  int current_idx_loc = current_idx + idx;
1633  int current_idx_pair_loc = current_idx_pair + idx;
1634  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1635  int index = row_offset + current_idx_loc;
1636  int index_pair = row_offset + current_idx_pair_loc;
1637  KernelDedicatedComplexT<MatrixT> element = input[index];
1638  KernelDedicatedComplexT<MatrixT> element_pair = input[index_pair];
1639  input[index].real = inv_sqrt2 * (element.real + element_pair.real);
1640  input[index].imag = inv_sqrt2 * (element.imag + element_pair.imag);
1641  input[index_pair].real = inv_sqrt2 * (element.real - element_pair.real);
1642  input[index_pair].imag = inv_sqrt2 * (element.imag - element_pair.imag);
1643  }
1644  }
1645  current_idx = current_idx + (index_step_target << 1);
1646  }
1647  }
1648 }
1649 
1650 template<typename MatrixT>
1651 void apply_S_kernel_to_input_omp_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1652  int index_step_target = 1 << target_qbit;
1653  int total_blocks = matrix_size >> 1;
1654 
1655  #pragma omp parallel for schedule(static)
1656  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1657  int current_idx = block_idx * (index_step_target << 1);
1658  int current_idx_pair = current_idx + index_step_target;
1659 
1660  if (current_idx_pair >= matrix_size) continue;
1661 
1662  for (int idx = 0; idx < index_step_target; idx++) {
1663  int current_idx_loc = current_idx + idx;
1664  int current_idx_pair_loc = current_idx_pair + idx;
1665 
1666  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1667  int row_offset_pair = current_idx_pair_loc * input.stride;
1668 
1669  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1670  int index_pair = row_offset_pair + col_idx;
1671 
1672  double real = input[index_pair].real;
1673  double imag = input[index_pair].imag;
1674  input[index_pair].real = -imag;
1675  input[index_pair].imag = real;
1676  }
1677  }
1678  }
1679  }
1680 }
1681 
1682 template<typename MatrixT>
1683 void apply_S_kernel_from_right_omp_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1684  int index_step_target = 1 << target_qbit;
1685 
1686  #pragma omp parallel for schedule(static)
1687  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1688  long long row_offset = (long long)row_idx * input.stride;
1689  int current_idx = 0;
1690  for (int current_idx_pair = current_idx + index_step_target;
1691  current_idx_pair < matrix_size;
1692  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1693  for (int idx = 0; idx < index_step_target; ++idx) {
1694  int current_idx_loc = current_idx + idx;
1695  int current_idx_pair_loc = current_idx_pair + idx;
1696  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1697  int index_pair = row_offset + current_idx_pair_loc;
1698  double real = input[index_pair].real;
1699  double imag = input[index_pair].imag;
1700  input[index_pair].real = -imag;
1701  input[index_pair].imag = real;
1702  }
1703  }
1704  current_idx = current_idx + (index_step_target << 1);
1705  }
1706  }
1707 }
1708 
1709 template<typename MatrixT>
1710 void apply_T_kernel_to_input_omp_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1711  int index_step_target = 1 << target_qbit;
1712  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
1713  int total_blocks = matrix_size >> 1;
1714 
1715  #pragma omp parallel for schedule(static)
1716  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1717  int current_idx = block_idx * (index_step_target << 1);
1718  int current_idx_pair = current_idx + index_step_target;
1719 
1720  if (current_idx_pair >= matrix_size) continue;
1721 
1722  for (int idx = 0; idx < index_step_target; idx++) {
1723  int current_idx_loc = current_idx + idx;
1724  int current_idx_pair_loc = current_idx_pair + idx;
1725 
1726  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1727  int row_offset_pair = current_idx_pair_loc * input.stride;
1728 
1729  for (int col_idx = 0; col_idx < input.cols; col_idx++) {
1730  int index_pair = row_offset_pair + col_idx;
1731 
1732  double real = input[index_pair].real;
1733  double imag = input[index_pair].imag;
1734  input[index_pair].real = inv_sqrt2 * (real - imag);
1735  input[index_pair].imag = inv_sqrt2 * (real + imag);
1736  }
1737  }
1738  }
1739  }
1740 }
1741 
1742 template<typename MatrixT>
1743 void apply_T_kernel_from_right_omp_impl(MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
1744  int index_step_target = 1 << target_qbit;
1745  const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
1746 
1747  #pragma omp parallel for schedule(static)
1748  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1749  long long row_offset = (long long)row_idx * input.stride;
1750  int current_idx = 0;
1751  for (int current_idx_pair = current_idx + index_step_target;
1752  current_idx_pair < matrix_size;
1753  current_idx_pair = current_idx_pair + (index_step_target << 1)) {
1754  for (int idx = 0; idx < index_step_target; ++idx) {
1755  int current_idx_loc = current_idx + idx;
1756  int current_idx_pair_loc = current_idx_pair + idx;
1757  if ((control_qbit < 0) || ((current_idx_loc >> control_qbit) & 1)) {
1758  int index_pair = row_offset + current_idx_pair_loc;
1759  double real = input[index_pair].real;
1760  double imag = input[index_pair].imag;
1761  input[index_pair].real = inv_sqrt2 * (real - imag);
1762  input[index_pair].imag = inv_sqrt2 * (real + imag);
1763  }
1764  }
1765  current_idx = current_idx + (index_step_target << 1);
1766  }
1767  }
1768 }
1769 
1770 template<typename MatrixT>
1771 void apply_SWAP_kernel_to_input_omp_impl(MatrixT& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) {
1772  // Validate target qubits - SWAP gate requires exactly 2 target qubits
1773  if (target_qbits.size() != 2) {
1774  throw std::runtime_error("SWAP gate kernel requires exactly 2 target qubits, got " +
1775  std::to_string(target_qbits.size()));
1776  }
1777 
1778  int target_qbit1 = target_qbits[0];
1779  int target_qbit2 = target_qbits[1];
1780 
1781  std::vector<int> non_involved_qbits;
1782  int qbit_num = (int)std::log2(matrix_size);
1783  for (int idx=0; idx<qbit_num; idx++){
1784  bool is_target = (idx == target_qbit1 || idx == target_qbit2);
1785  bool is_control = std::find(control_qbits.begin(), control_qbits.end(), idx) != control_qbits.end();
1786  if (!is_target && !is_control){
1787  non_involved_qbits.push_back(idx);
1788  }
1789  }
1790 
1791  // Build control qubit mask
1792  int control_mask = 0;
1793  for (int control_qbit : control_qbits) {
1794  control_mask |= (1 << control_qbit);
1795  }
1796 
1797  int total_blocks = matrix_size >> (qbit_num - non_involved_qbits.size());
1798 
1799  #pragma omp parallel for schedule(static)
1800  for (int block_idx = 0; block_idx < total_blocks; block_idx++) {
1801  int base = 0;
1802  for (size_t qdx=0; qdx<non_involved_qbits.size();qdx++){
1803  if ((block_idx >> qdx) & 1) {
1804  base |= (1<<non_involved_qbits[qdx]);
1805  }
1806  }
1807  base |= control_mask;
1808  int swap_idx = base|(1<<target_qbit1);
1809  int swap_idx_pair = base|(1<<target_qbit2);
1810 
1811  std::swap_ranges(
1812  input.get_data() + swap_idx*input.stride,
1813  input.get_data() + swap_idx*input.stride + input.cols,
1814  input.get_data() + swap_idx_pair*input.stride
1815  );
1816  }
1817 }
1818 
1819 template<typename MatrixT>
1820 void apply_SWAP_kernel_from_right_omp_impl(MatrixT& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) {
1821  if (target_qbits.size() != 2) {
1822  throw std::runtime_error("SWAP gate kernel requires exactly 2 target qubits, got " +
1823  std::to_string(target_qbits.size()));
1824  }
1825 
1826  int target_qbit1 = target_qbits[0];
1827  int target_qbit2 = target_qbits[1];
1828 
1829  std::vector<int> non_involved_qbits;
1830  int qbit_num = (int)std::log2(matrix_size);
1831  for (int idx = 0; idx < qbit_num; idx++) {
1832  bool is_target = (idx == target_qbit1 || idx == target_qbit2);
1833  bool is_control = std::find(control_qbits.begin(), control_qbits.end(), idx) != control_qbits.end();
1834  if (!is_target && !is_control) {
1835  non_involved_qbits.push_back(idx);
1836  }
1837  }
1838 
1839  int control_mask = 0;
1840  for (int control_qbit : control_qbits) {
1841  control_mask |= (1 << control_qbit);
1842  }
1843 
1844  int total_blocks = matrix_size >> (qbit_num - non_involved_qbits.size());
1845 
1846  #pragma omp parallel for schedule(static)
1847  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1848  long long row_offset = (long long)row_idx * input.stride;
1849  for (int block_idx = 0; block_idx < total_blocks; ++block_idx) {
1850  int base = 0;
1851  for (size_t qdx = 0; qdx < non_involved_qbits.size(); qdx++) {
1852  if ((block_idx >> qdx) & 1) {
1853  base |= (1 << non_involved_qbits[qdx]);
1854  }
1855  }
1856  base |= control_mask;
1857  int swap_idx = base | (1 << target_qbit1);
1858  int swap_idx_pair = base | (1 << target_qbit2);
1859 
1860  std::swap(input[row_offset + swap_idx], input[row_offset + swap_idx_pair]);
1861  }
1862  }
1863 }
1864 
1865 
1866 template<typename MatrixT>
1867 void apply_SYC_kernel_to_input_omp_impl(MatrixT& input, const int& target_qbit,
1868  const int& control_qbit,
1869  const int& matrix_size) {
1870 
1871  int index_step_target = 1 << target_qbit;
1872  int index_step_control = 1 << control_qbit;
1873 
1874  int loop_size = index_step_target < index_step_control ? index_step_target : index_step_control;
1875  int iterations = control_qbit < target_qbit ? Power_of_2(target_qbit - control_qbit - 1) : Power_of_2(control_qbit - target_qbit - 1);
1876 
1877  int idx00 = 0;
1878  int idx01 = index_step_target;
1879  int idx10 = index_step_control;
1880  int idx11 = index_step_target + index_step_control;
1881 
1882  const double phase_real = std::sqrt(3.0) / 2.0;
1883  const double phase_imag = -0.5;
1884 
1885  while (idx11 < matrix_size) {
1886  for (int jdx = 0; jdx < iterations; ++jdx) {
1887  #pragma omp parallel for schedule(static)
1888  for (int idx = 0; idx < loop_size; ++idx) {
1889  int idx01_loc = idx01 + idx;
1890  int idx10_loc = idx10 + idx;
1891  int idx11_loc = idx11 + idx;
1892 
1893  int offset01 = idx01_loc * input.stride;
1894  int offset10 = idx10_loc * input.stride;
1895  int offset11 = idx11_loc * input.stride;
1896 
1897  for (int col_idx = 0; col_idx < input.cols; ++col_idx) {
1898  KernelDedicatedComplexT<MatrixT> element01 = input[offset01 + col_idx];
1899  KernelDedicatedComplexT<MatrixT> element10 = input[offset10 + col_idx];
1900 
1901  input[offset01 + col_idx].real = element10.imag;
1902  input[offset01 + col_idx].imag = -element10.real;
1903 
1904  input[offset10 + col_idx].real = element01.imag;
1905  input[offset10 + col_idx].imag = -element01.real;
1906 
1907  KernelDedicatedComplexT<MatrixT> element11 = input[offset11 + col_idx];
1908  input[offset11 + col_idx].real = phase_real * element11.real - phase_imag * element11.imag;
1909  input[offset11 + col_idx].imag = phase_real * element11.imag + phase_imag * element11.real;
1910  }
1911  }
1912 
1913  idx00 += 2 * loop_size;
1914  idx01 += 2 * loop_size;
1915  idx10 += 2 * loop_size;
1916  idx11 += 2 * loop_size;
1917  }
1918 
1919  idx00 += 2 * loop_size * iterations;
1920  idx01 += 2 * loop_size * iterations;
1921  idx10 += 2 * loop_size * iterations;
1922  idx11 += 2 * loop_size * iterations;
1923  }
1924 }
1925 
1926 template<typename MatrixT>
1927 void apply_SYC_kernel_from_right_omp_impl(MatrixT& input, const int& target_qbit,
1928  const int& control_qbit,
1929  const int& matrix_size) {
1930 
1931  int index_step_target = 1 << target_qbit;
1932  int index_step_control = 1 << control_qbit;
1933 
1934  int loop_size = index_step_target < index_step_control ? index_step_target : index_step_control;
1935  int iterations = control_qbit < target_qbit ? Power_of_2(target_qbit - control_qbit - 1) : Power_of_2(control_qbit - target_qbit - 1);
1936 
1937  const double phase_real = std::sqrt(3.0) / 2.0;
1938  const double phase_imag = -0.5;
1939 
1940  #pragma omp parallel for schedule(static)
1941  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
1942  int offset = row_idx * input.stride;
1943  int idx00 = 0;
1944  int idx01 = index_step_target;
1945  int idx10 = index_step_control;
1946  int idx11 = index_step_target + index_step_control;
1947 
1948  while (idx11 < matrix_size) {
1949  for (int jdx = 0; jdx < iterations; ++jdx) {
1950  for (int idx = 0; idx < loop_size; ++idx) {
1951  int idx01_loc = idx01 + idx;
1952  int idx10_loc = idx10 + idx;
1953  int idx11_loc = idx11 + idx;
1954 
1955  KernelDedicatedComplexT<MatrixT> element01 = input[offset + idx01_loc];
1956  KernelDedicatedComplexT<MatrixT> element10 = input[offset + idx10_loc];
1957 
1958  input[offset + idx01_loc].real = element10.imag;
1959  input[offset + idx01_loc].imag = -element10.real;
1960 
1961  input[offset + idx10_loc].real = element01.imag;
1962  input[offset + idx10_loc].imag = -element01.real;
1963 
1964  KernelDedicatedComplexT<MatrixT> element11 = input[offset + idx11_loc];
1965  input[offset + idx11_loc].real = phase_real * element11.real - phase_imag * element11.imag;
1966  input[offset + idx11_loc].imag = phase_real * element11.imag + phase_imag * element11.real;
1967  }
1968 
1969  idx00 += 2 * loop_size;
1970  idx01 += 2 * loop_size;
1971  idx10 += 2 * loop_size;
1972  idx11 += 2 * loop_size;
1973  }
1974 
1975  idx00 += 2 * loop_size * iterations;
1976  idx01 += 2 * loop_size * iterations;
1977  idx10 += 2 * loop_size * iterations;
1978  idx11 += 2 * loop_size * iterations;
1979  }
1980  }
1981 }
1982 
1983 void apply_X_kernel_to_input(Matrix& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_to_input_impl(input, target_qbits, control_qbits, matrix_size); }
1984 void apply_X_kernel_to_input(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_to_input_impl(input, target_qbits, control_qbits, matrix_size); }
1985 void apply_X_kernel_from_right(Matrix& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_from_right_impl(input, target_qbits, control_qbits, matrix_size); }
1986 void apply_X_kernel_from_right(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_from_right_impl(input, target_qbits, control_qbits, matrix_size); }
1987 void apply_Y_kernel_to_input(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
1988 void apply_Y_kernel_to_input(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
1989 void apply_Y_kernel_from_right(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
1990 void apply_Y_kernel_from_right(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
1991 void apply_Z_kernel_to_input(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
1992 void apply_Z_kernel_to_input(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
1993 void apply_Z_kernel_from_right(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
1994 void apply_Z_kernel_from_right(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
1995 void apply_H_kernel_to_input(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
1996 void apply_H_kernel_to_input(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
1997 void apply_H_kernel_from_right(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
1998 void apply_H_kernel_from_right(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
1999 void apply_S_kernel_to_input(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
2000 void apply_S_kernel_to_input(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
2001 void apply_S_kernel_from_right(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
2002 void apply_S_kernel_from_right(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
2003 void apply_T_kernel_to_input(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
2004 void apply_T_kernel_to_input(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
2005 void apply_T_kernel_from_right(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
2006 void apply_T_kernel_from_right(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
2007 void apply_SWAP_kernel_to_input(Matrix& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_to_input_impl(input, target_qbits, control_qbits, matrix_size); }
2008 void apply_SWAP_kernel_to_input(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_to_input_impl(input, target_qbits, control_qbits, matrix_size); }
2009 void apply_SWAP_kernel_from_right(Matrix& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_from_right_impl(input, target_qbits, control_qbits, matrix_size); }
2010 void apply_SWAP_kernel_from_right(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_from_right_impl(input, target_qbits, control_qbits, matrix_size); }
2011 void apply_SYC_kernel_to_input(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
2012 void apply_SYC_kernel_to_input(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_to_input_impl(input, target_qbit, control_qbit, matrix_size); }
2013 void apply_SYC_kernel_from_right(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
2014 void apply_SYC_kernel_from_right(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_from_right_impl(input, target_qbit, control_qbit, matrix_size); }
2015 
2016 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) { apply_X_kernel_to_input_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2017 void apply_X_kernel_to_input_tbb(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_to_input_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2018 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) { apply_X_kernel_from_right_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2019 void apply_X_kernel_from_right_tbb(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_from_right_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2020 void apply_Y_kernel_to_input_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2021 void apply_Y_kernel_to_input_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2022 void apply_Y_kernel_from_right_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2023 void apply_Y_kernel_from_right_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2024 void apply_Z_kernel_to_input_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2025 void apply_Z_kernel_to_input_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2026 void apply_Z_kernel_from_right_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2027 void apply_Z_kernel_from_right_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2028 void apply_H_kernel_to_input_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2029 void apply_H_kernel_to_input_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2030 void apply_H_kernel_from_right_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2031 void apply_H_kernel_from_right_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2032 void apply_S_kernel_to_input_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2033 void apply_S_kernel_to_input_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2034 void apply_S_kernel_from_right_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2035 void apply_S_kernel_from_right_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2036 void apply_T_kernel_to_input_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2037 void apply_T_kernel_to_input_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2038 void apply_T_kernel_from_right_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2039 void apply_T_kernel_from_right_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2040 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) { apply_SWAP_kernel_to_input_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2041 void apply_SWAP_kernel_to_input_tbb(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_to_input_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2042 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) { apply_SWAP_kernel_from_right_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2043 void apply_SWAP_kernel_from_right_tbb(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_from_right_tbb_impl(input, target_qbits, control_qbits, matrix_size); }
2044 void apply_SYC_kernel_to_input_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2045 void apply_SYC_kernel_to_input_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_to_input_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2046 void apply_SYC_kernel_from_right_tbb(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2047 void apply_SYC_kernel_from_right_tbb(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_from_right_tbb_impl(input, target_qbit, control_qbit, matrix_size); }
2048 
2049 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) { apply_X_kernel_to_input_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2050 void apply_X_kernel_to_input_omp(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_to_input_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2051 void apply_X_kernel_from_right_omp(Matrix& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_from_right_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2052 void apply_X_kernel_from_right_omp(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_X_kernel_from_right_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2053 void apply_Y_kernel_to_input_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2054 void apply_Y_kernel_to_input_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2055 void apply_Y_kernel_from_right_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2056 void apply_Y_kernel_from_right_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Y_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2057 void apply_Z_kernel_to_input_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2058 void apply_Z_kernel_to_input_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2059 void apply_Z_kernel_from_right_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2060 void apply_Z_kernel_from_right_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_Z_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2061 void apply_H_kernel_to_input_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2062 void apply_H_kernel_to_input_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2063 void apply_H_kernel_from_right_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2064 void apply_H_kernel_from_right_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_H_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2065 void apply_S_kernel_to_input_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2066 void apply_S_kernel_to_input_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2067 void apply_S_kernel_from_right_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2068 void apply_S_kernel_from_right_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_S_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2069 void apply_T_kernel_to_input_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2070 void apply_T_kernel_to_input_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2071 void apply_T_kernel_from_right_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2072 void apply_T_kernel_from_right_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_T_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2073 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) { apply_SWAP_kernel_to_input_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2074 void apply_SWAP_kernel_to_input_omp(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_to_input_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2075 void apply_SWAP_kernel_from_right_omp(Matrix& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_from_right_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2076 void apply_SWAP_kernel_from_right_omp(Matrix_float& input, const std::vector<int>& target_qbits, const std::vector<int>& control_qbits, const int& matrix_size) { apply_SWAP_kernel_from_right_omp_impl(input, target_qbits, control_qbits, matrix_size); }
2077 void apply_SYC_kernel_to_input_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2078 void apply_SYC_kernel_to_input_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_to_input_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2079 void apply_SYC_kernel_from_right_omp(Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
2080 void apply_SYC_kernel_from_right_omp(Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) { apply_SYC_kernel_from_right_omp_impl(input, target_qbit, control_qbit, matrix_size); }
void apply_T_kernel_to_input_omp(Matrix &input, 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_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.
void apply_X_kernel_from_right_tbb_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_SYC_kernel_from_right_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Y_kernel_from_right_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Y_kernel_from_right_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_from_right_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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_SYC_kernel_to_input_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_X_kernel_from_right_omp_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_Y_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Applies the Y gate kernel to the input matrix.
void apply_SWAP_kernel_to_input_omp_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_H_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_from_right_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Applies the S gate kernel to the input matrix.
void apply_Y_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Z_kernel_to_input_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_from_right_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SWAP_kernel_to_input_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_T_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, 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)
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)
void apply_Z_kernel_to_input_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_to_input_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_X_kernel_to_input_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
Call to apply X gate kernel on an input matrix.
void apply_Y_kernel_to_input_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_from_right_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Z_kernel_from_right_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SWAP_kernel_from_right_omp_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_SWAP_kernel_to_input_tbb_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_S_kernel_from_right_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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_SYC_kernel_from_right_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_to_input_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_to_input_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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_T_kernel_to_input_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Z_kernel_from_right_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_X_kernel_to_input_tbb_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_H_kernel_from_right_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_to_input_omp_impl(MatrixT &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_Z_kernel_from_right_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_to_input_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SWAP_kernel_from_right_tbb_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
matrix_size
[load Umtx]
Definition: example.py:58
void apply_Z_kernel_to_input_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_from_right_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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)
void apply_Z_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_from_right_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_to_input_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Y_kernel_from_right_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_to_input(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Applies the T gate kernel to the input matrix.
void apply_H_kernel_to_input_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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 apply_S_kernel_from_right_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Z_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_X_kernel_to_input_omp_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:136
Double-precision complex matrix (float64).
Definition: matrix.h:38
void apply_Y_kernel_to_input_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SWAP_kernel_from_right_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_SYC_kernel_from_right_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Y_kernel_from_right_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
void apply_Y_kernel_to_input_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_X_kernel_from_right_impl(MatrixT &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_X_kernel_from_right_omp(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_Y_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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)
void apply_T_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_from_right_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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_Z_kernel_from_right_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SWAP_kernel_from_right_omp(Matrix &input, const std::vector< int > &target_qbits, const std::vector< int > &control_qbits, const int &matrix_size)
void apply_Z_kernel_from_right(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Y_kernel_to_input_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_SYC_kernel_from_right_tbb(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_to_input_tbb_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_H_kernel_to_input_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_from_right_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_S_kernel_from_right_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
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)
typename std::remove_reference< decltype(std::declval< MatrixT & >()[0])>::type KernelDedicatedComplexT
void apply_T_kernel_to_input_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_T_kernel_from_right_omp_impl(MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_Y_kernel_to_input_omp(Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)