25 #include <immintrin.h> 46 __m128d* u3_1qubit_tmp = (__m128d*) & u3_1qbit[0];
47 __m256d u3_1qbit_00_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
49 u3_1qubit_tmp = (__m128d*) & u3_1qbit[1];
50 __m256d u3_1qbit_01_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
52 u3_1qubit_tmp = (__m128d*) & u3_1qbit[2];
53 __m256d u3_1qbit_10_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
55 u3_1qubit_tmp = (__m128d*) & u3_1qbit[3];
56 __m256d u3_1qbit_11_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
59 for (
int current_idx_pair=current_idx + index_step_target; current_idx_pair<
matrix_size; current_idx_pair=current_idx_pair+(index_step_target << 1) ) {
61 for (
int idx = 0; idx < index_step_target; idx++) {
64 int current_idx_loc = current_idx + idx;
65 int current_idx_pair_loc = current_idx_pair + idx;
67 int row_offset = current_idx_loc * input.
stride;
68 int row_offset_pair = current_idx_pair_loc * input.
stride;
70 if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
73 double* element = (
double*)input.
get_data() + 2 * row_offset;
74 double* element_pair = (
double*)input.
get_data() + 2 * row_offset_pair;
77 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
80 for (
int col_idx = 0; col_idx < 2 * (input.
cols - 1); col_idx = col_idx + 4) {
83 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
84 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
89 __m256d vec3 = _mm256_mul_pd(u3_1qbit_00_vec, element_vec);
92 __m256d element_vec_permuted = _mm256_permute_pd(element_vec, 0x5);
95 element_vec_permuted = _mm256_mul_pd(element_vec_permuted, neg);
98 __m256d vec4 = _mm256_mul_pd(u3_1qbit_00_vec, element_vec_permuted);
101 vec3 = _mm256_hsub_pd(vec3, vec4);
107 __m256d vec5 = _mm256_mul_pd(u3_1qbit_01_vec, element_pair_vec);
110 __m256d element_pair_vec_permuted = _mm256_permute_pd(element_pair_vec, 0x5);
113 element_pair_vec_permuted = _mm256_mul_pd(element_pair_vec_permuted, neg);
116 vec4 = _mm256_mul_pd(u3_1qbit_01_vec, element_pair_vec_permuted);
119 vec5 = _mm256_hsub_pd(vec5, vec4);
122 vec3 = _mm256_add_pd(vec3, vec5);
126 _mm256_storeu_pd(element + col_idx, vec3);
132 vec3 = _mm256_mul_pd(u3_1qbit_10_vec, element_vec);
135 vec4 = _mm256_mul_pd(u3_1qbit_10_vec, element_vec_permuted);
138 vec3 = _mm256_hsub_pd(vec3, vec4);
144 vec5 = _mm256_mul_pd(u3_1qbit_11_vec, element_pair_vec);
147 vec4 = _mm256_mul_pd(u3_1qbit_11_vec, element_pair_vec_permuted);
150 vec5 = _mm256_hsub_pd(vec5, vec4);
153 vec3 = _mm256_add_pd(vec3, vec5);
156 _mm256_storeu_pd(element_pair + col_idx, vec3);
160 if (input.
cols % 2 == 1) {
162 int col_idx = input.
cols - 1;
164 int index = row_offset + col_idx;
165 int index_pair = row_offset_pair + col_idx;
173 input[index].real = tmp1.
real + tmp2.
real;
174 input[index].imag = tmp1.
imag + tmp2.
imag;
176 tmp1 =
mult(u3_1qbit[2], element);
177 tmp2 =
mult(u3_1qbit[3], element_pair);
179 input[index_pair].real = tmp1.
real + tmp2.
real;
180 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
204 current_idx = current_idx + (index_step_target << 1);
219 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
220 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
221 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
227 const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].
real);
228 const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
229 const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
230 const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
231 const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
232 const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
233 const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
234 const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
236 for (
int current_idx_pair = current_idx + index_step_target; current_idx_pair <
matrix_size; current_idx_pair += (index_step_target << 1)) {
237 for (
int idx = 0; idx < index_step_target; idx++) {
238 const int current_idx_loc = current_idx + idx;
239 const int current_idx_pair_loc = current_idx_pair + idx;
240 const int row_offset = current_idx_loc * input.
stride;
241 const int row_offset_pair = current_idx_pair_loc * input.
stride;
243 if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
244 float* element = (
float*)input.
get_data() + 2 * row_offset;
245 float* element_pair = (
float*)input.
get_data() + 2 * row_offset_pair;
248 const int limit = 2 * input.
cols - 8;
249 for (; col_idx <= limit; col_idx += 8) {
250 const __m256 e = _mm256_loadu_ps(element + col_idx);
251 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
253 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u01r, u01i, p));
254 const __m256 out1 = _mm256_add_ps(cmul_ps(u10r, u10i, e), cmul_ps(u11r, u11i, p));
256 _mm256_storeu_ps(element + col_idx, out0);
257 _mm256_storeu_ps(element_pair + col_idx, out1);
260 for (
int c = col_idx / 2; c < input.
cols; ++c) {
261 const int index = row_offset + c;
262 const int index_pair = row_offset_pair + c;
269 input[index].real = tmp1.
real + tmp2.
real;
270 input[index].imag = tmp1.
imag + tmp2.
imag;
272 tmp1 =
mult(u3_1qbit[2], element_c);
273 tmp2 =
mult(u3_1qbit[3], element_pair_c);
274 input[index_pair].real = tmp1.
real + tmp2.
real;
275 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
282 current_idx += (index_step_target << 1);
293 const __m256d u00r = _mm256_broadcast_sd(&u3_1qbit[0].
real);
294 const __m256d u00i = _mm256_broadcast_sd(&u3_1qbit[0].imag);
295 const __m256d u01r = _mm256_broadcast_sd(&u3_1qbit[1].real);
296 const __m256d u01i = _mm256_broadcast_sd(&u3_1qbit[1].imag);
297 const __m256d u10r = _mm256_broadcast_sd(&u3_1qbit[2].real);
298 const __m256d u10i = _mm256_broadcast_sd(&u3_1qbit[2].imag);
299 const __m256d u11r = _mm256_broadcast_sd(&u3_1qbit[3].real);
300 const __m256d u11i = _mm256_broadcast_sd(&u3_1qbit[3].imag);
302 auto apply_pair_scalar = [&](
const int index,
const int index_pair) {
308 input[index].real = tmp1.
real + tmp2.
real;
309 input[index].imag = tmp1.
imag + tmp2.
imag;
311 tmp1 =
mult(u3_1qbit[1], element);
312 tmp2 =
mult(u3_1qbit[3], element_pair);
313 input[index_pair].real = tmp1.
real + tmp2.
real;
314 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
317 for (
int row_idx = 0; row_idx < input.
rows; ++row_idx) {
318 const int row_offset = row_idx * input.
stride;
319 double*
const row_data = (
double*)input.
get_data() + 2 * row_offset;
322 int current_idx_pair = index_step_target;
324 while (current_idx_pair < input.
cols) {
325 const bool mixed = (control_qbit >= 0 && control_qbit <
target_qbit);
326 const bool active = (control_qbit < 0)
327 || (control_qbit >= target_qbit && ((current_idx >> control_qbit) & 1));
329 if (!mixed && active) {
330 double* element = row_data + 2 * current_idx;
331 double* element_pair = row_data + 2 * current_idx_pair;
334 const int avx_limit = 2 * index_step_target - 8;
336 for (; col_idx <= avx_limit; col_idx += 8) {
337 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
338 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
339 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
340 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
343 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
344 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
345 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
346 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
347 element_pair_vec = tmp;
349 __m256d vec3 = _mm256_mul_pd(u00r, element_vec);
350 vec3 = _mm256_fnmadd_pd(u00i, element_vec2, vec3);
351 __m256d vec4 = _mm256_mul_pd(u10r, element_pair_vec);
352 vec4 = _mm256_fnmadd_pd(u10i, element_pair_vec2, vec4);
353 vec3 = _mm256_add_pd(vec3, vec4);
354 __m256d vec5 = _mm256_mul_pd(u00r, element_vec2);
355 vec5 = _mm256_fmadd_pd(u00i, element_vec, vec5);
356 __m256d vec6 = _mm256_mul_pd(u10r, element_pair_vec2);
357 vec6 = _mm256_fmadd_pd(u10i, element_pair_vec, vec6);
358 vec5 = _mm256_add_pd(vec5, vec6);
360 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
361 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
363 _mm256_storeu_pd(element + col_idx, vec3);
364 _mm256_storeu_pd(element + col_idx + 4, vec5);
366 __m256d vec7 = _mm256_mul_pd(u01r, element_vec);
367 vec7 = _mm256_fnmadd_pd(u01i, element_vec2, vec7);
368 __m256d vec8 = _mm256_mul_pd(u11r, element_pair_vec);
369 vec8 = _mm256_fnmadd_pd(u11i, element_pair_vec2, vec8);
370 vec7 = _mm256_add_pd(vec7, vec8);
371 __m256d vec9 = _mm256_mul_pd(u01r, element_vec2);
372 vec9 = _mm256_fmadd_pd(u01i, element_vec, vec9);
373 __m256d vec10 = _mm256_mul_pd(u11r, element_pair_vec2);
374 vec10 = _mm256_fmadd_pd(u11i, element_pair_vec, vec10);
375 vec9 = _mm256_add_pd(vec9, vec10);
377 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
378 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
380 _mm256_storeu_pd(element_pair + col_idx, vec7);
381 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
384 for (
int c = col_idx / 2; c < index_step_target; ++c) {
385 const int index = row_offset + current_idx + c;
386 const int index_pair = row_offset + current_idx_pair + c;
387 apply_pair_scalar(index, index_pair);
391 for (
int idx = 0; idx < index_step_target; ++idx) {
392 const int col = current_idx + idx;
393 if ((col >> control_qbit) & 1) {
394 const int index = row_offset + col;
395 const int index_pair = row_offset + current_idx_pair + idx;
396 apply_pair_scalar(index, index_pair);
401 current_idx += (index_step_target << 1);
402 current_idx_pair += (index_step_target << 1);
414 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
415 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
416 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
421 const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].
real);
422 const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
423 const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
424 const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
425 const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
426 const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
427 const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
428 const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
430 const float u00r_s = u3_1qbit[0].real;
431 const float u00i_s = u3_1qbit[0].imag;
432 const float u01r_s = u3_1qbit[1].real;
433 const float u01i_s = u3_1qbit[1].imag;
434 const float u10r_s = u3_1qbit[2].real;
435 const float u10i_s = u3_1qbit[2].imag;
436 const float u11r_s = u3_1qbit[3].real;
437 const float u11i_s = u3_1qbit[3].imag;
439 auto apply_pair_scalar = [&](
const int index,
const int index_pair) {
442 input[index].real = u00r_s * element.
real - u00i_s * element.
imag + u10r_s * element_pair.
real - u10i_s * element_pair.
imag;
443 input[index].imag = u00r_s * element.
imag + u00i_s * element.
real + u10r_s * element_pair.
imag + u10i_s * element_pair.
real;
444 input[index_pair].real = u01r_s * element.
real - u01i_s * element.
imag + u11r_s * element_pair.
real - u11i_s * element_pair.
imag;
445 input[index_pair].imag = u01r_s * element.
imag + u01i_s * element.
real + u11r_s * element_pair.
imag + u11i_s * element_pair.
real;
448 for (
int row_idx = 0; row_idx < input.
rows; ++row_idx) {
449 const int row_offset = row_idx * input.
stride;
450 float*
const row_data = (
float*)input.
get_data() + 2 * row_offset;
453 int current_idx_pair = index_step_target;
455 while (current_idx_pair < input.
cols) {
456 const bool mixed = (control_qbit >= 0 && control_qbit <
target_qbit);
457 const bool active = (control_qbit < 0)
458 || (control_qbit >= target_qbit && ((current_idx >> control_qbit) & 1));
460 if (!mixed && active) {
461 float* element = row_data + 2 * current_idx;
462 float* element_pair = row_data + 2 * current_idx_pair;
465 const int avx_limit = 2 * index_step_target - 8;
467 for (; col_idx <= avx_limit; col_idx += 8) {
468 const __m256 e = _mm256_loadu_ps(element + col_idx);
469 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
471 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
472 const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
474 _mm256_storeu_ps(element + col_idx, out0);
475 _mm256_storeu_ps(element_pair + col_idx, out1);
478 for (
int c = col_idx / 2; c < index_step_target; ++c) {
479 const int index = row_offset + current_idx + c;
480 const int index_pair = row_offset + current_idx_pair + c;
481 apply_pair_scalar(index, index_pair);
485 for (
int idx = 0; idx < index_step_target; ++idx) {
486 const int col = current_idx + idx;
487 if ((col >> control_qbit) & 1) {
488 const int index = row_offset + col;
489 const int index_pair = row_offset + current_idx_pair + idx;
490 apply_pair_scalar(index, index_pair);
495 current_idx += (index_step_target << 1);
496 current_idx_pair += (index_step_target << 1);
508 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
509 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
510 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
516 const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].
real);
517 const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
518 const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
519 const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
520 const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
521 const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
522 const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
523 const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
525 for (
int current_idx_pair = current_idx + index_step_target; current_idx_pair <
matrix_size; current_idx_pair += (index_step_target << 1)) {
526 for (
int idx = 0; idx < index_step_target; idx++) {
527 const int current_idx_loc = current_idx + idx;
528 const int current_idx_pair_loc = current_idx_pair + idx;
529 const int row_offset = current_idx_loc * input.
stride;
530 const int row_offset_pair = current_idx_pair_loc * input.
stride;
532 if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
533 float* element = (
float*)input.
get_data() + 2 * row_offset;
534 float* element_pair = (
float*)input.
get_data() + 2 * row_offset_pair;
537 const int limit = 2 * input.
cols - 8;
538 for (; col_idx <= limit; col_idx += 8) {
539 const __m256 e = _mm256_loadu_ps(element + col_idx);
540 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
542 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u01r, u01i, p));
543 const __m256 out1 = _mm256_add_ps(cmul_ps(u10r, u10i, e), cmul_ps(u11r, u11i, p));
545 _mm256_storeu_ps(element + col_idx, out0);
546 _mm256_storeu_ps(element_pair + col_idx, out1);
549 for (
int c = col_idx / 2; c < input.
cols; ++c) {
550 const int index = row_offset + c;
551 const int index_pair = row_offset_pair + c;
558 input[index].real = tmp1.
real + tmp2.
real;
559 input[index].imag = tmp1.
imag + tmp2.
imag;
561 tmp1 =
mult(u3_1qbit[2], element_c);
562 tmp2 =
mult(u3_1qbit[3], element_pair_c);
563 input[index_pair].real = tmp1.
real + tmp2.
real;
564 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
571 current_idx += (index_step_target << 1);
580 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
581 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
582 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
587 const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].
real);
588 const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
589 const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
590 const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
591 const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
592 const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
593 const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
594 const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
596 const int parallel_outer_cycles = matrix_size / (index_step_target << 1);
597 tbb::parallel_for(tbb::blocked_range<int>(0, parallel_outer_cycles, 8), [&](tbb::blocked_range<int> r) {
598 int current_idx = r.begin() * (index_step_target << 1);
599 int current_idx_pair = index_step_target + r.begin() * (index_step_target << 1);
601 for (
int rdx = r.begin(); rdx < r.end(); ++rdx) {
602 for (
int idx = 0; idx < index_step_target; ++idx) {
603 const int current_idx_loc = current_idx + idx;
604 const int current_idx_pair_loc = current_idx_pair + idx;
605 const int row_offset = current_idx_loc * input.
stride;
606 const int row_offset_pair = current_idx_pair_loc * input.
stride;
608 if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
609 float* element = (
float*)input.
get_data() + 2 * row_offset;
610 float* element_pair = (
float*)input.
get_data() + 2 * row_offset_pair;
613 const int limit = 2 * input.
cols - 8;
614 for (; col_idx <= limit; col_idx += 8) {
615 const __m256 e = _mm256_loadu_ps(element + col_idx);
616 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
618 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u01r, u01i, p));
619 const __m256 out1 = _mm256_add_ps(cmul_ps(u10r, u10i, e), cmul_ps(u11r, u11i, p));
621 _mm256_storeu_ps(element + col_idx, out0);
622 _mm256_storeu_ps(element_pair + col_idx, out1);
625 for (
int c = col_idx / 2; c < input.
cols; ++c) {
626 const int index = row_offset + c;
627 const int index_pair = row_offset_pair + c;
634 input[index].real = tmp1.
real + tmp2.
real;
635 input[index].imag = tmp1.
imag + tmp2.
imag;
637 tmp1 =
mult(u3_1qbit[2], element_c);
638 tmp2 =
mult(u3_1qbit[3], element_pair_c);
639 input[index_pair].real = tmp1.
real + tmp2.
real;
640 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
648 current_idx += (index_step_target << 1);
649 current_idx_pair += (index_step_target << 1);
674 __m256d u3_1bit_00r_vec = _mm256_broadcast_sd(&u3_1qbit[0].
real);
675 __m256d u3_1bit_00i_vec = _mm256_broadcast_sd(&u3_1qbit[0].imag);
676 __m256d u3_1bit_01r_vec = _mm256_broadcast_sd(&u3_1qbit[1].real);
677 __m256d u3_1bit_01i_vec = _mm256_broadcast_sd(&u3_1qbit[1].imag);
678 __m256d u3_1bit_10r_vec = _mm256_broadcast_sd(&u3_1qbit[2].real);
679 __m256d u3_1bit_10i_vec = _mm256_broadcast_sd(&u3_1qbit[2].imag);
680 __m256d u3_1bit_11r_vec = _mm256_broadcast_sd(&u3_1qbit[3].real);
681 __m256d u3_1bit_11i_vec = _mm256_broadcast_sd(&u3_1qbit[3].imag);
684 for (
int current_idx_pair=current_idx + index_step_target; current_idx_pair<
matrix_size; current_idx_pair=current_idx_pair+(index_step_target << 1) ) {
687 for (
int idx = 0; idx < index_step_target; idx++) {
690 int current_idx_loc = current_idx + idx;
691 int current_idx_pair_loc = current_idx_pair + idx;
693 int row_offset = current_idx_loc * input.
stride;
694 int row_offset_pair = current_idx_pair_loc * input.
stride;
696 if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
699 double* element = (
double*)input.
get_data() + 2 * row_offset;
700 double* element_pair = (
double*)input.
get_data() + 2 * row_offset_pair;
703 for (
int col_idx = 0; col_idx < 2 * (input.
cols - 3); col_idx = col_idx + 8) {
706 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
707 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
708 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
709 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
712 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
713 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
714 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
715 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
716 element_pair_vec = tmp;
718 __m256d vec3 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec);
719 vec3 = _mm256_fnmadd_pd(u3_1bit_00i_vec, element_vec2, vec3);
720 __m256d vec4 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec);
721 vec4 = _mm256_fnmadd_pd(u3_1bit_01i_vec, element_pair_vec2, vec4);
722 vec3 = _mm256_add_pd(vec3, vec4);
723 __m256d vec5 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec2);
724 vec5 = _mm256_fmadd_pd(u3_1bit_00i_vec, element_vec, vec5);
725 __m256d vec6 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec2);
726 vec6 = _mm256_fmadd_pd(u3_1bit_01i_vec, element_pair_vec, vec6);
727 vec5 = _mm256_add_pd(vec5, vec6);
730 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
731 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
733 _mm256_storeu_pd(element + col_idx, vec3);
734 _mm256_storeu_pd(element + col_idx + 4, vec5);
736 __m256d vec7 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec);
737 vec7 = _mm256_fnmadd_pd(u3_1bit_10i_vec, element_vec2, vec7);
738 __m256d vec8 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec);
739 vec8 = _mm256_fnmadd_pd(u3_1bit_11i_vec, element_pair_vec2, vec8);
740 vec7 = _mm256_add_pd(vec7, vec8);
741 __m256d vec9 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec2);
742 vec9 = _mm256_fmadd_pd(u3_1bit_10i_vec, element_vec, vec9);
743 __m256d vec10 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec2);
744 vec10 = _mm256_fmadd_pd(u3_1bit_11i_vec, element_pair_vec, vec10);
745 vec9 = _mm256_add_pd(vec9, vec10);
748 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
749 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
751 _mm256_storeu_pd(element_pair + col_idx, vec7);
752 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
755 int remainder = input.
cols % 4;
756 if (remainder != 0) {
758 for (
int col_idx = input.
cols-remainder; col_idx < input.
cols; col_idx++) {
759 int index = row_offset + col_idx;
760 int index_pair = row_offset_pair + col_idx;
768 input[index].real = tmp1.
real + tmp2.
real;
769 input[index].imag = tmp1.
imag + tmp2.
imag;
771 tmp1 =
mult(u3_1qbit[2], element);
772 tmp2 =
mult(u3_1qbit[3], element_pair);
774 input[index_pair].real = tmp1.
real + tmp2.
real;
775 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
799 current_idx = current_idx + (index_step_target << 1);
825 __m256d u3_1bit_00r_vec = _mm256_broadcast_sd(&u3_1qbit[0].
real);
826 __m256d u3_1bit_00i_vec = _mm256_broadcast_sd(&u3_1qbit[0].imag);
827 __m256d u3_1bit_01r_vec = _mm256_broadcast_sd(&u3_1qbit[1].real);
828 __m256d u3_1bit_01i_vec = _mm256_broadcast_sd(&u3_1qbit[1].imag);
829 __m256d u3_1bit_10r_vec = _mm256_broadcast_sd(&u3_1qbit[2].real);
830 __m256d u3_1bit_10i_vec = _mm256_broadcast_sd(&u3_1qbit[2].imag);
831 __m256d u3_1bit_11r_vec = _mm256_broadcast_sd(&u3_1qbit[3].real);
832 __m256d u3_1bit_11i_vec = _mm256_broadcast_sd(&u3_1qbit[3].imag);
836 int parallel_outer_cycles = matrix_size/(index_step_target << 1);
837 int outer_grain_size;
838 if ( index_step_target <= 2 ) {
839 outer_grain_size = 32;
841 else if ( index_step_target <= 4 ) {
842 outer_grain_size = 16;
844 else if ( index_step_target <= 8 ) {
845 outer_grain_size = 8;
847 else if ( index_step_target <= 16 ) {
848 outer_grain_size = 4;
851 outer_grain_size = 2;
855 tbb::parallel_for( tbb::blocked_range<int>(0,parallel_outer_cycles,outer_grain_size), [&](tbb::blocked_range<int> r) {
857 int current_idx = r.begin()*(index_step_target << 1);
858 int current_idx_pair = index_step_target + r.begin()*(index_step_target << 1);
860 for (
int rdx=r.begin(); rdx<r.end(); rdx++) {
863 tbb::parallel_for( tbb::blocked_range<int>(0,index_step_target,32), [&](tbb::blocked_range<int> r) {
864 for (
int idx=r.begin(); idx<r.end(); ++idx) {
867 int current_idx_loc = current_idx + idx;
868 int current_idx_pair_loc = current_idx_pair + idx;
870 int row_offset = current_idx_loc * input.
stride;
871 int row_offset_pair = current_idx_pair_loc * input.
stride;
873 if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
876 double* element = (
double*)input.
get_data() + 2 * row_offset;
877 double* element_pair = (
double*)input.
get_data() + 2 * row_offset_pair;
880 for (
int col_idx = 0; col_idx < 2 * (input.
cols - 3); col_idx = col_idx + 8) {
883 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
884 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
885 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
886 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
889 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
890 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
891 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
892 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
893 element_pair_vec = tmp;
895 __m256d vec3 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec);
896 vec3 = _mm256_fnmadd_pd(u3_1bit_00i_vec, element_vec2, vec3);
897 __m256d vec4 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec);
898 vec4 = _mm256_fnmadd_pd(u3_1bit_01i_vec, element_pair_vec2, vec4);
899 vec3 = _mm256_add_pd(vec3, vec4);
900 __m256d vec5 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec2);
901 vec5 = _mm256_fmadd_pd(u3_1bit_00i_vec, element_vec, vec5);
902 __m256d vec6 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec2);
903 vec6 = _mm256_fmadd_pd(u3_1bit_01i_vec, element_pair_vec, vec6);
904 vec5 = _mm256_add_pd(vec5, vec6);
907 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
908 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
910 _mm256_storeu_pd(element + col_idx, vec3);
911 _mm256_storeu_pd(element + col_idx + 4, vec5);
913 __m256d vec7 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec);
914 vec7 = _mm256_fnmadd_pd(u3_1bit_10i_vec, element_vec2, vec7);
915 __m256d vec8 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec);
916 vec8 = _mm256_fnmadd_pd(u3_1bit_11i_vec, element_pair_vec2, vec8);
917 vec7 = _mm256_add_pd(vec7, vec8);
918 __m256d vec9 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec2);
919 vec9 = _mm256_fmadd_pd(u3_1bit_10i_vec, element_vec, vec9);
920 __m256d vec10 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec2);
921 vec10 = _mm256_fmadd_pd(u3_1bit_11i_vec, element_pair_vec, vec10);
922 vec9 = _mm256_add_pd(vec9, vec10);
925 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
926 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
928 _mm256_storeu_pd(element_pair + col_idx, vec7);
929 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
932 int remainder = input.
cols % 4;
933 if (remainder != 0) {
935 for (
int col_idx = input.
cols-remainder; col_idx < input.
cols; col_idx++) {
936 int index = row_offset + col_idx;
937 int index_pair = row_offset_pair + col_idx;
945 input[index].real = tmp1.
real + tmp2.
real;
946 input[index].imag = tmp1.
imag + tmp2.
imag;
948 tmp1 =
mult(u3_1qbit[2], element);
949 tmp2 =
mult(u3_1qbit[3], element_pair);
951 input[index_pair].real = tmp1.
real + tmp2.
real;
952 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
977 current_idx = current_idx + (index_step_target << 1);
978 current_idx_pair = current_idx_pair + (index_step_target << 1);
1023 const __m256d u00r = _mm256_broadcast_sd(&u3_1qbit[0].
real);
1024 const __m256d u00i = _mm256_broadcast_sd(&u3_1qbit[0].imag);
1025 const __m256d u01r = _mm256_broadcast_sd(&u3_1qbit[1].real);
1026 const __m256d u01i = _mm256_broadcast_sd(&u3_1qbit[1].imag);
1027 const __m256d u10r = _mm256_broadcast_sd(&u3_1qbit[2].real);
1028 const __m256d u10i = _mm256_broadcast_sd(&u3_1qbit[2].imag);
1029 const __m256d u11r = _mm256_broadcast_sd(&u3_1qbit[3].real);
1030 const __m256d u11i = _mm256_broadcast_sd(&u3_1qbit[3].imag);
1032 for (
int row_idx = 0; row_idx < input.
rows; row_idx++) {
1034 const int row_offset = row_idx * input.
stride;
1035 double*
const row_data = (
double*)input.
get_data() + 2 * row_offset;
1037 int current_idx = 0;
1038 int current_idx_pair = index_step_target;
1040 while (current_idx_pair < input.
cols) {
1045 const bool mixed = (control_qbit >= 0 && control_qbit <
target_qbit);
1046 const bool active = (control_qbit < 0) ||
1047 (control_qbit >= target_qbit &&
1048 ((current_idx >> control_qbit) & 1));
1050 if (!mixed && !active) {
1052 }
else if (!mixed) {
1054 double* element = row_data + 2 * current_idx;
1055 double* element_pair = row_data + 2 * current_idx_pair;
1058 const int avx_limit = 2 * (index_step_target - 3);
1060 for (; col_idx < avx_limit; col_idx += 8) {
1062 __m256d e_vec = _mm256_loadu_pd(element + col_idx);
1063 __m256d e_vec2 = _mm256_loadu_pd(element + col_idx + 4);
1064 __m256d tmp = _mm256_shuffle_pd(e_vec, e_vec2, 0);
1065 e_vec2 = _mm256_shuffle_pd(e_vec, e_vec2, 0xf);
1068 __m256d p_vec = _mm256_loadu_pd(element_pair + col_idx);
1069 __m256d p_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
1070 tmp = _mm256_shuffle_pd(p_vec, p_vec2, 0);
1071 p_vec2 = _mm256_shuffle_pd(p_vec, p_vec2, 0xf);
1075 __m256d vec3 = _mm256_mul_pd(u00r, e_vec);
1076 vec3 = _mm256_fnmadd_pd(u00i, e_vec2, vec3);
1077 __m256d vec4 = _mm256_mul_pd(u10r, p_vec);
1078 vec4 = _mm256_fnmadd_pd(u10i, p_vec2, vec4);
1079 vec3 = _mm256_add_pd(vec3, vec4);
1080 __m256d vec5 = _mm256_mul_pd(u00r, e_vec2);
1081 vec5 = _mm256_fmadd_pd(u00i, e_vec, vec5);
1082 __m256d vec6 = _mm256_mul_pd(u10r, p_vec2);
1083 vec6 = _mm256_fmadd_pd(u10i, p_vec, vec6);
1084 vec5 = _mm256_add_pd(vec5, vec6);
1086 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
1087 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
1089 _mm256_storeu_pd(element + col_idx, vec3);
1090 _mm256_storeu_pd(element + col_idx + 4, vec5);
1093 __m256d vec7 = _mm256_mul_pd(u01r, e_vec);
1094 vec7 = _mm256_fnmadd_pd(u01i, e_vec2, vec7);
1095 __m256d vec8 = _mm256_mul_pd(u11r, p_vec);
1096 vec8 = _mm256_fnmadd_pd(u11i, p_vec2, vec8);
1097 vec7 = _mm256_add_pd(vec7, vec8);
1098 __m256d vec9 = _mm256_mul_pd(u01r, e_vec2);
1099 vec9 = _mm256_fmadd_pd(u01i, e_vec, vec9);
1100 __m256d vec10 = _mm256_mul_pd(u11r, p_vec2);
1101 vec10 = _mm256_fmadd_pd(u11i, p_vec, vec10);
1102 vec9 = _mm256_add_pd(vec9, vec10);
1104 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
1105 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
1107 _mm256_storeu_pd(element_pair + col_idx, vec7);
1108 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
1112 for (
int c = col_idx / 2; c < index_step_target; c++) {
1113 const int index = row_offset + current_idx + c;
1114 const int index_pair = row_offset + current_idx_pair + c;
1119 input[index].real = t1.
real + t2.
real;
1120 input[index].imag = t1.
imag + t2.
imag;
1121 t1 =
mult(u3_1qbit[1], e);
1122 t2 =
mult(u3_1qbit[3], p);
1123 input[index_pair].real = t1.
real + t2.
real;
1124 input[index_pair].imag = t1.
imag + t2.
imag;
1129 for (
int idx = 0; idx < index_step_target; idx++) {
1130 const int col = current_idx + idx;
1131 const int col_pair = current_idx_pair + idx;
1132 if ((col >> control_qbit) & 1) {
1133 const int index = row_offset + col;
1134 const int index_pair = row_offset + col_pair;
1139 input[index].real = t1.
real + t2.
real;
1140 input[index].imag = t1.
imag + t2.
imag;
1141 t1 =
mult(u3_1qbit[1], e);
1142 t2 =
mult(u3_1qbit[3], p);
1143 input[index_pair].real = t1.
real + t2.
real;
1144 input[index_pair].imag = t1.
imag + t2.
imag;
1149 current_idx += (index_step_target << 1);
1150 current_idx_pair += (index_step_target << 1);
1169 const __m256d u00r = _mm256_broadcast_sd(&u3_1qbit[0].
real);
1170 const __m256d u00i = _mm256_broadcast_sd(&u3_1qbit[0].imag);
1171 const __m256d u01r = _mm256_broadcast_sd(&u3_1qbit[1].real);
1172 const __m256d u01i = _mm256_broadcast_sd(&u3_1qbit[1].imag);
1173 const __m256d u10r = _mm256_broadcast_sd(&u3_1qbit[2].real);
1174 const __m256d u10i = _mm256_broadcast_sd(&u3_1qbit[2].imag);
1175 const __m256d u11r = _mm256_broadcast_sd(&u3_1qbit[3].real);
1176 const __m256d u11i = _mm256_broadcast_sd(&u3_1qbit[3].imag);
1178 const int grain = (input.
rows < 64) ? 1 : 8;
1180 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, grain),
1181 [&](tbb::blocked_range<int> r) {
1183 for (int row_idx = r.begin(); row_idx < r.end(); row_idx++) {
1185 const int row_offset = row_idx * input.stride;
1186 double* const row_data = (double*)input.get_data() + 2 * row_offset;
1188 int current_idx = 0;
1189 int current_idx_pair = index_step_target;
1191 while (current_idx_pair < input.cols) {
1193 const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
1194 const bool active = (control_qbit < 0) ||
1195 (control_qbit >= target_qbit &&
1196 ((current_idx >> control_qbit) & 1));
1198 if (!mixed && !active) {
1200 } else if (!mixed) {
1201 double* element = row_data + 2 * current_idx;
1202 double* element_pair = row_data + 2 * current_idx_pair;
1205 const int avx_limit = 2 * (index_step_target - 3);
1207 for (; col_idx < avx_limit; col_idx += 8) {
1209 __m256d e_vec = _mm256_loadu_pd(element + col_idx);
1210 __m256d e_vec2 = _mm256_loadu_pd(element + col_idx + 4);
1211 __m256d tmp = _mm256_shuffle_pd(e_vec, e_vec2, 0);
1212 e_vec2 = _mm256_shuffle_pd(e_vec, e_vec2, 0xf);
1215 __m256d p_vec = _mm256_loadu_pd(element_pair + col_idx);
1216 __m256d p_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
1217 tmp = _mm256_shuffle_pd(p_vec, p_vec2, 0);
1218 p_vec2 = _mm256_shuffle_pd(p_vec, p_vec2, 0xf);
1221 __m256d vec3 = _mm256_mul_pd(u00r, e_vec);
1222 vec3 = _mm256_fnmadd_pd(u00i, e_vec2, vec3);
1223 __m256d vec4 = _mm256_mul_pd(u10r, p_vec);
1224 vec4 = _mm256_fnmadd_pd(u10i, p_vec2, vec4);
1225 vec3 = _mm256_add_pd(vec3, vec4);
1226 __m256d vec5 = _mm256_mul_pd(u00r, e_vec2);
1227 vec5 = _mm256_fmadd_pd(u00i, e_vec, vec5);
1228 __m256d vec6 = _mm256_mul_pd(u10r, p_vec2);
1229 vec6 = _mm256_fmadd_pd(u10i, p_vec, vec6);
1230 vec5 = _mm256_add_pd(vec5, vec6);
1232 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
1233 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
1235 _mm256_storeu_pd(element + col_idx, vec3);
1236 _mm256_storeu_pd(element + col_idx + 4, vec5);
1238 __m256d vec7 = _mm256_mul_pd(u01r, e_vec);
1239 vec7 = _mm256_fnmadd_pd(u01i, e_vec2, vec7);
1240 __m256d vec8 = _mm256_mul_pd(u11r, p_vec);
1241 vec8 = _mm256_fnmadd_pd(u11i, p_vec2, vec8);
1242 vec7 = _mm256_add_pd(vec7, vec8);
1243 __m256d vec9 = _mm256_mul_pd(u01r, e_vec2);
1244 vec9 = _mm256_fmadd_pd(u01i, e_vec, vec9);
1245 __m256d vec10 = _mm256_mul_pd(u11r, p_vec2);
1246 vec10 = _mm256_fmadd_pd(u11i, p_vec, vec10);
1247 vec9 = _mm256_add_pd(vec9, vec10);
1249 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
1250 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
1252 _mm256_storeu_pd(element_pair + col_idx, vec7);
1253 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
1256 for (int c = col_idx / 2; c < index_step_target; c++) {
1257 const int index = row_offset + current_idx + c;
1258 const int index_pair = row_offset + current_idx_pair + c;
1259 QGD_Complex16 e = input[index];
1260 QGD_Complex16 p = input[index_pair];
1261 QGD_Complex16 t1 = mult(u3_1qbit[0], e);
1262 QGD_Complex16 t2 = mult(u3_1qbit[2], p);
1263 input[index].real = t1.real + t2.real;
1264 input[index].imag = t1.imag + t2.imag;
1265 t1 = mult(u3_1qbit[1], e);
1266 t2 = mult(u3_1qbit[3], p);
1267 input[index_pair].real = t1.real + t2.real;
1268 input[index_pair].imag = t1.imag + t2.imag;
1272 for (int idx = 0; idx < index_step_target; idx++) {
1273 const int col = current_idx + idx;
1274 const int col_pair = current_idx_pair + idx;
1275 if ((col >> control_qbit) & 1) {
1276 const int index = row_offset + col;
1277 const int index_pair = row_offset + col_pair;
1278 QGD_Complex16 e = input[index];
1279 QGD_Complex16 p = input[index_pair];
1280 QGD_Complex16 t1 = mult(u3_1qbit[0], e);
1281 QGD_Complex16 t2 = mult(u3_1qbit[2], p);
1282 input[index].real = t1.real + t2.real;
1283 input[index].imag = t1.imag + t2.imag;
1284 t1 = mult(u3_1qbit[1], e);
1285 t2 = mult(u3_1qbit[3], p);
1286 input[index_pair].real = t1.real + t2.real;
1287 input[index_pair].imag = t1.imag + t2.imag;
1292 current_idx += (index_step_target << 1);
1293 current_idx_pair += (index_step_target << 1);
1311 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
1312 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
1313 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
1319 const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].
real);
1320 const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
1321 const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
1322 const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
1323 const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
1324 const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
1325 const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
1326 const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
1328 const float u00r_s = u3_1qbit[0].real;
1329 const float u00i_s = u3_1qbit[0].imag;
1330 const float u01r_s = u3_1qbit[1].real;
1331 const float u01i_s = u3_1qbit[1].imag;
1332 const float u10r_s = u3_1qbit[2].real;
1333 const float u10i_s = u3_1qbit[2].imag;
1334 const float u11r_s = u3_1qbit[3].real;
1335 const float u11i_s = u3_1qbit[3].imag;
1337 auto apply_pair_scalar = [&](
const int index,
const int index_pair) {
1340 input[index].real = u00r_s * e.
real - u00i_s * e.
imag + u10r_s * p.
real - u10i_s * p.
imag;
1341 input[index].imag = u00r_s * e.
imag + u00i_s * e.
real + u10r_s * p.
imag + u10i_s * p.
real;
1342 input[index_pair].real = u01r_s * e.
real - u01i_s * e.
imag + u11r_s * p.
real - u11i_s * p.
imag;
1343 input[index_pair].imag = u01r_s * e.
imag + u01i_s * e.
real + u11r_s * p.
imag + u11i_s * p.
real;
1346 for (
int row_idx = 0; row_idx < input.
rows; row_idx++) {
1348 const int row_offset = row_idx * input.
stride;
1349 float*
const row_data = (
float*)input.
get_data() + 2 * row_offset;
1351 int current_idx = 0;
1352 int current_idx_pair = index_step_target;
1354 while (current_idx_pair < input.
cols) {
1356 const bool mixed = (control_qbit >= 0 && control_qbit <
target_qbit);
1357 const bool active = (control_qbit < 0) ||
1358 (control_qbit >= target_qbit &&
1359 ((current_idx >> control_qbit) & 1));
1361 if (!mixed && !active) {
1363 }
else if (!mixed) {
1364 float* element = row_data + 2 * current_idx;
1365 float* element_pair = row_data + 2 * current_idx_pair;
1368 const int avx_limit = 2 * index_step_target - 8;
1370 for (; col_idx <= avx_limit; col_idx += 8) {
1371 const __m256 e = _mm256_loadu_ps(element + col_idx);
1372 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
1374 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
1376 const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
1377 _mm256_storeu_ps(element + col_idx, out0);
1378 _mm256_storeu_ps(element_pair + col_idx, out1);
1381 for (
int c = col_idx / 2; c < index_step_target; c++) {
1382 const int index = row_offset + current_idx + c;
1383 const int index_pair = row_offset + current_idx_pair + c;
1384 apply_pair_scalar(index, index_pair);
1388 for (
int idx = 0; idx < index_step_target; idx++) {
1389 const int col = current_idx + idx;
1390 const int col_pair = current_idx_pair + idx;
1391 if ((col >> control_qbit) & 1) {
1392 const int index = row_offset + col;
1393 const int index_pair = row_offset + col_pair;
1394 apply_pair_scalar(index, index_pair);
1399 current_idx += (index_step_target << 1);
1400 current_idx_pair += (index_step_target << 1);
1417 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
1418 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
1419 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
1424 const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].
real);
1425 const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
1426 const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
1427 const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
1428 const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
1429 const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
1430 const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
1431 const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
1433 const float u00r_s = u3_1qbit[0].real;
1434 const float u00i_s = u3_1qbit[0].imag;
1435 const float u01r_s = u3_1qbit[1].real;
1436 const float u01i_s = u3_1qbit[1].imag;
1437 const float u10r_s = u3_1qbit[2].real;
1438 const float u10i_s = u3_1qbit[2].imag;
1439 const float u11r_s = u3_1qbit[3].real;
1440 const float u11i_s = u3_1qbit[3].imag;
1442 const int grain = (input.
rows < 64) ? 1 : 8;
1444 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, grain),
1445 [&](tbb::blocked_range<int> r) {
1447 for (int row_idx = r.begin(); row_idx < r.end(); row_idx++) {
1449 const int row_offset = row_idx * input.stride;
1450 float* const row_data = (float*)input.get_data() + 2 * row_offset;
1452 auto apply_pair_scalar = [&](const int index, const int index_pair) {
1453 const QGD_Complex8 e = input[index];
1454 const QGD_Complex8 p = input[index_pair];
1455 input[index].real = u00r_s * e.real - u00i_s * e.imag + u10r_s * p.real - u10i_s * p.imag;
1456 input[index].imag = u00r_s * e.imag + u00i_s * e.real + u10r_s * p.imag + u10i_s * p.real;
1457 input[index_pair].real = u01r_s * e.real - u01i_s * e.imag + u11r_s * p.real - u11i_s * p.imag;
1458 input[index_pair].imag = u01r_s * e.imag + u01i_s * e.real + u11r_s * p.imag + u11i_s * p.real;
1461 int current_idx = 0;
1462 int current_idx_pair = index_step_target;
1464 while (current_idx_pair < input.cols) {
1466 const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
1467 const bool active = (control_qbit < 0) ||
1468 (control_qbit >= target_qbit &&
1469 ((current_idx >> control_qbit) & 1));
1471 if (!mixed && !active) {
1473 } else if (!mixed) {
1474 float* element = row_data + 2 * current_idx;
1475 float* element_pair = row_data + 2 * current_idx_pair;
1478 const int avx_limit = 2 * index_step_target - 8;
1480 for (; col_idx <= avx_limit; col_idx += 8) {
1481 const __m256 e = _mm256_loadu_ps(element + col_idx);
1482 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
1483 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
1484 const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
1485 _mm256_storeu_ps(element + col_idx, out0);
1486 _mm256_storeu_ps(element_pair + col_idx, out1);
1489 for (int c = col_idx / 2; c < index_step_target; c++) {
1490 const int index = row_offset + current_idx + c;
1491 const int index_pair = row_offset + current_idx_pair + c;
1492 apply_pair_scalar(index, index_pair);
1496 for (int idx = 0; idx < index_step_target; idx++) {
1497 const int col = current_idx + idx;
1498 const int col_pair = current_idx_pair + idx;
1499 if ((col >> control_qbit) & 1) {
1500 const int index = row_offset + col;
1501 const int index_pair = row_offset + col_pair;
1502 apply_pair_scalar(index, index_pair);
1507 current_idx += (index_step_target << 1);
1508 current_idx_pair += (index_step_target << 1);
int stride
The column stride of the array. (The array elements in one row are a_0, a_1, ... a_{cols-1}, 0, 0, 0, 0. The number of zeros is stride-cols)
Structure type representing single-precision complex numbers.
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
scalar * get_data() const
Call to get the pointer to the stored data.
int rows
The number of rows.
int cols
The number of columns.
Structure type representing complex numbers in the SQUANDER package.
Double-precision complex matrix (float64).
Single-precision complex matrix (float32).
double real
the real part of a complex number
double imag
the imaginary part of a complex number