32 void apply_2qbit_kernel_to_matrix_input_AVX(
Matrix& two_qbit_unitary,
Matrix& input,
const std::vector<int>& involved_qbits,
const int&
matrix_size);
33 void apply_2qbit_kernel_to_matrix_input_parallel_AVX_TBB(
Matrix& two_qbit_unitary,
Matrix& input,
const std::vector<int>& involved_qbits,
const int&
matrix_size);
45 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
46 element_outer_vec = _mm256_permute4x64_pd(element_outer_vec,0b11011000);
47 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
48 element_inner_vec = _mm256_permute4x64_pd(element_inner_vec,0b11011000);
49 __m256d outer_inner_vec = _mm256_shuffle_pd(element_outer_vec,element_inner_vec,0b0000);
50 outer_inner_vec = _mm256_permute4x64_pd(outer_inner_vec,0b11011000);
52 return outer_inner_vec;
62 inline __m256d
complex_mult_AVX(__m256d input_vec, __m256d unitary_row_vec, __m256d neg){
64 __m256d vec3 = _mm256_mul_pd(input_vec, unitary_row_vec);
65 __m256d unitary_row_switched = _mm256_permute_pd(unitary_row_vec, 0x5);
66 unitary_row_switched = _mm256_mul_pd(unitary_row_switched, neg);
67 __m256d vec4 = _mm256_mul_pd(input_vec, unitary_row_switched);
68 __m256d result_vec = _mm256_hsub_pd(vec3, vec4);
69 result_vec = _mm256_permute4x64_pd(result_vec,0b11011000);
92 switch(involved_qbits.size()){
112 switch (involved_qbits.size()) {
114 apply_2qbit_kernel_to_matrix_input_AVX(unitary, input, involved_qbits, matrix_size);
147 switch(involved_qbits.size()){
167 switch (involved_qbits.size()) {
195 void apply_2qbit_kernel_to_matrix_input_AVX(
Matrix& two_qbit_unitary,
Matrix& input,
const std::vector<int>& involved_qbits,
const int& matrix_size) {
196 int inner_qbit = involved_qbits[0];
197 int outer_qbit = involved_qbits[1];
198 int index_step_outer = 1 << outer_qbit;
199 int index_step_inner = 1 << inner_qbit;
201 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
203 for (
int current_idx_pair_outer = current_idx + index_step_outer; current_idx_pair_outer < input.
rows; current_idx_pair_outer += (index_step_outer << 1)) {
204 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner += (index_step_inner << 1)) {
205 for (
int idx = 0; idx < index_step_inner; idx++) {
206 int current_idx_outer_loc = current_idx + current_idx_inner + idx;
207 int current_idx_inner_loc = current_idx + current_idx_inner + idx + index_step_inner;
208 int current_idx_outer_pair_loc = current_idx_pair_outer + idx + current_idx_inner;
209 int current_idx_inner_pair_loc = current_idx_pair_outer + idx + current_idx_inner + index_step_inner;
211 int row_offset_outer = current_idx_outer_loc * input.
cols;
212 int row_offset_inner = current_idx_inner_loc * input.
cols;
213 int row_offset_outer_pair = current_idx_outer_pair_loc * input.
cols;
214 int row_offset_inner_pair = current_idx_inner_pair_loc * input.
cols;
216 for (
int col_idx = 0; col_idx < input.
cols; col_idx++) {
217 int current_idx_outer = row_offset_outer + col_idx;
218 int current_idx_inner = row_offset_inner + col_idx;
219 int current_idx_outer_pair = row_offset_outer_pair + col_idx;
220 int current_idx_inner_pair = row_offset_inner_pair + col_idx;
222 double results[8] = {0., 0., 0., 0., 0., 0., 0., 0.};
224 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer;
225 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner;
226 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair;
227 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair;
229 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
230 element_outer_vec = _mm256_permute4x64_pd(element_outer_vec, 0b11011000);
231 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
232 element_inner_vec = _mm256_permute4x64_pd(element_inner_vec, 0b11011000);
233 __m256d outer_inner_vec = _mm256_shuffle_pd(element_outer_vec, element_inner_vec, 0b0000);
234 outer_inner_vec = _mm256_permute4x64_pd(outer_inner_vec, 0b11011000);
236 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
237 element_outer_pair_vec = _mm256_permute4x64_pd(element_outer_pair_vec, 0b11011000);
238 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
239 element_inner_pair_vec = _mm256_permute4x64_pd(element_inner_pair_vec, 0b11011000);
240 __m256d outer_inner_pair_vec = _mm256_shuffle_pd(element_outer_pair_vec, element_inner_pair_vec, 0b0000);
241 outer_inner_pair_vec = _mm256_permute4x64_pd(outer_inner_pair_vec, 0b11011000);
243 for (
int mult_idx = 0; mult_idx < 4; mult_idx++) {
244 double* unitary_row_01 = (
double*)two_qbit_unitary.
get_data() + 8 * mult_idx;
245 double* unitary_row_23 = (
double*)two_qbit_unitary.
get_data() + 8 * mult_idx + 4;
247 __m256d unitary_row_01_vec = _mm256_loadu_pd(unitary_row_01);
248 __m256d unitary_row_23_vec = _mm256_loadu_pd(unitary_row_23);
250 __m256d result_upper_vec =
complex_mult_AVX(outer_inner_vec, unitary_row_01_vec, neg);
251 __m256d result_lower_vec =
complex_mult_AVX(outer_inner_pair_vec, unitary_row_23_vec, neg);
253 __m256d result_vec = _mm256_hadd_pd(result_upper_vec, result_lower_vec);
254 result_vec = _mm256_hadd_pd(result_vec, result_vec);
255 double*
result = (
double*)&result_vec;
256 results[mult_idx * 2] = result[0];
257 results[mult_idx * 2 + 1] = result[2];
260 input[current_idx_outer].real = results[0];
261 input[current_idx_outer].imag = results[1];
262 input[current_idx_inner].real = results[2];
263 input[current_idx_inner].imag = results[3];
264 input[current_idx_outer_pair].real = results[4];
265 input[current_idx_outer_pair].imag = results[5];
266 input[current_idx_inner_pair].real = results[6];
267 input[current_idx_inner_pair].imag = results[7];
272 current_idx = current_idx + (index_step_outer << 1);
278 void apply_2qbit_kernel_to_matrix_input_parallel_AVX_TBB(
Matrix& two_qbit_unitary,
Matrix& input,
const std::vector<int>& involved_qbits,
const int& matrix_size) {
279 int inner_qbit = involved_qbits[0];
280 int outer_qbit = involved_qbits[1];
281 int index_step_outer = 1 << outer_qbit;
282 int index_step_inner = 1 << inner_qbit;
284 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
286 for (
int current_idx_pair_outer = current_idx + index_step_outer; current_idx_pair_outer < input.
rows; current_idx_pair_outer += (index_step_outer << 1)) {
287 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner += (index_step_inner << 1)) {
288 for (
int idx = 0; idx < index_step_inner; idx++) {
289 int current_idx_outer_loc = current_idx + current_idx_inner + idx;
290 int current_idx_inner_loc = current_idx + current_idx_inner + idx + index_step_inner;
291 int current_idx_outer_pair_loc = current_idx_pair_outer + idx + current_idx_inner;
292 int current_idx_inner_pair_loc = current_idx_pair_outer + idx + current_idx_inner + index_step_inner;
294 int row_offset_outer = current_idx_outer_loc * input.
cols;
295 int row_offset_inner = current_idx_inner_loc * input.
cols;
296 int row_offset_outer_pair = current_idx_outer_pair_loc * input.
cols;
297 int row_offset_inner_pair = current_idx_inner_pair_loc * input.
cols;
299 tbb::parallel_for(tbb::blocked_range<int>(0, input.
cols, 32), [&](
const tbb::blocked_range<int>& range) {
300 for (int col_idx = range.begin(); col_idx < range.end(); ++col_idx) {
301 int current_idx_outer = row_offset_outer + col_idx;
302 int current_idx_inner = row_offset_inner + col_idx;
303 int current_idx_outer_pair = row_offset_outer_pair + col_idx;
304 int current_idx_inner_pair = row_offset_inner_pair + col_idx;
306 double results[8] = {0., 0., 0., 0., 0., 0., 0., 0.};
308 double* element_outer = (double*)input.get_data() + 2 * current_idx_outer;
309 double* element_inner = (double*)input.get_data() + 2 * current_idx_inner;
310 double* element_outer_pair = (double*)input.get_data() + 2 * current_idx_outer_pair;
311 double* element_inner_pair = (double*)input.get_data() + 2 * current_idx_inner_pair;
313 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
314 element_outer_vec = _mm256_permute4x64_pd(element_outer_vec, 0b11011000);
315 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
316 element_inner_vec = _mm256_permute4x64_pd(element_inner_vec, 0b11011000);
317 __m256d outer_inner_vec = _mm256_shuffle_pd(element_outer_vec, element_inner_vec, 0b0000);
318 outer_inner_vec = _mm256_permute4x64_pd(outer_inner_vec, 0b11011000);
320 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
321 element_outer_pair_vec = _mm256_permute4x64_pd(element_outer_pair_vec, 0b11011000);
322 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
323 element_inner_pair_vec = _mm256_permute4x64_pd(element_inner_pair_vec, 0b11011000);
324 __m256d outer_inner_pair_vec = _mm256_shuffle_pd(element_outer_pair_vec, element_inner_pair_vec, 0b0000);
325 outer_inner_pair_vec = _mm256_permute4x64_pd(outer_inner_pair_vec, 0b11011000);
327 for (int mult_idx = 0; mult_idx < 4; mult_idx++) {
328 double* unitary_row_01 = (double*)two_qbit_unitary.get_data() + 8 * mult_idx;
329 double* unitary_row_23 = (double*)two_qbit_unitary.get_data() + 8 * mult_idx + 4;
331 __m256d unitary_row_01_vec = _mm256_loadu_pd(unitary_row_01);
332 __m256d unitary_row_23_vec = _mm256_loadu_pd(unitary_row_23);
334 __m256d result_upper_vec = complex_mult_AVX(outer_inner_vec, unitary_row_01_vec, neg);
335 __m256d result_lower_vec = complex_mult_AVX(outer_inner_pair_vec, unitary_row_23_vec, neg);
337 __m256d result_vec = _mm256_hadd_pd(result_upper_vec, result_lower_vec);
338 result_vec = _mm256_hadd_pd(result_vec, result_vec);
339 double* result = (double*)&result_vec;
340 results[mult_idx * 2] = result[0];
341 results[mult_idx * 2 + 1] = result[2];
344 input[current_idx_outer].real = results[0];
345 input[current_idx_outer].imag = results[1];
346 input[current_idx_inner].real = results[2];
347 input[current_idx_inner].imag = results[3];
348 input[current_idx_outer_pair].real = results[4];
349 input[current_idx_outer_pair].imag = results[5];
350 input[current_idx_inner_pair].real = results[6];
351 input[current_idx_inner_pair].imag = results[7];
357 current_idx = current_idx + (index_step_outer << 1);
375 switch(involved_qbits.size()){
395 switch (involved_qbits.size()) {
397 apply_2qbit_kernel_to_matrix_input_parallel_AVX_TBB(unitary, input, involved_qbits, matrix_size);
427 const std::vector<int>& non_targets,
428 std::vector<int>& block_pattern) {
429 int block_size = 1 << target_qubits.size();
431 for (
int k = 0;
k < block_size; ++
k) {
433 for (
size_t bit = 0; bit < target_qubits.size(); ++bit) {
434 if (
k & (1 << bit)) {
435 idx |= (1 << target_qubits[bit]);
438 block_pattern[
k] = idx;
452 const std::vector<int>& target_qubits,
453 const std::vector<int>& non_targets,
454 const std::vector<int>& block_pattern,
455 std::vector<int>& indices) {
457 for (
size_t i = 0; i < non_targets.size(); ++i) {
458 if (iter_idx & (1ULL << i)) {
459 base |= (1 << non_targets[i]);
462 for (
size_t k = 0;
k < block_pattern.size(); ++
k) {
463 indices[
k] = base | block_pattern[
k];
484 __m256d* mv_xy = (__m256d*) _mm_malloc(
sizeof(__m256d) * matrix_size *
matrix_size, 32);
490 mv_xy[rdx * matrix_size + cdx] = _mm256_set_pd(
491 -gate_kernel_unitary[matrix_size*rdx+cdx+1].imag,
492 gate_kernel_unitary[matrix_size*rdx+cdx+1].
real,
493 -gate_kernel_unitary[matrix_size*rdx+cdx].imag,
494 gate_kernel_unitary[matrix_size*rdx+cdx].
real 497 mv_xy[rdx * matrix_size + cdx + 1] = _mm256_set_pd(
498 gate_kernel_unitary[matrix_size*rdx+cdx+1].real,
499 gate_kernel_unitary[matrix_size*rdx+cdx+1].imag,
500 gate_kernel_unitary[matrix_size*rdx+cdx].real,
501 gate_kernel_unitary[matrix_size*rdx+cdx].imag
517 int inner_qbit = involved_qbits[0];
518 int outer_qbit = involved_qbits[1];
519 int index_step_outer = 1 << outer_qbit;
520 int index_step_inner = 1 << inner_qbit;
541 __m256d mv00 = _mm256_set_pd(-two_qbit_unitary[1].imag, two_qbit_unitary[1].
real, -two_qbit_unitary[0].imag, two_qbit_unitary[0].real);
542 __m256d mv01 = _mm256_set_pd( two_qbit_unitary[1].real, two_qbit_unitary[1].imag, two_qbit_unitary[0].real, two_qbit_unitary[0].imag);
543 __m256d mv20 = _mm256_set_pd(-two_qbit_unitary[3].imag, two_qbit_unitary[3].real, -two_qbit_unitary[2].imag, two_qbit_unitary[2].real);
544 __m256d mv21 = _mm256_set_pd( two_qbit_unitary[3].real, two_qbit_unitary[3].imag, two_qbit_unitary[2].real, two_qbit_unitary[2].imag);
545 __m256d mv40 = _mm256_set_pd(-two_qbit_unitary[5].imag, two_qbit_unitary[5].real, -two_qbit_unitary[4].imag, two_qbit_unitary[4].real);
546 __m256d mv41 = _mm256_set_pd( two_qbit_unitary[5].real, two_qbit_unitary[5].imag, two_qbit_unitary[4].real, two_qbit_unitary[4].imag);
547 __m256d mv60 = _mm256_set_pd(-two_qbit_unitary[7].imag, two_qbit_unitary[7].real, -two_qbit_unitary[6].imag, two_qbit_unitary[6].real);
548 __m256d mv61 = _mm256_set_pd( two_qbit_unitary[7].real, two_qbit_unitary[7].imag, two_qbit_unitary[6].real, two_qbit_unitary[6].imag);
549 __m256d mv80 = _mm256_set_pd(-two_qbit_unitary[9].imag, two_qbit_unitary[9].real, -two_qbit_unitary[8].imag, two_qbit_unitary[8].real);
550 __m256d mv81 = _mm256_set_pd( two_qbit_unitary[9].real, two_qbit_unitary[9].imag, two_qbit_unitary[8].real, two_qbit_unitary[8].imag);
551 __m256d mv100 = _mm256_set_pd(-two_qbit_unitary[11].imag, two_qbit_unitary[11].real, -two_qbit_unitary[10].imag, two_qbit_unitary[10].real);
552 __m256d mv101 = _mm256_set_pd( two_qbit_unitary[11].real, two_qbit_unitary[11].imag, two_qbit_unitary[10].real, two_qbit_unitary[10].imag);
553 __m256d mv120 = _mm256_set_pd(-two_qbit_unitary[13].imag, two_qbit_unitary[13].real, -two_qbit_unitary[12].imag, two_qbit_unitary[12].real);
554 __m256d mv121 = _mm256_set_pd( two_qbit_unitary[13].real, two_qbit_unitary[13].imag, two_qbit_unitary[12].real, two_qbit_unitary[12].imag);
555 __m256d mv140 = _mm256_set_pd(-two_qbit_unitary[15].imag, two_qbit_unitary[15].real, -two_qbit_unitary[14].imag, two_qbit_unitary[14].real);
556 __m256d mv141 = _mm256_set_pd( two_qbit_unitary[15].real, two_qbit_unitary[15].imag, two_qbit_unitary[14].real, two_qbit_unitary[14].imag);
558 for (
int current_idx = 0; current_idx < input.
rows; current_idx += (index_step_outer << 1)) {
559 int current_idx_pair_outer = current_idx + index_step_outer;
561 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner += (index_step_inner << 1)) {
563 int current_idx_outer_loc = current_idx + current_idx_inner;
564 int current_idx_outer_pair_loc = current_idx_pair_outer + current_idx_inner;
567 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
568 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
571 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
572 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
577 __m256d data_u0 = _mm256_mul_pd(element_outer_vec, mv00);
578 __m256d data_u1 = _mm256_mul_pd(element_outer_vec, mv01);
579 __m256d data_u3 = _mm256_mul_pd(element_outer_pair_vec, mv20);
580 __m256d data_u4 = _mm256_mul_pd(element_outer_pair_vec, mv21);
581 __m256d data_u5 = _mm256_add_pd(data_u3, data_u0);
582 __m256d data_u2 = _mm256_add_pd(data_u1, data_u4);
583 __m256d data_u7 = _mm256_hadd_pd(data_u5, data_u2);
584 __m256d data_u8 = _mm256_permute4x64_pd(data_u7, 0b11011000);
585 __m256d data_u6 = _mm256_hadd_pd(data_u8, data_u8);
587 __m256d data_d0 = _mm256_mul_pd(element_outer_vec, mv40);
588 __m256d data_d1 = _mm256_mul_pd(element_outer_vec, mv41);
589 __m256d data_d3 = _mm256_mul_pd(element_outer_pair_vec, mv60);
590 __m256d data_d4 = _mm256_mul_pd(element_outer_pair_vec, mv61);
591 __m256d data_d5 = _mm256_add_pd(data_d3, data_d0);
592 __m256d data_d6 = _mm256_add_pd(data_d1, data_d4);
593 data_d6 = _mm256_hadd_pd(data_d5, data_d6);
594 data_d6 = _mm256_permute4x64_pd(data_d6, 0b11011000);
595 data_d6 = _mm256_hadd_pd(data_d6, data_d6);
598 __m256d data_e0 = _mm256_mul_pd(element_outer_vec, mv80);
599 __m256d data_e1 = _mm256_mul_pd(element_outer_vec, mv81);
600 __m256d data_e3 = _mm256_mul_pd(element_outer_pair_vec, mv100);
601 __m256d data_e4 = _mm256_mul_pd(element_outer_pair_vec, mv101);
602 __m256d data_e5 = _mm256_add_pd(data_e3, data_e0);
603 __m256d data_e6 = _mm256_add_pd(data_e1, data_e4);
604 data_e6 = _mm256_hadd_pd(data_e5, data_e6);
605 data_e6 = _mm256_permute4x64_pd(data_e6, 0b11011000);
606 data_e6 = _mm256_hadd_pd(data_e6, data_e6);
609 __m256d data_f0 = _mm256_mul_pd(element_outer_vec, mv120);
610 __m256d data_f1 = _mm256_mul_pd(element_outer_vec, mv121);
611 __m256d data_f3 = _mm256_mul_pd(element_outer_pair_vec, mv140);
612 __m256d data_f4 = _mm256_mul_pd(element_outer_pair_vec, mv141);
613 __m256d data_f5 = _mm256_add_pd(data_f3, data_f0);
614 __m256d data_f6 = _mm256_add_pd(data_f1, data_f4);
615 data_f6 = _mm256_hadd_pd(data_f5, data_f6);
616 data_f6 = _mm256_permute4x64_pd(data_f6, 0b11011000);
617 data_f6 = _mm256_hadd_pd(data_f6, data_f6);
620 __m128d low128u = _mm256_castpd256_pd128(data_u6);
621 __m128d high128u = _mm256_extractf128_pd(data_u6, 1);
623 input[current_idx_outer_loc].real = _mm_cvtsd_f64(low128u);
624 input[current_idx_outer_loc].imag = _mm_cvtsd_f64(high128u);
626 __m128d low128d = _mm256_castpd256_pd128(data_d6);
627 __m128d high128d = _mm256_extractf128_pd(data_d6, 1);
628 input[current_idx_outer_loc + 1].real = _mm_cvtsd_f64(low128d);
629 input[current_idx_outer_loc + 1].imag = _mm_cvtsd_f64(high128d);
631 __m128d low128e = _mm256_castpd256_pd128(data_e6);
632 __m128d high128e = _mm256_extractf128_pd(data_e6, 1);
633 input[current_idx_outer_pair_loc].real = _mm_cvtsd_f64(low128e);
634 input[current_idx_outer_pair_loc].imag = _mm_cvtsd_f64(high128e);
636 __m128d low128f = _mm256_castpd256_pd128(data_f6);
637 __m128d high128f = _mm256_extractf128_pd(data_f6, 1);
638 input[current_idx_outer_pair_loc + 1].real = _mm_cvtsd_f64(low128f);
639 input[current_idx_outer_pair_loc + 1].imag = _mm_cvtsd_f64(high128f);
644 __m256d mv00 = _mm256_set_pd(-two_qbit_unitary[0].imag, two_qbit_unitary[0].
real, -two_qbit_unitary[0].imag, two_qbit_unitary[0].real);
645 __m256d mv01 = _mm256_set_pd( two_qbit_unitary[0].real, two_qbit_unitary[0].imag, two_qbit_unitary[0].real, two_qbit_unitary[0].imag);
646 __m256d mv10 = _mm256_set_pd(-two_qbit_unitary[1].imag, two_qbit_unitary[1].real, -two_qbit_unitary[1].imag, two_qbit_unitary[1].real);
647 __m256d mv11 = _mm256_set_pd( two_qbit_unitary[1].real, two_qbit_unitary[1].imag, two_qbit_unitary[1].real, two_qbit_unitary[1].imag);
648 __m256d mv20 = _mm256_set_pd(-two_qbit_unitary[2].imag, two_qbit_unitary[2].real, -two_qbit_unitary[2].imag, two_qbit_unitary[2].real);
649 __m256d mv21 = _mm256_set_pd( two_qbit_unitary[2].real, two_qbit_unitary[2].imag, two_qbit_unitary[2].real, two_qbit_unitary[2].imag);
650 __m256d mv30 = _mm256_set_pd(-two_qbit_unitary[3].imag, two_qbit_unitary[3].real, -two_qbit_unitary[3].imag, two_qbit_unitary[3].real);
651 __m256d mv31 = _mm256_set_pd( two_qbit_unitary[3].real, two_qbit_unitary[3].imag, two_qbit_unitary[3].real, two_qbit_unitary[3].imag);
652 __m256d mv40 = _mm256_set_pd(-two_qbit_unitary[4].imag, two_qbit_unitary[4].real, -two_qbit_unitary[4].imag, two_qbit_unitary[4].real);
653 __m256d mv41 = _mm256_set_pd( two_qbit_unitary[4].real, two_qbit_unitary[4].imag, two_qbit_unitary[4].real, two_qbit_unitary[4].imag);
654 __m256d mv50 = _mm256_set_pd(-two_qbit_unitary[5].imag, two_qbit_unitary[5].real, -two_qbit_unitary[5].imag, two_qbit_unitary[5].real);
655 __m256d mv51 = _mm256_set_pd( two_qbit_unitary[5].real, two_qbit_unitary[5].imag, two_qbit_unitary[5].real, two_qbit_unitary[5].imag);
656 __m256d mv60 = _mm256_set_pd(-two_qbit_unitary[6].imag, two_qbit_unitary[6].real, -two_qbit_unitary[6].imag, two_qbit_unitary[6].real);
657 __m256d mv61 = _mm256_set_pd( two_qbit_unitary[6].real, two_qbit_unitary[6].imag, two_qbit_unitary[6].real, two_qbit_unitary[6].imag);
658 __m256d mv70 = _mm256_set_pd(-two_qbit_unitary[7].imag, two_qbit_unitary[7].real, -two_qbit_unitary[7].imag, two_qbit_unitary[7].real);
659 __m256d mv71 = _mm256_set_pd( two_qbit_unitary[7].real, two_qbit_unitary[7].imag, two_qbit_unitary[7].real, two_qbit_unitary[7].imag);
660 __m256d mv80 = _mm256_set_pd(-two_qbit_unitary[8].imag, two_qbit_unitary[8].real, -two_qbit_unitary[8].imag, two_qbit_unitary[8].real);
661 __m256d mv81 = _mm256_set_pd( two_qbit_unitary[8].real, two_qbit_unitary[8].imag, two_qbit_unitary[8].real, two_qbit_unitary[8].imag);
662 __m256d mv90 = _mm256_set_pd(-two_qbit_unitary[9].imag, two_qbit_unitary[9].real, -two_qbit_unitary[9].imag, two_qbit_unitary[9].real);
663 __m256d mv91 = _mm256_set_pd( two_qbit_unitary[9].real, two_qbit_unitary[9].imag, two_qbit_unitary[9].real, two_qbit_unitary[9].imag);
664 __m256d mv100 = _mm256_set_pd(-two_qbit_unitary[10].imag, two_qbit_unitary[10].real, -two_qbit_unitary[10].imag, two_qbit_unitary[10].real);
665 __m256d mv101 = _mm256_set_pd( two_qbit_unitary[10].real, two_qbit_unitary[10].imag, two_qbit_unitary[10].real, two_qbit_unitary[10].imag);
666 __m256d mv110 = _mm256_set_pd(-two_qbit_unitary[11].imag, two_qbit_unitary[11].real, -two_qbit_unitary[11].imag, two_qbit_unitary[11].real);
667 __m256d mv111 = _mm256_set_pd( two_qbit_unitary[11].real, two_qbit_unitary[11].imag, two_qbit_unitary[11].real, two_qbit_unitary[11].imag);
668 __m256d mv120 = _mm256_set_pd(-two_qbit_unitary[12].imag, two_qbit_unitary[12].real, -two_qbit_unitary[12].imag, two_qbit_unitary[12].real);
669 __m256d mv121 = _mm256_set_pd( two_qbit_unitary[12].real, two_qbit_unitary[12].imag, two_qbit_unitary[12].real, two_qbit_unitary[12].imag);
670 __m256d mv130 = _mm256_set_pd(-two_qbit_unitary[13].imag, two_qbit_unitary[13].real, -two_qbit_unitary[13].imag, two_qbit_unitary[13].real);
671 __m256d mv131 = _mm256_set_pd( two_qbit_unitary[13].real, two_qbit_unitary[13].imag, two_qbit_unitary[13].real, two_qbit_unitary[13].imag);
672 __m256d mv140 = _mm256_set_pd(-two_qbit_unitary[14].imag, two_qbit_unitary[14].real, -two_qbit_unitary[14].imag, two_qbit_unitary[14].real);
673 __m256d mv141 = _mm256_set_pd( two_qbit_unitary[14].real, two_qbit_unitary[14].imag, two_qbit_unitary[14].real, two_qbit_unitary[14].imag);
674 __m256d mv150 = _mm256_set_pd(-two_qbit_unitary[15].imag, two_qbit_unitary[15].real, -two_qbit_unitary[15].imag, two_qbit_unitary[15].real);
675 __m256d mv151 = _mm256_set_pd( two_qbit_unitary[15].real, two_qbit_unitary[15].imag, two_qbit_unitary[15].real, two_qbit_unitary[15].imag);
676 for (
int current_idx_pair_outer=current_idx + index_step_outer; current_idx_pair_outer<input.
rows; current_idx_pair_outer=current_idx_pair_outer+(index_step_outer << 1)){
678 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner=current_idx_inner+(index_step_inner<<1)){
680 for (
int idx=0; idx<index_step_inner; idx=idx+2){
682 int current_idx_outer_loc = current_idx + current_idx_inner + idx;
684 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
685 double* element_inner = element_outer + 2 * index_step_inner;
687 double* element_outer_pair = element_outer + 2 * index_step_outer;
688 double* element_inner_pair = element_outer_pair + 2 * index_step_inner;
691 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
692 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
694 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
695 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
697 __m256d data_u0 = _mm256_mul_pd(element_outer_vec, mv00);
698 __m256d data_u1 = _mm256_mul_pd(element_inner_vec, mv10);
699 __m256d data_u2 = _mm256_mul_pd(element_outer_vec, mv01);
700 __m256d data_u3 = _mm256_mul_pd(element_inner_vec, mv11);
701 __m256d data_u4 = _mm256_mul_pd(element_outer_pair_vec, mv20);
702 __m256d data_u5 = _mm256_mul_pd(element_inner_pair_vec, mv30);
703 __m256d data_u6 = _mm256_mul_pd(element_outer_pair_vec, mv21);
704 __m256d data_u7 = _mm256_mul_pd(element_inner_pair_vec, mv31);
705 __m256d data_u8 = _mm256_hadd_pd(data_u0, data_u2);
706 __m256d data_u9 = _mm256_hadd_pd(data_u1, data_u3);
707 __m256d data_u10 = _mm256_hadd_pd(data_u4, data_u6);
708 __m256d data_u11 = _mm256_hadd_pd(data_u5, data_u7);
709 __m256d data_u = _mm256_add_pd(data_u8, data_u9);
710 data_u = _mm256_add_pd(data_u, data_u10);
711 data_u = _mm256_add_pd(data_u, data_u11);
713 __m256d data_d0 = _mm256_mul_pd(element_outer_vec, mv40);
714 __m256d data_d1 = _mm256_mul_pd(element_inner_vec, mv50);
715 __m256d data_d2 = _mm256_mul_pd(element_outer_vec, mv41);
716 __m256d data_d3 = _mm256_mul_pd(element_inner_vec, mv51);
717 __m256d data_d4 = _mm256_mul_pd(element_outer_pair_vec, mv60);
718 __m256d data_d5 = _mm256_mul_pd(element_inner_pair_vec, mv70);
719 __m256d data_d6 = _mm256_mul_pd(element_outer_pair_vec, mv61);
720 __m256d data_d7 = _mm256_mul_pd(element_inner_pair_vec, mv71);
721 __m256d data_d8 = _mm256_hadd_pd(data_d0, data_d2);
722 __m256d data_d9 = _mm256_hadd_pd(data_d1, data_d3);
723 __m256d data_d10 = _mm256_hadd_pd(data_d4, data_d6);
724 __m256d data_d11 = _mm256_hadd_pd(data_d5, data_d7);
725 __m256d data_d = _mm256_add_pd(data_d8, data_d9);
726 data_d = _mm256_add_pd(data_d, data_d10);
727 data_d = _mm256_add_pd(data_d, data_d11);
729 __m256d data_e0 = _mm256_mul_pd(element_outer_vec, mv80);
730 __m256d data_e1 = _mm256_mul_pd(element_inner_vec, mv90);
731 __m256d data_e2 = _mm256_mul_pd(element_outer_vec, mv81);
732 __m256d data_e3 = _mm256_mul_pd(element_inner_vec, mv91);
733 __m256d data_e4 = _mm256_mul_pd(element_outer_pair_vec, mv100);
734 __m256d data_e5 = _mm256_mul_pd(element_inner_pair_vec, mv110);
735 __m256d data_e6 = _mm256_mul_pd(element_outer_pair_vec, mv101);
736 __m256d data_e7 = _mm256_mul_pd(element_inner_pair_vec, mv111);
737 __m256d data_e8 = _mm256_hadd_pd(data_e0, data_e2);
738 __m256d data_e9 = _mm256_hadd_pd(data_e1, data_e3);
739 __m256d data_e10 = _mm256_hadd_pd(data_e4, data_e6);
740 __m256d data_e11 = _mm256_hadd_pd(data_e5, data_e7);
741 __m256d data_e = _mm256_add_pd(data_e8, data_e9);
742 data_e = _mm256_add_pd(data_e, data_e10);
743 data_e = _mm256_add_pd(data_e, data_e11);
745 __m256d data_f0 = _mm256_mul_pd(element_outer_vec, mv120);
746 __m256d data_f1 = _mm256_mul_pd(element_inner_vec, mv130);
747 __m256d data_f2 = _mm256_mul_pd(element_outer_vec, mv121);
748 __m256d data_f3 = _mm256_mul_pd(element_inner_vec, mv131);
749 __m256d data_f4 = _mm256_mul_pd(element_outer_pair_vec, mv140);
750 __m256d data_f5 = _mm256_mul_pd(element_inner_pair_vec, mv150);
751 __m256d data_f6 = _mm256_mul_pd(element_outer_pair_vec, mv141);
752 __m256d data_f7 = _mm256_mul_pd(element_inner_pair_vec, mv151);
753 __m256d data_f8 = _mm256_hadd_pd(data_f0, data_f2);
754 __m256d data_f9 = _mm256_hadd_pd(data_f1, data_f3);
755 __m256d data_f10 = _mm256_hadd_pd(data_f4, data_f6);
756 __m256d data_f11 = _mm256_hadd_pd(data_f5, data_f7);
757 __m256d data_f = _mm256_add_pd(data_f8, data_f9);
758 data_f = _mm256_add_pd(data_f, data_f10);
759 data_f = _mm256_add_pd(data_f, data_f11);
761 _mm256_storeu_pd(element_outer, data_u);
762 _mm256_storeu_pd(element_inner, data_d);
763 _mm256_storeu_pd(element_outer_pair, data_e);
764 _mm256_storeu_pd(element_inner_pair, data_f);
770 current_idx = current_idx + (index_step_outer << 1);
779 #define CREATE_MATRIX_VECTOR_CONSECUTIVE(base_idx) \ 780 __m256d mv##base_idx##0 = _mm256_set_pd(-unitary[base_idx+1].imag, unitary[base_idx+1].real, -unitary[base_idx].imag, unitary[base_idx].real); \ 781 __m256d mv##base_idx##1 = _mm256_set_pd( unitary[base_idx+1].real, unitary[base_idx+1].imag, unitary[base_idx].real, unitary[base_idx].imag); 783 #define COMPUTE_3QBIT_ROW_CONSECUTIVE(row_letter, mv00, mv20, mv40, mv60) \ 784 __m256d data_real_##row_letter = _mm256_setzero_pd(); \ 785 __m256d data_imag_##row_letter = _mm256_setzero_pd(); \ 786 data_real_##row_letter = _mm256_fmadd_pd(element_000_vec, mv##mv00##0, data_real_##row_letter); \ 787 data_real_##row_letter = _mm256_fmadd_pd(element_010_vec, mv##mv20##0, data_real_##row_letter); \ 788 data_real_##row_letter = _mm256_fmadd_pd(element_100_vec, mv##mv40##0, data_real_##row_letter); \ 789 data_real_##row_letter = _mm256_fmadd_pd(element_110_vec, mv##mv60##0, data_real_##row_letter); \ 790 data_imag_##row_letter = _mm256_fmadd_pd(element_000_vec, mv##mv00##1, data_imag_##row_letter); \ 791 data_imag_##row_letter = _mm256_fmadd_pd(element_010_vec, mv##mv20##1, data_imag_##row_letter); \ 792 data_imag_##row_letter = _mm256_fmadd_pd(element_100_vec, mv##mv40##1, data_imag_##row_letter); \ 793 data_imag_##row_letter = _mm256_fmadd_pd(element_110_vec, mv##mv60##1, data_imag_##row_letter); \ 794 __m256d data_##row_letter = _mm256_hadd_pd(data_real_##row_letter, data_imag_##row_letter ); \ 795 data_##row_letter = _mm256_permute4x64_pd(data_##row_letter, 0b11011000); \ 796 data_##row_letter = _mm256_hadd_pd(data_##row_letter, data_##row_letter); \ 797 __m128d low128##row_letter = _mm256_castpd256_pd128(data_##row_letter); \ 798 __m128d high128##row_letter = _mm256_extractf128_pd(data_##row_letter, 1); \ 799 results[row_idx].real = _mm_cvtsd_f64(low128##row_letter); \ 800 results[row_idx].imag = _mm_cvtsd_f64(high128##row_letter); 804 #define CREATE_MATRIX_VECTOR(base_idx) \ 805 __m256d mv##base_idx##0 = _mm256_set_pd(-unitary[base_idx].imag, unitary[base_idx].real, -unitary[base_idx].imag, unitary[base_idx].real); \ 806 __m256d mv##base_idx##1 = _mm256_set_pd( unitary[base_idx].real, unitary[base_idx].imag, unitary[base_idx].real, unitary[base_idx].imag); 808 #define COMPUTE_3QBIT_ROW(row_letter, base0, base1, base2, base3, base4, base5, base6, base7) \ 809 __m256d data_##row_letter##0 = _mm256_mul_pd(element_outer_vec, mv##base0##0); \ 810 __m256d data_##row_letter##1 = _mm256_mul_pd(element_inner_vec, mv##base1##0); \ 811 __m256d data_##row_letter##2 = _mm256_mul_pd(element_outer_vec, mv##base0##1); \ 812 __m256d data_##row_letter##3 = _mm256_mul_pd(element_inner_vec, mv##base1##1); \ 813 __m256d data_##row_letter##4 = _mm256_mul_pd(element_middle_vec, mv##base2##0); \ 814 __m256d data_##row_letter##5 = _mm256_mul_pd(element_middle_inner_vec, mv##base3##0); \ 815 __m256d data_##row_letter##6 = _mm256_mul_pd(element_middle_vec, mv##base2##1); \ 816 __m256d data_##row_letter##7 = _mm256_mul_pd(element_middle_inner_vec, mv##base3##1); \ 817 __m256d data_##row_letter##8 = _mm256_mul_pd(element_outer_pair_vec, mv##base4##0); \ 818 __m256d data_##row_letter##9 = _mm256_mul_pd(element_inner_pair_vec, mv##base5##0); \ 819 __m256d data_##row_letter##10 = _mm256_mul_pd(element_outer_pair_vec, mv##base4##1); \ 820 __m256d data_##row_letter##11 = _mm256_mul_pd(element_inner_pair_vec, mv##base5##1); \ 821 __m256d data_##row_letter##12 = _mm256_mul_pd(element_middle_pair_vec, mv##base6##0); \ 822 __m256d data_##row_letter##13 = _mm256_mul_pd(element_middle_inner_pair_vec, mv##base7##0); \ 823 __m256d data_##row_letter##14 = _mm256_mul_pd(element_middle_pair_vec, mv##base6##1); \ 824 __m256d data_##row_letter##15 = _mm256_mul_pd(element_middle_inner_pair_vec, mv##base7##1); \ 825 __m256d data_##row_letter##16 = _mm256_hadd_pd(data_##row_letter##0, data_##row_letter##2); \ 826 __m256d data_##row_letter##17 = _mm256_hadd_pd(data_##row_letter##1, data_##row_letter##3); \ 827 __m256d data_##row_letter##18 = _mm256_hadd_pd(data_##row_letter##4, data_##row_letter##6); \ 828 __m256d data_##row_letter##19 = _mm256_hadd_pd(data_##row_letter##5, data_##row_letter##7); \ 829 __m256d data_##row_letter##20 = _mm256_hadd_pd(data_##row_letter##8, data_##row_letter##10); \ 830 __m256d data_##row_letter##21 = _mm256_hadd_pd(data_##row_letter##9, data_##row_letter##11); \ 831 __m256d data_##row_letter##22 = _mm256_hadd_pd(data_##row_letter##12, data_##row_letter##14); \ 832 __m256d data_##row_letter##23 = _mm256_hadd_pd(data_##row_letter##13, data_##row_letter##15); \ 833 __m256d data_##row_letter = _mm256_add_pd(data_##row_letter##16, data_##row_letter##17); \ 834 data_##row_letter = _mm256_add_pd(data_##row_letter, data_##row_letter##18); \ 835 data_##row_letter = _mm256_add_pd(data_##row_letter, data_##row_letter##19); \ 836 data_##row_letter = _mm256_add_pd(data_##row_letter, data_##row_letter##20); \ 837 data_##row_letter = _mm256_add_pd(data_##row_letter, data_##row_letter##21); \ 838 data_##row_letter = _mm256_add_pd(data_##row_letter, data_##row_letter##22); \ 839 data_##row_letter = _mm256_add_pd(data_##row_letter, data_##row_letter##23); 850 int inner_qbit = involved_qbits[0];
851 int middle_qbit = involved_qbits[1];
852 int outer_qbit = involved_qbits[2];
854 int index_step_inner = 1 << inner_qbit;
855 int index_step_middle = 1 << middle_qbit;
856 int index_step_outer = 1 << outer_qbit;
858 int qubit_num = (
int) std::log2(input.
rows);
860 std::vector<int> is_target(qubit_num, 0);
861 for (
int q : involved_qbits) is_target[q] = 1;
863 std::vector<int> non_targets;
864 non_targets.reserve(qubit_num - 3);
865 for (
int q = 0; q < qubit_num; ++q) {
866 if (!is_target[q]) non_targets.push_back(q);
869 if (inner_qbit == 0) {
889 for (
int iter_idx = 0; iter_idx < matrix_size>>3; iter_idx++) {
891 for (
size_t i = 0; i < non_targets.size(); ++i) {
892 if (iter_idx & (1ULL << i)) {
893 base |= (1 << non_targets[i]);
897 double* ptr_000 = (
double*)input.
get_data() + 2 * base;
898 double* ptr_010 = (
double*)input.
get_data() + 2 * (base | index_step_middle);
899 double* ptr_100 = (
double*)input.
get_data() + 2 * (base | index_step_outer);
900 double* ptr_110 = (
double*)input.
get_data() + 2 * (base | index_step_middle | index_step_outer);
902 __m256d element_000_vec = _mm256_loadu_pd(ptr_000);
903 __m256d element_010_vec = _mm256_loadu_pd(ptr_010);
904 __m256d element_100_vec = _mm256_loadu_pd(ptr_100);
905 __m256d element_110_vec = _mm256_loadu_pd(ptr_110);
927 input[base] = results[0];
928 input[base+1] = results[1];
929 input[(base | index_step_middle)] = results[2];
930 input[(base | index_step_middle)+1] = results[3];
931 input[(base | index_step_outer)] = results[4];
932 input[(base | index_step_outer)+1] = results[5];
933 input[(base | index_step_middle | index_step_outer)] = results[6];
934 input[(base | index_step_middle | index_step_outer)+1] = results[7];
956 for (
int iter_idx = 0; iter_idx < matrix_size>>3; iter_idx+=2) {
959 for (
size_t i = 0; i < non_targets.size(); ++i) {
960 if (iter_idx & (1ULL << i)) {
961 base |= (1 << non_targets[i]);
965 double* element_outer = (
double*)input.
get_data() + 2 * base;
966 double* element_inner = (
double*)input.
get_data() + 2 * (base | index_step_inner);
967 double* element_middle = (
double*)input.
get_data() + 2 * (base | index_step_middle);
968 double* element_middle_inner = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_middle);
969 double* element_outer_pair = (
double*)input.
get_data() + 2 * (base | index_step_outer);
970 double* element_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_outer);
971 double* element_middle_pair = (
double*)input.
get_data() + 2 * (base | index_step_middle | index_step_outer);
972 double* element_middle_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_middle | index_step_outer);
974 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
975 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
976 __m256d element_middle_vec = _mm256_loadu_pd(element_middle);
977 __m256d element_middle_inner_vec = _mm256_loadu_pd(element_middle_inner);
978 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
979 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
980 __m256d element_middle_pair_vec = _mm256_loadu_pd(element_middle_pair);
981 __m256d element_middle_inner_pair_vec = _mm256_loadu_pd(element_middle_inner_pair);
994 _mm256_storeu_pd(element_outer, data_u);
995 _mm256_storeu_pd(element_inner, data_d);
996 _mm256_storeu_pd(element_middle, data_e);
997 _mm256_storeu_pd(element_middle_inner, data_f);
998 _mm256_storeu_pd(element_outer_pair, data_g);
999 _mm256_storeu_pd(element_inner_pair, data_h);
1000 _mm256_storeu_pd(element_middle_pair, data_i);
1001 _mm256_storeu_pd(element_middle_inner_pair, data_j);
1015 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
1017 int index_step_inner = 1 << involved_qbits[0];
1018 int index_step_middle1 = 1 << involved_qbits[1];
1019 int index_step_middle2 = 1 << involved_qbits[2];
1020 int index_step_outer = 1 << involved_qbits[3];
1023 int num_qubits = (
int)std::log2(matrix_size);
1024 int num_blocks = matrix_size >> 4;
1028 std::vector<int> is_target(num_qubits, 0);
1029 for (
int q : involved_qbits) is_target[q] = 1;
1031 std::vector<int> non_targets;
1032 non_targets.reserve(num_qubits - 4);
1033 for (
int q = 0; q < num_qubits; ++q) {
1034 if (!is_target[q]) non_targets.push_back(q);
1037 for (
int block_idx = 0; block_idx < num_blocks; block_idx++) {
1038 __m256d element_0000_vec_real = _mm256_setzero_pd();
1039 __m256d element_0010_vec_real = _mm256_setzero_pd();
1040 __m256d element_0100_vec_real = _mm256_setzero_pd();
1041 __m256d element_0110_vec_real = _mm256_setzero_pd();
1042 __m256d element_1000_vec_real = _mm256_setzero_pd();
1043 __m256d element_1010_vec_real = _mm256_setzero_pd();
1044 __m256d element_1100_vec_real = _mm256_setzero_pd();
1045 __m256d element_1110_vec_real = _mm256_setzero_pd();
1048 for (
size_t i = 0; i < non_targets.size(); ++i) {
1049 if (block_idx & (1ULL << i)) {
1050 base |= (1 << non_targets[i]);
1054 int current_idx_outer_loc = base;
1055 int current_idx_inner_loc = base | index_step_inner;
1056 int current_idx_middle1_loc = base | index_step_middle1;
1057 int current_idx_middle1_inner_loc = base | index_step_middle1 | index_step_inner;
1058 int current_idx_middle2_loc = base | index_step_middle2;
1059 int current_idx_middle2_inner_loc = base | index_step_middle2 | index_step_inner;
1060 int current_idx_middle12_loc = base | index_step_middle1 | index_step_middle2;
1061 int current_idx_middle12_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_inner;
1063 int current_idx_outer_pair_loc = base | index_step_outer;
1064 int current_idx_inner_pair_loc = base | index_step_outer | index_step_inner;
1065 int current_idx_middle1_pair_loc = base | index_step_outer | index_step_middle1;
1066 int current_idx_middle1_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_inner;
1067 int current_idx_middle2_pair_loc = base | index_step_outer | index_step_middle2;
1068 int current_idx_middle2_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_inner;
1069 int current_idx_middle12_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2;
1070 int current_idx_middle12_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_inner;
1071 if(involved_qbits[0] == 0){
1073 element_0000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_loc);
1074 element_0010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_loc);
1075 element_0100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_loc);
1076 element_0110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_loc);
1077 element_1000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_pair_loc);
1078 element_1010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc);
1079 element_1100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc);
1080 element_1110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc);
1084 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
1085 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner_loc;
1087 double* element_middle1 = (
double*)input.
get_data() + 2 * current_idx_middle1_loc;
1088 double* element_middle1_inner = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_loc;
1090 double* element_middle2 = (
double*)input.
get_data() + 2 * current_idx_middle2_loc;
1091 double* element_middle2_inner = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_loc;
1093 double* element_middle12 = (
double*)input.
get_data() + 2 * current_idx_middle12_loc;
1094 double* element_middle12_inner = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_loc;
1096 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
1097 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair_loc;
1099 double* element_middle1_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc;
1100 double* element_middle1_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_pair_loc;
1102 double* element_middle2_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc;
1103 double* element_middle2_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_pair_loc;
1105 double* element_middle12_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc;
1106 double* element_middle12_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_pair_loc;
1108 element_0000_vec_real =
get_AVX_vector(element_outer, element_inner);
1109 element_0010_vec_real =
get_AVX_vector(element_middle1, element_middle1_inner);
1110 element_0100_vec_real =
get_AVX_vector(element_middle2, element_middle2_inner);
1111 element_0110_vec_real =
get_AVX_vector(element_middle12, element_middle12_inner);
1112 element_1000_vec_real =
get_AVX_vector(element_outer_pair, element_inner_pair);
1113 element_1010_vec_real =
get_AVX_vector(element_middle1_pair, element_middle1_inner_pair);
1114 element_1100_vec_real =
get_AVX_vector(element_middle2_pair, element_middle2_inner_pair);
1115 element_1110_vec_real =
get_AVX_vector(element_middle12_pair, element_middle12_inner_pair);
1118 __m256d element_0000_vec_imag = _mm256_permute4x64_pd(element_0000_vec_real, 0b10110001);
1119 __m256d element_0010_vec_imag = _mm256_permute4x64_pd(element_0010_vec_real, 0b10110001);
1120 __m256d element_0100_vec_imag = _mm256_permute4x64_pd(element_0100_vec_real, 0b10110001);
1121 __m256d element_0110_vec_imag = _mm256_permute4x64_pd(element_0110_vec_real, 0b10110001);
1122 __m256d element_1000_vec_imag = _mm256_permute4x64_pd(element_1000_vec_real, 0b10110001);
1123 __m256d element_1010_vec_imag = _mm256_permute4x64_pd(element_1010_vec_real, 0b10110001);
1124 __m256d element_1100_vec_imag = _mm256_permute4x64_pd(element_1100_vec_real, 0b10110001);
1125 __m256d element_1110_vec_imag = _mm256_permute4x64_pd(element_1110_vec_real, 0b10110001);
1127 element_0000_vec_real = _mm256_mul_pd(element_0000_vec_real,neg);
1128 element_0010_vec_real = _mm256_mul_pd(element_0010_vec_real,neg);
1129 element_0100_vec_real = _mm256_mul_pd(element_0100_vec_real,neg);
1130 element_0110_vec_real = _mm256_mul_pd(element_0110_vec_real,neg);
1131 element_1000_vec_real = _mm256_mul_pd(element_1000_vec_real,neg);
1132 element_1010_vec_real = _mm256_mul_pd(element_1010_vec_real,neg);
1133 element_1100_vec_real = _mm256_mul_pd(element_1100_vec_real,neg);
1134 element_1110_vec_real = _mm256_mul_pd(element_1110_vec_real,neg);
1137 for (
int mult_idx = 0; mult_idx < 16; mult_idx++) {
1138 double* unitary_row_1 = (
double*)unitary.
get_data() + 32*mult_idx;
1139 double* unitary_row_2 = unitary_row_1 + 4;
1140 double* unitary_row_3 = unitary_row_1 + 8;
1141 double* unitary_row_4 = unitary_row_1 + 12;
1142 double* unitary_row_5 = unitary_row_1 + 16;
1143 double* unitary_row_6 = unitary_row_1 + 20;
1144 double* unitary_row_7 = unitary_row_1 + 24;
1145 double* unitary_row_8 = unitary_row_1 + 28;
1147 __m256d row1_vec = _mm256_loadu_pd(unitary_row_1);
1148 __m256d row2_vec = _mm256_loadu_pd(unitary_row_2);
1149 __m256d row3_vec = _mm256_loadu_pd(unitary_row_3);
1150 __m256d row4_vec = _mm256_loadu_pd(unitary_row_4);
1151 __m256d row5_vec = _mm256_loadu_pd(unitary_row_5);
1152 __m256d row6_vec = _mm256_loadu_pd(unitary_row_6);
1153 __m256d row7_vec = _mm256_loadu_pd(unitary_row_7);
1154 __m256d row8_vec = _mm256_loadu_pd(unitary_row_8);
1156 __m256d data_real = _mm256_setzero_pd();
1157 __m256d data_imag = _mm256_setzero_pd();
1159 data_real = _mm256_fmadd_pd(element_0000_vec_real, row1_vec, data_real);
1160 data_imag = _mm256_fmadd_pd(element_0000_vec_imag, row1_vec, data_imag);
1161 data_real = _mm256_fmadd_pd(element_0010_vec_real, row2_vec, data_real);
1162 data_imag = _mm256_fmadd_pd(element_0010_vec_imag, row2_vec, data_imag);
1163 data_real = _mm256_fmadd_pd(element_0100_vec_real, row3_vec, data_real);
1164 data_imag = _mm256_fmadd_pd(element_0100_vec_imag, row3_vec, data_imag);
1165 data_real = _mm256_fmadd_pd(element_0110_vec_real, row4_vec, data_real);
1166 data_imag = _mm256_fmadd_pd(element_0110_vec_imag, row4_vec, data_imag);
1167 data_real = _mm256_fmadd_pd(element_1000_vec_real, row5_vec, data_real);
1168 data_imag = _mm256_fmadd_pd(element_1000_vec_imag, row5_vec, data_imag);
1169 data_real = _mm256_fmadd_pd(element_1010_vec_real, row6_vec, data_real);
1170 data_imag = _mm256_fmadd_pd(element_1010_vec_imag, row6_vec, data_imag);
1171 data_real = _mm256_fmadd_pd(element_1100_vec_real, row7_vec, data_real);
1172 data_imag = _mm256_fmadd_pd(element_1100_vec_imag, row7_vec, data_imag);
1173 data_real = _mm256_fmadd_pd(element_1110_vec_real, row8_vec, data_real);
1174 data_imag = _mm256_fmadd_pd(element_1110_vec_imag, row8_vec, data_imag);
1176 __m256d final_vec = _mm256_hadd_pd(data_real, data_imag);
1177 final_vec = _mm256_permute4x64_pd(final_vec, 0b11011000);
1178 final_vec = _mm256_hadd_pd(final_vec, final_vec);
1179 __m128d low128 = _mm256_castpd256_pd128(final_vec);
1180 __m128d high128 = _mm256_extractf128_pd(final_vec, 1);
1181 results[mult_idx].
real = _mm_cvtsd_f64(low128);
1182 results[mult_idx].
imag = _mm_cvtsd_f64(high128);
1185 input[current_idx_outer_loc] = results[0];
1186 input[current_idx_inner_loc] = results[1];
1187 input[current_idx_middle1_loc] = results[2];
1188 input[current_idx_middle1_inner_loc] = results[3];
1189 input[current_idx_middle2_loc] = results[4];
1190 input[current_idx_middle2_inner_loc] = results[5];
1191 input[current_idx_middle12_loc] = results[6];
1192 input[current_idx_middle12_inner_loc] = results[7];
1193 input[current_idx_outer_pair_loc] = results[8];
1194 input[current_idx_inner_pair_loc] = results[9];
1195 input[current_idx_middle1_pair_loc] = results[10];
1196 input[current_idx_middle1_inner_pair_loc] = results[11];
1197 input[current_idx_middle2_pair_loc] = results[12];
1198 input[current_idx_middle2_inner_pair_loc] = results[13];
1199 input[current_idx_middle12_pair_loc] = results[14];
1200 input[current_idx_middle12_inner_pair_loc] = results[15];
1217 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
1219 int index_step_inner = 1 << involved_qbits[0];
1220 int index_step_middle1 = 1 << involved_qbits[1];
1221 int index_step_middle2 = 1 << involved_qbits[2];
1222 int index_step_middle3 = 1 << involved_qbits[3];
1223 int index_step_outer = 1 << involved_qbits[4];
1225 int num_qubits = (
int)std::log2(matrix_size);
1226 int num_blocks = matrix_size >> 5;
1227 std::vector<int> is_target(num_qubits, 0);
1228 for (
int q : involved_qbits) is_target[q] = 1;
1230 std::vector<int> non_targets;
1231 non_targets.reserve(num_qubits - 5);
1232 for (
int q = 0; q < num_qubits; ++q) {
1233 if (!is_target[q]) non_targets.push_back(q);
1236 for (
int block_idx = 0; block_idx < num_blocks; block_idx++) {
1239 for (
size_t i = 0; i < non_targets.size(); ++i) {
1240 if (block_idx & (1ULL << i)) {
1241 base |= (1 << non_targets[i]);
1245 int current_idx_outer_loc = base;
1246 int current_idx_inner_loc = base | index_step_inner;
1248 int current_idx_middle1_loc = base | index_step_middle1;
1249 int current_idx_middle1_inner_loc = base | index_step_middle1 | index_step_inner;
1251 int current_idx_middle2_loc = base | index_step_middle2;
1252 int current_idx_middle2_inner_loc = base | index_step_middle2 | index_step_inner;
1254 int current_idx_middle12_loc = base | index_step_middle1 | index_step_middle2;
1255 int current_idx_middle12_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_inner;
1257 int current_idx_middle3_loc = base | index_step_middle3;
1258 int current_idx_middle3_inner_loc = base | index_step_middle3 | index_step_inner;
1260 int current_idx_middle13_loc = base | index_step_middle1 | index_step_middle3;
1261 int current_idx_middle13_inner_loc = base | index_step_middle1 | index_step_middle3 | index_step_inner;
1263 int current_idx_middle23_loc = base | index_step_middle2 | index_step_middle3;
1264 int current_idx_middle23_inner_loc = base | index_step_middle2 | index_step_middle3 | index_step_inner;
1266 int current_idx_middle123_loc = base | index_step_middle1 | index_step_middle2 | index_step_middle3;
1267 int current_idx_middle123_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_middle3 | index_step_inner;
1269 int current_idx_outer_pair_loc = base | index_step_outer;
1270 int current_idx_inner_pair_loc = base | index_step_outer | index_step_inner;
1272 int current_idx_middle1_pair_loc = base | index_step_outer | index_step_middle1;
1273 int current_idx_middle1_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_inner;
1275 int current_idx_middle2_pair_loc = base | index_step_outer | index_step_middle2;
1276 int current_idx_middle2_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_inner;
1278 int current_idx_middle12_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2;
1279 int current_idx_middle12_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_inner;
1281 int current_idx_middle3_pair_loc = base | index_step_outer | index_step_middle3;
1282 int current_idx_middle3_inner_pair_loc = base | index_step_outer | index_step_middle3 | index_step_inner;
1284 int current_idx_middle13_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle3;
1285 int current_idx_middle13_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle3 | index_step_inner;
1287 int current_idx_middle23_pair_loc = base | index_step_outer | index_step_middle2 | index_step_middle3;
1288 int current_idx_middle23_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_middle3 | index_step_inner;
1290 int current_idx_middle123_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_middle3;
1291 int current_idx_middle123_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_middle3 | index_step_inner;
1293 __m256d element_00000_vec_real = _mm256_setzero_pd();
1294 __m256d element_00010_vec_real = _mm256_setzero_pd();
1295 __m256d element_00100_vec_real = _mm256_setzero_pd();
1296 __m256d element_00110_vec_real = _mm256_setzero_pd();
1297 __m256d element_01000_vec_real = _mm256_setzero_pd();
1298 __m256d element_01010_vec_real = _mm256_setzero_pd();
1299 __m256d element_01100_vec_real = _mm256_setzero_pd();
1300 __m256d element_01110_vec_real = _mm256_setzero_pd();
1301 __m256d element_10000_vec_real = _mm256_setzero_pd();
1302 __m256d element_10010_vec_real = _mm256_setzero_pd();
1303 __m256d element_10100_vec_real = _mm256_setzero_pd();
1304 __m256d element_10110_vec_real = _mm256_setzero_pd();
1305 __m256d element_11000_vec_real = _mm256_setzero_pd();
1306 __m256d element_11010_vec_real = _mm256_setzero_pd();
1307 __m256d element_11100_vec_real = _mm256_setzero_pd();
1308 __m256d element_11110_vec_real = _mm256_setzero_pd();
1310 if (involved_qbits[0] == 0) {
1312 element_00000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_loc);
1313 element_00010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_loc);
1314 element_00100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_loc);
1315 element_00110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_loc);
1316 element_01000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle3_loc);
1317 element_01010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle13_loc);
1318 element_01100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle23_loc);
1319 element_01110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle123_loc);
1320 element_10000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_pair_loc);
1321 element_10010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc);
1322 element_10100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc);
1323 element_10110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc);
1324 element_11000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle3_pair_loc);
1325 element_11010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle13_pair_loc);
1326 element_11100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle23_pair_loc);
1327 element_11110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle123_pair_loc);
1331 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
1332 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner_loc;
1334 double* element_middle1 = (
double*)input.
get_data() + 2 * current_idx_middle1_loc;
1335 double* element_middle1_inner = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_loc;
1337 double* element_middle2 = (
double*)input.
get_data() + 2 * current_idx_middle2_loc;
1338 double* element_middle2_inner = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_loc;
1340 double* element_middle12 = (
double*)input.
get_data() + 2 * current_idx_middle12_loc;
1341 double* element_middle12_inner = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_loc;
1343 double* element_middle3 = (
double*)input.
get_data() + 2 * current_idx_middle3_loc;
1344 double* element_middle3_inner = (
double*)input.
get_data() + 2 * current_idx_middle3_inner_loc;
1346 double* element_middle13 = (
double*)input.
get_data() + 2 * current_idx_middle13_loc;
1347 double* element_middle13_inner = (
double*)input.
get_data() + 2 * current_idx_middle13_inner_loc;
1349 double* element_middle23 = (
double*)input.
get_data() + 2 * current_idx_middle23_loc;
1350 double* element_middle23_inner = (
double*)input.
get_data() + 2 * current_idx_middle23_inner_loc;
1352 double* element_middle123 = (
double*)input.
get_data() + 2 * current_idx_middle123_loc;
1353 double* element_middle123_inner = (
double*)input.
get_data() + 2 * current_idx_middle123_inner_loc;
1355 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
1356 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair_loc;
1358 double* element_middle1_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc;
1359 double* element_middle1_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_pair_loc;
1361 double* element_middle2_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc;
1362 double* element_middle2_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_pair_loc;
1364 double* element_middle12_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc;
1365 double* element_middle12_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_pair_loc;
1367 double* element_middle3_pair = (
double*)input.
get_data() + 2 * current_idx_middle3_pair_loc;
1368 double* element_middle3_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle3_inner_pair_loc;
1370 double* element_middle13_pair = (
double*)input.
get_data() + 2 * current_idx_middle13_pair_loc;
1371 double* element_middle13_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle13_inner_pair_loc;
1373 double* element_middle23_pair = (
double*)input.
get_data() + 2 * current_idx_middle23_pair_loc;
1374 double* element_middle23_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle23_inner_pair_loc;
1376 double* element_middle123_pair = (
double*)input.
get_data() + 2 * current_idx_middle123_pair_loc;
1377 double* element_middle123_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle123_inner_pair_loc;
1379 element_00000_vec_real =
get_AVX_vector(element_outer, element_inner);
1380 element_00010_vec_real =
get_AVX_vector(element_middle1, element_middle1_inner);
1381 element_00100_vec_real =
get_AVX_vector(element_middle2, element_middle2_inner);
1382 element_00110_vec_real =
get_AVX_vector(element_middle12, element_middle12_inner);
1383 element_01000_vec_real =
get_AVX_vector(element_middle3, element_middle3_inner);
1384 element_01010_vec_real =
get_AVX_vector(element_middle13, element_middle13_inner);
1385 element_01100_vec_real =
get_AVX_vector(element_middle23, element_middle23_inner);
1386 element_01110_vec_real =
get_AVX_vector(element_middle123, element_middle123_inner);
1387 element_10000_vec_real =
get_AVX_vector(element_outer_pair, element_inner_pair);
1388 element_10010_vec_real =
get_AVX_vector(element_middle1_pair, element_middle1_inner_pair);
1389 element_10100_vec_real =
get_AVX_vector(element_middle2_pair, element_middle2_inner_pair);
1390 element_10110_vec_real =
get_AVX_vector(element_middle12_pair, element_middle12_inner_pair);
1391 element_11000_vec_real =
get_AVX_vector(element_middle3_pair, element_middle3_inner_pair);
1392 element_11010_vec_real =
get_AVX_vector(element_middle13_pair, element_middle13_inner_pair);
1393 element_11100_vec_real =
get_AVX_vector(element_middle23_pair, element_middle23_inner_pair);
1394 element_11110_vec_real =
get_AVX_vector(element_middle123_pair, element_middle123_inner_pair);
1397 __m256d element_00000_vec_imag = _mm256_permute4x64_pd(element_00000_vec_real, 0b10110001);
1398 __m256d element_00010_vec_imag = _mm256_permute4x64_pd(element_00010_vec_real, 0b10110001);
1399 __m256d element_00100_vec_imag = _mm256_permute4x64_pd(element_00100_vec_real, 0b10110001);
1400 __m256d element_00110_vec_imag = _mm256_permute4x64_pd(element_00110_vec_real, 0b10110001);
1401 __m256d element_01000_vec_imag = _mm256_permute4x64_pd(element_01000_vec_real, 0b10110001);
1402 __m256d element_01010_vec_imag = _mm256_permute4x64_pd(element_01010_vec_real, 0b10110001);
1403 __m256d element_01100_vec_imag = _mm256_permute4x64_pd(element_01100_vec_real, 0b10110001);
1404 __m256d element_01110_vec_imag = _mm256_permute4x64_pd(element_01110_vec_real, 0b10110001);
1405 __m256d element_10000_vec_imag = _mm256_permute4x64_pd(element_10000_vec_real, 0b10110001);
1406 __m256d element_10010_vec_imag = _mm256_permute4x64_pd(element_10010_vec_real, 0b10110001);
1407 __m256d element_10100_vec_imag = _mm256_permute4x64_pd(element_10100_vec_real, 0b10110001);
1408 __m256d element_10110_vec_imag = _mm256_permute4x64_pd(element_10110_vec_real, 0b10110001);
1409 __m256d element_11000_vec_imag = _mm256_permute4x64_pd(element_11000_vec_real, 0b10110001);
1410 __m256d element_11010_vec_imag = _mm256_permute4x64_pd(element_11010_vec_real, 0b10110001);
1411 __m256d element_11100_vec_imag = _mm256_permute4x64_pd(element_11100_vec_real, 0b10110001);
1412 __m256d element_11110_vec_imag = _mm256_permute4x64_pd(element_11110_vec_real, 0b10110001);
1414 element_00000_vec_real = _mm256_mul_pd(element_00000_vec_real,neg);
1415 element_00010_vec_real = _mm256_mul_pd(element_00010_vec_real,neg);
1416 element_00100_vec_real = _mm256_mul_pd(element_00100_vec_real,neg);
1417 element_00110_vec_real = _mm256_mul_pd(element_00110_vec_real,neg);
1418 element_01000_vec_real = _mm256_mul_pd(element_01000_vec_real,neg);
1419 element_01010_vec_real = _mm256_mul_pd(element_01010_vec_real,neg);
1420 element_01100_vec_real = _mm256_mul_pd(element_01100_vec_real,neg);
1421 element_01110_vec_real = _mm256_mul_pd(element_01110_vec_real,neg);
1422 element_10000_vec_real = _mm256_mul_pd(element_10000_vec_real,neg);
1423 element_10010_vec_real = _mm256_mul_pd(element_10010_vec_real,neg);
1424 element_10100_vec_real = _mm256_mul_pd(element_10100_vec_real,neg);
1425 element_10110_vec_real = _mm256_mul_pd(element_10110_vec_real,neg);
1426 element_11000_vec_real = _mm256_mul_pd(element_11000_vec_real,neg);
1427 element_11010_vec_real = _mm256_mul_pd(element_11010_vec_real,neg);
1428 element_11100_vec_real = _mm256_mul_pd(element_11100_vec_real,neg);
1429 element_11110_vec_real = _mm256_mul_pd(element_11110_vec_real,neg);
1432 for (
int mult_idx = 0; mult_idx < 32; mult_idx++) {
1433 double* unitary_row_1 = (
double*)unitary.
get_data() + 64*mult_idx;
1434 double* unitary_row_2 = unitary_row_1 + 4;
1435 double* unitary_row_3 = unitary_row_1 + 8;
1436 double* unitary_row_4 = unitary_row_1 + 12;
1437 double* unitary_row_5 = unitary_row_1 + 16;
1438 double* unitary_row_6 = unitary_row_1 + 20;
1439 double* unitary_row_7 = unitary_row_1 + 24;
1440 double* unitary_row_8 = unitary_row_1 + 28;
1441 double* unitary_row_9 = unitary_row_1 + 32;
1442 double* unitary_row_10 = unitary_row_1 + 36;
1443 double* unitary_row_11 = unitary_row_1 + 40;
1444 double* unitary_row_12 = unitary_row_1 + 44;
1445 double* unitary_row_13 = unitary_row_1 + 48;
1446 double* unitary_row_14 = unitary_row_1 + 52;
1447 double* unitary_row_15 = unitary_row_1 + 56;
1448 double* unitary_row_16 = unitary_row_1 + 60;
1450 __m256d row1_vec = _mm256_loadu_pd(unitary_row_1);
1451 __m256d row2_vec = _mm256_loadu_pd(unitary_row_2);
1452 __m256d row3_vec = _mm256_loadu_pd(unitary_row_3);
1453 __m256d row4_vec = _mm256_loadu_pd(unitary_row_4);
1454 __m256d row5_vec = _mm256_loadu_pd(unitary_row_5);
1455 __m256d row6_vec = _mm256_loadu_pd(unitary_row_6);
1456 __m256d row7_vec = _mm256_loadu_pd(unitary_row_7);
1457 __m256d row8_vec = _mm256_loadu_pd(unitary_row_8);
1458 __m256d row9_vec = _mm256_loadu_pd(unitary_row_9);
1459 __m256d row10_vec = _mm256_loadu_pd(unitary_row_10);
1460 __m256d row11_vec = _mm256_loadu_pd(unitary_row_11);
1461 __m256d row12_vec = _mm256_loadu_pd(unitary_row_12);
1462 __m256d row13_vec = _mm256_loadu_pd(unitary_row_13);
1463 __m256d row14_vec = _mm256_loadu_pd(unitary_row_14);
1464 __m256d row15_vec = _mm256_loadu_pd(unitary_row_15);
1465 __m256d row16_vec = _mm256_loadu_pd(unitary_row_16);
1467 __m256d data_real = _mm256_setzero_pd();
1468 __m256d data_imag = _mm256_setzero_pd();
1470 data_real = _mm256_fmadd_pd(element_00000_vec_real, row1_vec, data_real);
1471 data_imag = _mm256_fmadd_pd(element_00000_vec_imag, row1_vec, data_imag);
1472 data_real = _mm256_fmadd_pd(element_00010_vec_real, row2_vec, data_real);
1473 data_imag = _mm256_fmadd_pd(element_00010_vec_imag, row2_vec, data_imag);
1474 data_real = _mm256_fmadd_pd(element_00100_vec_real, row3_vec, data_real);
1475 data_imag = _mm256_fmadd_pd(element_00100_vec_imag, row3_vec, data_imag);
1476 data_real = _mm256_fmadd_pd(element_00110_vec_real, row4_vec, data_real);
1477 data_imag = _mm256_fmadd_pd(element_00110_vec_imag, row4_vec, data_imag);
1478 data_real = _mm256_fmadd_pd(element_01000_vec_real, row5_vec, data_real);
1479 data_imag = _mm256_fmadd_pd(element_01000_vec_imag, row5_vec, data_imag);
1480 data_real = _mm256_fmadd_pd(element_01010_vec_real, row6_vec, data_real);
1481 data_imag = _mm256_fmadd_pd(element_01010_vec_imag, row6_vec, data_imag);
1482 data_real = _mm256_fmadd_pd(element_01100_vec_real, row7_vec, data_real);
1483 data_imag = _mm256_fmadd_pd(element_01100_vec_imag, row7_vec, data_imag);
1484 data_real = _mm256_fmadd_pd(element_01110_vec_real, row8_vec, data_real);
1485 data_imag = _mm256_fmadd_pd(element_01110_vec_imag, row8_vec, data_imag);
1486 data_real = _mm256_fmadd_pd(element_10000_vec_real, row9_vec, data_real);
1487 data_imag = _mm256_fmadd_pd(element_10000_vec_imag, row9_vec, data_imag);
1488 data_real = _mm256_fmadd_pd(element_10010_vec_real, row10_vec, data_real);
1489 data_imag = _mm256_fmadd_pd(element_10010_vec_imag, row10_vec, data_imag);
1490 data_real = _mm256_fmadd_pd(element_10100_vec_real, row11_vec, data_real);
1491 data_imag = _mm256_fmadd_pd(element_10100_vec_imag, row11_vec, data_imag);
1492 data_real = _mm256_fmadd_pd(element_10110_vec_real, row12_vec, data_real);
1493 data_imag = _mm256_fmadd_pd(element_10110_vec_imag, row12_vec, data_imag);
1494 data_real = _mm256_fmadd_pd(element_11000_vec_real, row13_vec, data_real);
1495 data_imag = _mm256_fmadd_pd(element_11000_vec_imag, row13_vec, data_imag);
1496 data_real = _mm256_fmadd_pd(element_11010_vec_real, row14_vec, data_real);
1497 data_imag = _mm256_fmadd_pd(element_11010_vec_imag, row14_vec, data_imag);
1498 data_real = _mm256_fmadd_pd(element_11100_vec_real, row15_vec, data_real);
1499 data_imag = _mm256_fmadd_pd(element_11100_vec_imag, row15_vec, data_imag);
1500 data_real = _mm256_fmadd_pd(element_11110_vec_real, row16_vec, data_real);
1501 data_imag = _mm256_fmadd_pd(element_11110_vec_imag, row16_vec, data_imag);
1503 __m256d final_vec = _mm256_hadd_pd(data_real, data_imag);
1504 final_vec = _mm256_permute4x64_pd(final_vec, 0b11011000);
1505 final_vec = _mm256_hadd_pd(final_vec, final_vec);
1506 __m128d low128 = _mm256_castpd256_pd128(final_vec);
1507 __m128d high128 = _mm256_extractf128_pd(final_vec, 1);
1508 results[mult_idx].
real = _mm_cvtsd_f64(low128);
1509 results[mult_idx].
imag = _mm_cvtsd_f64(high128);
1511 input[current_idx_outer_loc] = results[0];
1512 input[current_idx_inner_loc] = results[1];
1513 input[current_idx_middle1_loc] = results[2];
1514 input[current_idx_middle1_inner_loc] = results[3];
1515 input[current_idx_middle2_loc] = results[4];
1516 input[current_idx_middle2_inner_loc] = results[5];
1517 input[current_idx_middle12_loc] = results[6];
1518 input[current_idx_middle12_inner_loc] = results[7];
1519 input[current_idx_middle3_loc] = results[8];
1520 input[current_idx_middle3_inner_loc] = results[9];
1521 input[current_idx_middle13_loc] = results[10];
1522 input[current_idx_middle13_inner_loc] = results[11];
1523 input[current_idx_middle23_loc] = results[12];
1524 input[current_idx_middle23_inner_loc] = results[13];
1525 input[current_idx_middle123_loc] = results[14];
1526 input[current_idx_middle123_inner_loc] = results[15];
1527 input[current_idx_outer_pair_loc] = results[16];
1528 input[current_idx_inner_pair_loc] = results[17];
1529 input[current_idx_middle1_pair_loc] = results[18];
1530 input[current_idx_middle1_inner_pair_loc] = results[19];
1531 input[current_idx_middle2_pair_loc] = results[20];
1532 input[current_idx_middle2_inner_pair_loc] = results[21];
1533 input[current_idx_middle12_pair_loc] = results[22];
1534 input[current_idx_middle12_inner_pair_loc] = results[23];
1535 input[current_idx_middle3_pair_loc] = results[24];
1536 input[current_idx_middle3_inner_pair_loc] = results[25];
1537 input[current_idx_middle13_pair_loc] = results[26];
1538 input[current_idx_middle13_inner_pair_loc] = results[27];
1539 input[current_idx_middle23_pair_loc] = results[28];
1540 input[current_idx_middle23_inner_pair_loc] = results[29];
1541 input[current_idx_middle123_pair_loc] = results[30];
1542 input[current_idx_middle123_inner_pair_loc] = results[31];
1554 int inner_qbit = involved_qbits[0];
1555 int outer_qbit = involved_qbits[1];
1556 int index_step_outer = 1 << outer_qbit;
1557 int index_step_inner = 1 << inner_qbit;
1559 int num_qubits = (
int)std::log2(matrix_size);
1560 std::vector<int> is_target(num_qubits, 0);
1561 for (
int q : involved_qbits) is_target[q] = 1;
1563 std::vector<int> non_targets;
1564 non_targets.reserve(num_qubits - 2);
1565 for (
int q = 0; q < num_qubits; ++q) {
1566 if (!is_target[q]) non_targets.push_back(q);
1570 __m256d mv00 = _mm256_set_pd(-two_qbit_unitary[1].imag, two_qbit_unitary[1].
real, -two_qbit_unitary[0].imag, two_qbit_unitary[0].real);
1571 __m256d mv01 = _mm256_set_pd( two_qbit_unitary[1].real, two_qbit_unitary[1].imag, two_qbit_unitary[0].real, two_qbit_unitary[0].imag);
1572 __m256d mv20 = _mm256_set_pd(-two_qbit_unitary[3].imag, two_qbit_unitary[3].real, -two_qbit_unitary[2].imag, two_qbit_unitary[2].real);
1573 __m256d mv21 = _mm256_set_pd( two_qbit_unitary[3].real, two_qbit_unitary[3].imag, two_qbit_unitary[2].real, two_qbit_unitary[2].imag);
1574 __m256d mv40 = _mm256_set_pd(-two_qbit_unitary[5].imag, two_qbit_unitary[5].real, -two_qbit_unitary[4].imag, two_qbit_unitary[4].real);
1575 __m256d mv41 = _mm256_set_pd( two_qbit_unitary[5].real, two_qbit_unitary[5].imag, two_qbit_unitary[4].real, two_qbit_unitary[4].imag);
1576 __m256d mv60 = _mm256_set_pd(-two_qbit_unitary[7].imag, two_qbit_unitary[7].real, -two_qbit_unitary[6].imag, two_qbit_unitary[6].real);
1577 __m256d mv61 = _mm256_set_pd( two_qbit_unitary[7].real, two_qbit_unitary[7].imag, two_qbit_unitary[6].real, two_qbit_unitary[6].imag);
1578 __m256d mv80 = _mm256_set_pd(-two_qbit_unitary[9].imag, two_qbit_unitary[9].real, -two_qbit_unitary[8].imag, two_qbit_unitary[8].real);
1579 __m256d mv81 = _mm256_set_pd( two_qbit_unitary[9].real, two_qbit_unitary[9].imag, two_qbit_unitary[8].real, two_qbit_unitary[8].imag);
1580 __m256d mv100 = _mm256_set_pd(-two_qbit_unitary[11].imag, two_qbit_unitary[11].real, -two_qbit_unitary[10].imag, two_qbit_unitary[10].real);
1581 __m256d mv101 = _mm256_set_pd( two_qbit_unitary[11].real, two_qbit_unitary[11].imag, two_qbit_unitary[10].real, two_qbit_unitary[10].imag);
1582 __m256d mv120 = _mm256_set_pd(-two_qbit_unitary[13].imag, two_qbit_unitary[13].real, -two_qbit_unitary[12].imag, two_qbit_unitary[12].real);
1583 __m256d mv121 = _mm256_set_pd( two_qbit_unitary[13].real, two_qbit_unitary[13].imag, two_qbit_unitary[12].real, two_qbit_unitary[12].imag);
1584 __m256d mv140 = _mm256_set_pd(-two_qbit_unitary[15].imag, two_qbit_unitary[15].real, -two_qbit_unitary[14].imag, two_qbit_unitary[14].real);
1585 __m256d mv141 = _mm256_set_pd( two_qbit_unitary[15].real, two_qbit_unitary[15].imag, two_qbit_unitary[14].real, two_qbit_unitary[14].imag);
1588 #pragma omp parallel for schedule(static) 1590 #pragma omp parallel for simd schedule(static) 1592 for (
int block_idx = 0; block_idx < matrix_size>>2; block_idx++) {
1595 for (
size_t i = 0; i < non_targets.size(); ++i) {
1596 if (block_idx & (1ULL << i)) {
1597 base |= (1 << non_targets[i]);
1601 int current_idx_outer_loc = base ;
1602 int current_idx_outer_pair_loc = base | index_step_outer;
1605 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
1606 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
1609 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
1610 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
1615 __m256d data_u0 = _mm256_mul_pd(element_outer_vec, mv00);
1616 __m256d data_u1 = _mm256_mul_pd(element_outer_vec, mv01);
1617 __m256d data_u3 = _mm256_mul_pd(element_outer_pair_vec, mv20);
1618 __m256d data_u4 = _mm256_mul_pd(element_outer_pair_vec, mv21);
1619 __m256d data_u5 = _mm256_add_pd(data_u3, data_u0);
1620 __m256d data_u2 = _mm256_add_pd(data_u1, data_u4);
1621 __m256d data_u7 = _mm256_hadd_pd(data_u5, data_u2);
1622 __m256d data_u8 = _mm256_permute4x64_pd(data_u7, 0b11011000);
1623 __m256d data_u6 = _mm256_hadd_pd(data_u8, data_u8);
1625 __m256d data_d0 = _mm256_mul_pd(element_outer_vec, mv40);
1626 __m256d data_d1 = _mm256_mul_pd(element_outer_vec, mv41);
1627 __m256d data_d3 = _mm256_mul_pd(element_outer_pair_vec, mv60);
1628 __m256d data_d4 = _mm256_mul_pd(element_outer_pair_vec, mv61);
1629 __m256d data_d5 = _mm256_add_pd(data_d3, data_d0);
1630 __m256d data_d6 = _mm256_add_pd(data_d1, data_d4);
1631 data_d6 = _mm256_hadd_pd(data_d5, data_d6);
1632 data_d6 = _mm256_permute4x64_pd(data_d6, 0b11011000);
1633 data_d6 = _mm256_hadd_pd(data_d6, data_d6);
1636 __m256d data_e0 = _mm256_mul_pd(element_outer_vec, mv80);
1637 __m256d data_e1 = _mm256_mul_pd(element_outer_vec, mv81);
1638 __m256d data_e3 = _mm256_mul_pd(element_outer_pair_vec, mv100);
1639 __m256d data_e4 = _mm256_mul_pd(element_outer_pair_vec, mv101);
1640 __m256d data_e5 = _mm256_add_pd(data_e3, data_e0);
1641 __m256d data_e6 = _mm256_add_pd(data_e1, data_e4);
1642 data_e6 = _mm256_hadd_pd(data_e5, data_e6);
1643 data_e6 = _mm256_permute4x64_pd(data_e6, 0b11011000);
1644 data_e6 = _mm256_hadd_pd(data_e6, data_e6);
1647 __m256d data_f0 = _mm256_mul_pd(element_outer_vec, mv120);
1648 __m256d data_f1 = _mm256_mul_pd(element_outer_vec, mv121);
1649 __m256d data_f3 = _mm256_mul_pd(element_outer_pair_vec, mv140);
1650 __m256d data_f4 = _mm256_mul_pd(element_outer_pair_vec, mv141);
1651 __m256d data_f5 = _mm256_add_pd(data_f3, data_f0);
1652 __m256d data_f6 = _mm256_add_pd(data_f1, data_f4);
1653 data_f6 = _mm256_hadd_pd(data_f5, data_f6);
1654 data_f6 = _mm256_permute4x64_pd(data_f6, 0b11011000);
1655 data_f6 = _mm256_hadd_pd(data_f6, data_f6);
1658 __m128d low128u = _mm256_castpd256_pd128(data_u6);
1659 __m128d high128u = _mm256_extractf128_pd(data_u6, 1);
1661 input[current_idx_outer_loc].real = _mm_cvtsd_f64(low128u);
1662 input[current_idx_outer_loc].imag = _mm_cvtsd_f64(high128u);
1664 __m128d low128d = _mm256_castpd256_pd128(data_d6);
1665 __m128d high128d = _mm256_extractf128_pd(data_d6, 1);
1666 input[current_idx_outer_loc + 1].real = _mm_cvtsd_f64(low128d);
1667 input[current_idx_outer_loc + 1].imag = _mm_cvtsd_f64(high128d);
1669 __m128d low128e = _mm256_castpd256_pd128(data_e6);
1670 __m128d high128e = _mm256_extractf128_pd(data_e6, 1);
1671 input[current_idx_outer_pair_loc].real = _mm_cvtsd_f64(low128e);
1672 input[current_idx_outer_pair_loc].imag = _mm_cvtsd_f64(high128e);
1674 __m128d low128f = _mm256_castpd256_pd128(data_f6);
1675 __m128d high128f = _mm256_extractf128_pd(data_f6, 1);
1676 input[current_idx_outer_pair_loc + 1].real = _mm_cvtsd_f64(low128f);
1677 input[current_idx_outer_pair_loc + 1].imag = _mm_cvtsd_f64(high128f);
1682 __m256d mv00 = _mm256_set_pd(-two_qbit_unitary[0].imag, two_qbit_unitary[0].
real, -two_qbit_unitary[0].imag, two_qbit_unitary[0].real);
1683 __m256d mv01 = _mm256_set_pd( two_qbit_unitary[0].real, two_qbit_unitary[0].imag, two_qbit_unitary[0].real, two_qbit_unitary[0].imag);
1684 __m256d mv10 = _mm256_set_pd(-two_qbit_unitary[1].imag, two_qbit_unitary[1].real, -two_qbit_unitary[1].imag, two_qbit_unitary[1].real);
1685 __m256d mv11 = _mm256_set_pd( two_qbit_unitary[1].real, two_qbit_unitary[1].imag, two_qbit_unitary[1].real, two_qbit_unitary[1].imag);
1686 __m256d mv20 = _mm256_set_pd(-two_qbit_unitary[2].imag, two_qbit_unitary[2].real, -two_qbit_unitary[2].imag, two_qbit_unitary[2].real);
1687 __m256d mv21 = _mm256_set_pd( two_qbit_unitary[2].real, two_qbit_unitary[2].imag, two_qbit_unitary[2].real, two_qbit_unitary[2].imag);
1688 __m256d mv30 = _mm256_set_pd(-two_qbit_unitary[3].imag, two_qbit_unitary[3].real, -two_qbit_unitary[3].imag, two_qbit_unitary[3].real);
1689 __m256d mv31 = _mm256_set_pd( two_qbit_unitary[3].real, two_qbit_unitary[3].imag, two_qbit_unitary[3].real, two_qbit_unitary[3].imag);
1690 __m256d mv40 = _mm256_set_pd(-two_qbit_unitary[4].imag, two_qbit_unitary[4].real, -two_qbit_unitary[4].imag, two_qbit_unitary[4].real);
1691 __m256d mv41 = _mm256_set_pd( two_qbit_unitary[4].real, two_qbit_unitary[4].imag, two_qbit_unitary[4].real, two_qbit_unitary[4].imag);
1692 __m256d mv50 = _mm256_set_pd(-two_qbit_unitary[5].imag, two_qbit_unitary[5].real, -two_qbit_unitary[5].imag, two_qbit_unitary[5].real);
1693 __m256d mv51 = _mm256_set_pd( two_qbit_unitary[5].real, two_qbit_unitary[5].imag, two_qbit_unitary[5].real, two_qbit_unitary[5].imag);
1694 __m256d mv60 = _mm256_set_pd(-two_qbit_unitary[6].imag, two_qbit_unitary[6].real, -two_qbit_unitary[6].imag, two_qbit_unitary[6].real);
1695 __m256d mv61 = _mm256_set_pd( two_qbit_unitary[6].real, two_qbit_unitary[6].imag, two_qbit_unitary[6].real, two_qbit_unitary[6].imag);
1696 __m256d mv70 = _mm256_set_pd(-two_qbit_unitary[7].imag, two_qbit_unitary[7].real, -two_qbit_unitary[7].imag, two_qbit_unitary[7].real);
1697 __m256d mv71 = _mm256_set_pd( two_qbit_unitary[7].real, two_qbit_unitary[7].imag, two_qbit_unitary[7].real, two_qbit_unitary[7].imag);
1698 __m256d mv80 = _mm256_set_pd(-two_qbit_unitary[8].imag, two_qbit_unitary[8].real, -two_qbit_unitary[8].imag, two_qbit_unitary[8].real);
1699 __m256d mv81 = _mm256_set_pd( two_qbit_unitary[8].real, two_qbit_unitary[8].imag, two_qbit_unitary[8].real, two_qbit_unitary[8].imag);
1700 __m256d mv90 = _mm256_set_pd(-two_qbit_unitary[9].imag, two_qbit_unitary[9].real, -two_qbit_unitary[9].imag, two_qbit_unitary[9].real);
1701 __m256d mv91 = _mm256_set_pd( two_qbit_unitary[9].real, two_qbit_unitary[9].imag, two_qbit_unitary[9].real, two_qbit_unitary[9].imag);
1702 __m256d mv100 = _mm256_set_pd(-two_qbit_unitary[10].imag, two_qbit_unitary[10].real, -two_qbit_unitary[10].imag, two_qbit_unitary[10].real);
1703 __m256d mv101 = _mm256_set_pd( two_qbit_unitary[10].real, two_qbit_unitary[10].imag, two_qbit_unitary[10].real, two_qbit_unitary[10].imag);
1704 __m256d mv110 = _mm256_set_pd(-two_qbit_unitary[11].imag, two_qbit_unitary[11].real, -two_qbit_unitary[11].imag, two_qbit_unitary[11].real);
1705 __m256d mv111 = _mm256_set_pd( two_qbit_unitary[11].real, two_qbit_unitary[11].imag, two_qbit_unitary[11].real, two_qbit_unitary[11].imag);
1706 __m256d mv120 = _mm256_set_pd(-two_qbit_unitary[12].imag, two_qbit_unitary[12].real, -two_qbit_unitary[12].imag, two_qbit_unitary[12].real);
1707 __m256d mv121 = _mm256_set_pd( two_qbit_unitary[12].real, two_qbit_unitary[12].imag, two_qbit_unitary[12].real, two_qbit_unitary[12].imag);
1708 __m256d mv130 = _mm256_set_pd(-two_qbit_unitary[13].imag, two_qbit_unitary[13].real, -two_qbit_unitary[13].imag, two_qbit_unitary[13].real);
1709 __m256d mv131 = _mm256_set_pd( two_qbit_unitary[13].real, two_qbit_unitary[13].imag, two_qbit_unitary[13].real, two_qbit_unitary[13].imag);
1710 __m256d mv140 = _mm256_set_pd(-two_qbit_unitary[14].imag, two_qbit_unitary[14].real, -two_qbit_unitary[14].imag, two_qbit_unitary[14].real);
1711 __m256d mv141 = _mm256_set_pd( two_qbit_unitary[14].real, two_qbit_unitary[14].imag, two_qbit_unitary[14].real, two_qbit_unitary[14].imag);
1712 __m256d mv150 = _mm256_set_pd(-two_qbit_unitary[15].imag, two_qbit_unitary[15].real, -two_qbit_unitary[15].imag, two_qbit_unitary[15].real);
1713 __m256d mv151 = _mm256_set_pd( two_qbit_unitary[15].real, two_qbit_unitary[15].imag, two_qbit_unitary[15].real, two_qbit_unitary[15].imag);
1716 #pragma omp parallel for schedule(static) 1718 #pragma omp parallel for simd schedule(static) 1720 for (
int block_idx = 0; block_idx < matrix_size>>2; block_idx+=2) {
1723 for (
size_t i = 0; i < non_targets.size(); ++i) {
1724 if (block_idx & (1ULL << i)) {
1725 base |= (1 << non_targets[i]);
1730 double* element_outer = (
double*)input.
get_data() + 2 * base;
1731 double* element_inner = (
double*)input.
get_data() + 2 * ( base | index_step_inner);
1733 double* element_outer_pair = (
double*)input.
get_data() + 2 * (base | index_step_outer);
1734 double* element_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_outer);
1737 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
1738 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
1740 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
1741 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
1743 __m256d data_u0 = _mm256_mul_pd(element_outer_vec, mv00);
1744 __m256d data_u1 = _mm256_mul_pd(element_inner_vec, mv10);
1745 __m256d data_u2 = _mm256_mul_pd(element_outer_vec, mv01);
1746 __m256d data_u3 = _mm256_mul_pd(element_inner_vec, mv11);
1747 __m256d data_u4 = _mm256_mul_pd(element_outer_pair_vec, mv20);
1748 __m256d data_u5 = _mm256_mul_pd(element_inner_pair_vec, mv30);
1749 __m256d data_u6 = _mm256_mul_pd(element_outer_pair_vec, mv21);
1750 __m256d data_u7 = _mm256_mul_pd(element_inner_pair_vec, mv31);
1751 __m256d data_u8 = _mm256_hadd_pd(data_u0, data_u2);
1752 __m256d data_u9 = _mm256_hadd_pd(data_u1, data_u3);
1753 __m256d data_u10 = _mm256_hadd_pd(data_u4, data_u6);
1754 __m256d data_u11 = _mm256_hadd_pd(data_u5, data_u7);
1755 __m256d data_u = _mm256_add_pd(data_u8, data_u9);
1756 data_u = _mm256_add_pd(data_u, data_u10);
1757 data_u = _mm256_add_pd(data_u, data_u11);
1759 __m256d data_d0 = _mm256_mul_pd(element_outer_vec, mv40);
1760 __m256d data_d1 = _mm256_mul_pd(element_inner_vec, mv50);
1761 __m256d data_d2 = _mm256_mul_pd(element_outer_vec, mv41);
1762 __m256d data_d3 = _mm256_mul_pd(element_inner_vec, mv51);
1763 __m256d data_d4 = _mm256_mul_pd(element_outer_pair_vec, mv60);
1764 __m256d data_d5 = _mm256_mul_pd(element_inner_pair_vec, mv70);
1765 __m256d data_d6 = _mm256_mul_pd(element_outer_pair_vec, mv61);
1766 __m256d data_d7 = _mm256_mul_pd(element_inner_pair_vec, mv71);
1767 __m256d data_d8 = _mm256_hadd_pd(data_d0, data_d2);
1768 __m256d data_d9 = _mm256_hadd_pd(data_d1, data_d3);
1769 __m256d data_d10 = _mm256_hadd_pd(data_d4, data_d6);
1770 __m256d data_d11 = _mm256_hadd_pd(data_d5, data_d7);
1771 __m256d data_d = _mm256_add_pd(data_d8, data_d9);
1772 data_d = _mm256_add_pd(data_d, data_d10);
1773 data_d = _mm256_add_pd(data_d, data_d11);
1775 __m256d data_e0 = _mm256_mul_pd(element_outer_vec, mv80);
1776 __m256d data_e1 = _mm256_mul_pd(element_inner_vec, mv90);
1777 __m256d data_e2 = _mm256_mul_pd(element_outer_vec, mv81);
1778 __m256d data_e3 = _mm256_mul_pd(element_inner_vec, mv91);
1779 __m256d data_e4 = _mm256_mul_pd(element_outer_pair_vec, mv100);
1780 __m256d data_e5 = _mm256_mul_pd(element_inner_pair_vec, mv110);
1781 __m256d data_e6 = _mm256_mul_pd(element_outer_pair_vec, mv101);
1782 __m256d data_e7 = _mm256_mul_pd(element_inner_pair_vec, mv111);
1783 __m256d data_e8 = _mm256_hadd_pd(data_e0, data_e2);
1784 __m256d data_e9 = _mm256_hadd_pd(data_e1, data_e3);
1785 __m256d data_e10 = _mm256_hadd_pd(data_e4, data_e6);
1786 __m256d data_e11 = _mm256_hadd_pd(data_e5, data_e7);
1787 __m256d data_e = _mm256_add_pd(data_e8, data_e9);
1788 data_e = _mm256_add_pd(data_e, data_e10);
1789 data_e = _mm256_add_pd(data_e, data_e11);
1791 __m256d data_f0 = _mm256_mul_pd(element_outer_vec, mv120);
1792 __m256d data_f1 = _mm256_mul_pd(element_inner_vec, mv130);
1793 __m256d data_f2 = _mm256_mul_pd(element_outer_vec, mv121);
1794 __m256d data_f3 = _mm256_mul_pd(element_inner_vec, mv131);
1795 __m256d data_f4 = _mm256_mul_pd(element_outer_pair_vec, mv140);
1796 __m256d data_f5 = _mm256_mul_pd(element_inner_pair_vec, mv150);
1797 __m256d data_f6 = _mm256_mul_pd(element_outer_pair_vec, mv141);
1798 __m256d data_f7 = _mm256_mul_pd(element_inner_pair_vec, mv151);
1799 __m256d data_f8 = _mm256_hadd_pd(data_f0, data_f2);
1800 __m256d data_f9 = _mm256_hadd_pd(data_f1, data_f3);
1801 __m256d data_f10 = _mm256_hadd_pd(data_f4, data_f6);
1802 __m256d data_f11 = _mm256_hadd_pd(data_f5, data_f7);
1803 __m256d data_f = _mm256_add_pd(data_f8, data_f9);
1804 data_f = _mm256_add_pd(data_f, data_f10);
1805 data_f = _mm256_add_pd(data_f, data_f11);
1807 _mm256_storeu_pd(element_outer, data_u);
1808 _mm256_storeu_pd(element_inner, data_d);
1809 _mm256_storeu_pd(element_outer_pair, data_e);
1810 _mm256_storeu_pd(element_inner_pair, data_f);
1826 int inner_qbit = involved_qbits[0];
1827 int outer_qbit = involved_qbits[1];
1828 int index_step_outer = 1 << outer_qbit;
1829 int index_step_inner = 1 << inner_qbit;
1831 int num_qubits = (
int)std::log2(matrix_size);
1832 std::vector<int> is_target(num_qubits, 0);
1833 for (
int q : involved_qbits) is_target[q] = 1;
1835 std::vector<int> non_targets;
1836 non_targets.reserve(num_qubits - 2);
1837 for (
int q = 0; q < num_qubits; ++q) {
1838 if (!is_target[q]) non_targets.push_back(q);
1841 if (inner_qbit == 0) {
1843 __m256d mv00 = _mm256_set_pd(-two_qbit_unitary[1].imag, two_qbit_unitary[1].
real, -two_qbit_unitary[0].imag, two_qbit_unitary[0].real);
1844 __m256d mv01 = _mm256_set_pd( two_qbit_unitary[1].real, two_qbit_unitary[1].imag, two_qbit_unitary[0].real, two_qbit_unitary[0].imag);
1845 __m256d mv20 = _mm256_set_pd(-two_qbit_unitary[3].imag, two_qbit_unitary[3].real, -two_qbit_unitary[2].imag, two_qbit_unitary[2].real);
1846 __m256d mv21 = _mm256_set_pd( two_qbit_unitary[3].real, two_qbit_unitary[3].imag, two_qbit_unitary[2].real, two_qbit_unitary[2].imag);
1847 __m256d mv40 = _mm256_set_pd(-two_qbit_unitary[5].imag, two_qbit_unitary[5].real, -two_qbit_unitary[4].imag, two_qbit_unitary[4].real);
1848 __m256d mv41 = _mm256_set_pd( two_qbit_unitary[5].real, two_qbit_unitary[5].imag, two_qbit_unitary[4].real, two_qbit_unitary[4].imag);
1849 __m256d mv60 = _mm256_set_pd(-two_qbit_unitary[7].imag, two_qbit_unitary[7].real, -two_qbit_unitary[6].imag, two_qbit_unitary[6].real);
1850 __m256d mv61 = _mm256_set_pd( two_qbit_unitary[7].real, two_qbit_unitary[7].imag, two_qbit_unitary[6].real, two_qbit_unitary[6].imag);
1851 __m256d mv80 = _mm256_set_pd(-two_qbit_unitary[9].imag, two_qbit_unitary[9].real, -two_qbit_unitary[8].imag, two_qbit_unitary[8].real);
1852 __m256d mv81 = _mm256_set_pd( two_qbit_unitary[9].real, two_qbit_unitary[9].imag, two_qbit_unitary[8].real, two_qbit_unitary[8].imag);
1853 __m256d mv100 = _mm256_set_pd(-two_qbit_unitary[11].imag, two_qbit_unitary[11].real, -two_qbit_unitary[10].imag, two_qbit_unitary[10].real);
1854 __m256d mv101 = _mm256_set_pd( two_qbit_unitary[11].real, two_qbit_unitary[11].imag, two_qbit_unitary[10].real, two_qbit_unitary[10].imag);
1855 __m256d mv120 = _mm256_set_pd(-two_qbit_unitary[13].imag, two_qbit_unitary[13].real, -two_qbit_unitary[12].imag, two_qbit_unitary[12].real);
1856 __m256d mv121 = _mm256_set_pd( two_qbit_unitary[13].real, two_qbit_unitary[13].imag, two_qbit_unitary[12].real, two_qbit_unitary[12].imag);
1857 __m256d mv140 = _mm256_set_pd(-two_qbit_unitary[15].imag, two_qbit_unitary[15].real, -two_qbit_unitary[14].imag, two_qbit_unitary[14].real);
1858 __m256d mv141 = _mm256_set_pd( two_qbit_unitary[15].real, two_qbit_unitary[15].imag, two_qbit_unitary[14].real, two_qbit_unitary[14].imag);
1862 tbb::blocked_range<int>(0, matrix_size >> 2),
1863 [&](
const tbb::blocked_range<int>& range) {
1864 for (
int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
1867 for (
size_t i = 0; i < non_targets.size(); ++i) {
1868 if (block_idx & (1ULL << i)) {
1869 base |= (1 << non_targets[i]);
1873 int current_idx_outer_loc = base;
1874 int current_idx_outer_pair_loc = base | index_step_outer;
1877 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
1878 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
1881 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
1882 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
1887 __m256d data_u0 = _mm256_mul_pd(element_outer_vec, mv00);
1888 __m256d data_u1 = _mm256_mul_pd(element_outer_vec, mv01);
1889 __m256d data_u3 = _mm256_mul_pd(element_outer_pair_vec, mv20);
1890 __m256d data_u4 = _mm256_mul_pd(element_outer_pair_vec, mv21);
1891 __m256d data_u5 = _mm256_add_pd(data_u3, data_u0);
1892 __m256d data_u2 = _mm256_add_pd(data_u1, data_u4);
1893 __m256d data_u7 = _mm256_hadd_pd(data_u5, data_u2);
1894 __m256d data_u8 = _mm256_permute4x64_pd(data_u7, 0b11011000);
1895 __m256d data_u6 = _mm256_hadd_pd(data_u8, data_u8);
1897 __m256d data_d0 = _mm256_mul_pd(element_outer_vec, mv40);
1898 __m256d data_d1 = _mm256_mul_pd(element_outer_vec, mv41);
1899 __m256d data_d3 = _mm256_mul_pd(element_outer_pair_vec, mv60);
1900 __m256d data_d4 = _mm256_mul_pd(element_outer_pair_vec, mv61);
1901 __m256d data_d5 = _mm256_add_pd(data_d3, data_d0);
1902 __m256d data_d6 = _mm256_add_pd(data_d1, data_d4);
1903 data_d6 = _mm256_hadd_pd(data_d5, data_d6);
1904 data_d6 = _mm256_permute4x64_pd(data_d6, 0b11011000);
1905 data_d6 = _mm256_hadd_pd(data_d6, data_d6);
1908 __m256d data_e0 = _mm256_mul_pd(element_outer_vec, mv80);
1909 __m256d data_e1 = _mm256_mul_pd(element_outer_vec, mv81);
1910 __m256d data_e3 = _mm256_mul_pd(element_outer_pair_vec, mv100);
1911 __m256d data_e4 = _mm256_mul_pd(element_outer_pair_vec, mv101);
1912 __m256d data_e5 = _mm256_add_pd(data_e3, data_e0);
1913 __m256d data_e6 = _mm256_add_pd(data_e1, data_e4);
1914 data_e6 = _mm256_hadd_pd(data_e5, data_e6);
1915 data_e6 = _mm256_permute4x64_pd(data_e6, 0b11011000);
1916 data_e6 = _mm256_hadd_pd(data_e6, data_e6);
1919 __m256d data_f0 = _mm256_mul_pd(element_outer_vec, mv120);
1920 __m256d data_f1 = _mm256_mul_pd(element_outer_vec, mv121);
1921 __m256d data_f3 = _mm256_mul_pd(element_outer_pair_vec, mv140);
1922 __m256d data_f4 = _mm256_mul_pd(element_outer_pair_vec, mv141);
1923 __m256d data_f5 = _mm256_add_pd(data_f3, data_f0);
1924 __m256d data_f6 = _mm256_add_pd(data_f1, data_f4);
1925 data_f6 = _mm256_hadd_pd(data_f5, data_f6);
1926 data_f6 = _mm256_permute4x64_pd(data_f6, 0b11011000);
1927 data_f6 = _mm256_hadd_pd(data_f6, data_f6);
1930 __m128d low128u = _mm256_castpd256_pd128(data_u6);
1931 __m128d high128u = _mm256_extractf128_pd(data_u6, 1);
1933 input[current_idx_outer_loc].real = _mm_cvtsd_f64(low128u);
1934 input[current_idx_outer_loc].imag = _mm_cvtsd_f64(high128u);
1936 __m128d low128d = _mm256_castpd256_pd128(data_d6);
1937 __m128d high128d = _mm256_extractf128_pd(data_d6, 1);
1938 input[current_idx_outer_loc + 1].real = _mm_cvtsd_f64(low128d);
1939 input[current_idx_outer_loc + 1].imag = _mm_cvtsd_f64(high128d);
1941 __m128d low128e = _mm256_castpd256_pd128(data_e6);
1942 __m128d high128e = _mm256_extractf128_pd(data_e6, 1);
1943 input[current_idx_outer_pair_loc].real = _mm_cvtsd_f64(low128e);
1944 input[current_idx_outer_pair_loc].imag = _mm_cvtsd_f64(high128e);
1946 __m128d low128f = _mm256_castpd256_pd128(data_f6);
1947 __m128d high128f = _mm256_extractf128_pd(data_f6, 1);
1948 input[current_idx_outer_pair_loc + 1].real = _mm_cvtsd_f64(low128f);
1949 input[current_idx_outer_pair_loc + 1].imag = _mm_cvtsd_f64(high128f);
1956 __m256d mv00 = _mm256_set_pd(-two_qbit_unitary[0].imag, two_qbit_unitary[0].
real, -two_qbit_unitary[0].imag, two_qbit_unitary[0].real);
1957 __m256d mv01 = _mm256_set_pd( two_qbit_unitary[0].real, two_qbit_unitary[0].imag, two_qbit_unitary[0].real, two_qbit_unitary[0].imag);
1958 __m256d mv10 = _mm256_set_pd(-two_qbit_unitary[1].imag, two_qbit_unitary[1].real, -two_qbit_unitary[1].imag, two_qbit_unitary[1].real);
1959 __m256d mv11 = _mm256_set_pd( two_qbit_unitary[1].real, two_qbit_unitary[1].imag, two_qbit_unitary[1].real, two_qbit_unitary[1].imag);
1960 __m256d mv20 = _mm256_set_pd(-two_qbit_unitary[2].imag, two_qbit_unitary[2].real, -two_qbit_unitary[2].imag, two_qbit_unitary[2].real);
1961 __m256d mv21 = _mm256_set_pd( two_qbit_unitary[2].real, two_qbit_unitary[2].imag, two_qbit_unitary[2].real, two_qbit_unitary[2].imag);
1962 __m256d mv30 = _mm256_set_pd(-two_qbit_unitary[3].imag, two_qbit_unitary[3].real, -two_qbit_unitary[3].imag, two_qbit_unitary[3].real);
1963 __m256d mv31 = _mm256_set_pd( two_qbit_unitary[3].real, two_qbit_unitary[3].imag, two_qbit_unitary[3].real, two_qbit_unitary[3].imag);
1964 __m256d mv40 = _mm256_set_pd(-two_qbit_unitary[4].imag, two_qbit_unitary[4].real, -two_qbit_unitary[4].imag, two_qbit_unitary[4].real);
1965 __m256d mv41 = _mm256_set_pd( two_qbit_unitary[4].real, two_qbit_unitary[4].imag, two_qbit_unitary[4].real, two_qbit_unitary[4].imag);
1966 __m256d mv50 = _mm256_set_pd(-two_qbit_unitary[5].imag, two_qbit_unitary[5].real, -two_qbit_unitary[5].imag, two_qbit_unitary[5].real);
1967 __m256d mv51 = _mm256_set_pd( two_qbit_unitary[5].real, two_qbit_unitary[5].imag, two_qbit_unitary[5].real, two_qbit_unitary[5].imag);
1968 __m256d mv60 = _mm256_set_pd(-two_qbit_unitary[6].imag, two_qbit_unitary[6].real, -two_qbit_unitary[6].imag, two_qbit_unitary[6].real);
1969 __m256d mv61 = _mm256_set_pd( two_qbit_unitary[6].real, two_qbit_unitary[6].imag, two_qbit_unitary[6].real, two_qbit_unitary[6].imag);
1970 __m256d mv70 = _mm256_set_pd(-two_qbit_unitary[7].imag, two_qbit_unitary[7].real, -two_qbit_unitary[7].imag, two_qbit_unitary[7].real);
1971 __m256d mv71 = _mm256_set_pd( two_qbit_unitary[7].real, two_qbit_unitary[7].imag, two_qbit_unitary[7].real, two_qbit_unitary[7].imag);
1972 __m256d mv80 = _mm256_set_pd(-two_qbit_unitary[8].imag, two_qbit_unitary[8].real, -two_qbit_unitary[8].imag, two_qbit_unitary[8].real);
1973 __m256d mv81 = _mm256_set_pd( two_qbit_unitary[8].real, two_qbit_unitary[8].imag, two_qbit_unitary[8].real, two_qbit_unitary[8].imag);
1974 __m256d mv90 = _mm256_set_pd(-two_qbit_unitary[9].imag, two_qbit_unitary[9].real, -two_qbit_unitary[9].imag, two_qbit_unitary[9].real);
1975 __m256d mv91 = _mm256_set_pd( two_qbit_unitary[9].real, two_qbit_unitary[9].imag, two_qbit_unitary[9].real, two_qbit_unitary[9].imag);
1976 __m256d mv100 = _mm256_set_pd(-two_qbit_unitary[10].imag, two_qbit_unitary[10].real, -two_qbit_unitary[10].imag, two_qbit_unitary[10].real);
1977 __m256d mv101 = _mm256_set_pd( two_qbit_unitary[10].real, two_qbit_unitary[10].imag, two_qbit_unitary[10].real, two_qbit_unitary[10].imag);
1978 __m256d mv110 = _mm256_set_pd(-two_qbit_unitary[11].imag, two_qbit_unitary[11].real, -two_qbit_unitary[11].imag, two_qbit_unitary[11].real);
1979 __m256d mv111 = _mm256_set_pd( two_qbit_unitary[11].real, two_qbit_unitary[11].imag, two_qbit_unitary[11].real, two_qbit_unitary[11].imag);
1980 __m256d mv120 = _mm256_set_pd(-two_qbit_unitary[12].imag, two_qbit_unitary[12].real, -two_qbit_unitary[12].imag, two_qbit_unitary[12].real);
1981 __m256d mv121 = _mm256_set_pd( two_qbit_unitary[12].real, two_qbit_unitary[12].imag, two_qbit_unitary[12].real, two_qbit_unitary[12].imag);
1982 __m256d mv130 = _mm256_set_pd(-two_qbit_unitary[13].imag, two_qbit_unitary[13].real, -two_qbit_unitary[13].imag, two_qbit_unitary[13].real);
1983 __m256d mv131 = _mm256_set_pd( two_qbit_unitary[13].real, two_qbit_unitary[13].imag, two_qbit_unitary[13].real, two_qbit_unitary[13].imag);
1984 __m256d mv140 = _mm256_set_pd(-two_qbit_unitary[14].imag, two_qbit_unitary[14].real, -two_qbit_unitary[14].imag, two_qbit_unitary[14].real);
1985 __m256d mv141 = _mm256_set_pd( two_qbit_unitary[14].real, two_qbit_unitary[14].imag, two_qbit_unitary[14].real, two_qbit_unitary[14].imag);
1986 __m256d mv150 = _mm256_set_pd(-two_qbit_unitary[15].imag, two_qbit_unitary[15].real, -two_qbit_unitary[15].imag, two_qbit_unitary[15].real);
1987 __m256d mv151 = _mm256_set_pd( two_qbit_unitary[15].real, two_qbit_unitary[15].imag, two_qbit_unitary[15].real, two_qbit_unitary[15].imag);
1991 tbb::blocked_range<int>(0, matrix_size >> 2, 2),
1992 [&](
const tbb::blocked_range<int>& range) {
1993 for (
int block_idx = range.begin(); block_idx < range.end(); block_idx += 2) {
1996 for (
size_t i = 0; i < non_targets.size(); ++i) {
1997 if (block_idx & (1ULL << i)) {
1998 base |= (1 << non_targets[i]);
2002 double* element_outer = (
double*)input.
get_data() + 2 * base;
2003 double* element_inner = (
double*)input.
get_data() + 2 * ( base | index_step_inner);
2005 double* element_outer_pair = (
double*)input.
get_data() + 2 * (base | index_step_outer);
2006 double* element_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_outer);
2009 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
2010 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
2012 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
2013 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
2015 __m256d data_u0 = _mm256_mul_pd(element_outer_vec, mv00);
2016 __m256d data_u1 = _mm256_mul_pd(element_inner_vec, mv10);
2017 __m256d data_u2 = _mm256_mul_pd(element_outer_vec, mv01);
2018 __m256d data_u3 = _mm256_mul_pd(element_inner_vec, mv11);
2019 __m256d data_u4 = _mm256_mul_pd(element_outer_pair_vec, mv20);
2020 __m256d data_u5 = _mm256_mul_pd(element_inner_pair_vec, mv30);
2021 __m256d data_u6 = _mm256_mul_pd(element_outer_pair_vec, mv21);
2022 __m256d data_u7 = _mm256_mul_pd(element_inner_pair_vec, mv31);
2023 __m256d data_u8 = _mm256_hadd_pd(data_u0, data_u2);
2024 __m256d data_u9 = _mm256_hadd_pd(data_u1, data_u3);
2025 __m256d data_u10 = _mm256_hadd_pd(data_u4, data_u6);
2026 __m256d data_u11 = _mm256_hadd_pd(data_u5, data_u7);
2027 __m256d data_u = _mm256_add_pd(data_u8, data_u9);
2028 data_u = _mm256_add_pd(data_u, data_u10);
2029 data_u = _mm256_add_pd(data_u, data_u11);
2031 __m256d data_d0 = _mm256_mul_pd(element_outer_vec, mv40);
2032 __m256d data_d1 = _mm256_mul_pd(element_inner_vec, mv50);
2033 __m256d data_d2 = _mm256_mul_pd(element_outer_vec, mv41);
2034 __m256d data_d3 = _mm256_mul_pd(element_inner_vec, mv51);
2035 __m256d data_d4 = _mm256_mul_pd(element_outer_pair_vec, mv60);
2036 __m256d data_d5 = _mm256_mul_pd(element_inner_pair_vec, mv70);
2037 __m256d data_d6 = _mm256_mul_pd(element_outer_pair_vec, mv61);
2038 __m256d data_d7 = _mm256_mul_pd(element_inner_pair_vec, mv71);
2039 __m256d data_d8 = _mm256_hadd_pd(data_d0, data_d2);
2040 __m256d data_d9 = _mm256_hadd_pd(data_d1, data_d3);
2041 __m256d data_d10 = _mm256_hadd_pd(data_d4, data_d6);
2042 __m256d data_d11 = _mm256_hadd_pd(data_d5, data_d7);
2043 __m256d data_d = _mm256_add_pd(data_d8, data_d9);
2044 data_d = _mm256_add_pd(data_d, data_d10);
2045 data_d = _mm256_add_pd(data_d, data_d11);
2047 __m256d data_e0 = _mm256_mul_pd(element_outer_vec, mv80);
2048 __m256d data_e1 = _mm256_mul_pd(element_inner_vec, mv90);
2049 __m256d data_e2 = _mm256_mul_pd(element_outer_vec, mv81);
2050 __m256d data_e3 = _mm256_mul_pd(element_inner_vec, mv91);
2051 __m256d data_e4 = _mm256_mul_pd(element_outer_pair_vec, mv100);
2052 __m256d data_e5 = _mm256_mul_pd(element_inner_pair_vec, mv110);
2053 __m256d data_e6 = _mm256_mul_pd(element_outer_pair_vec, mv101);
2054 __m256d data_e7 = _mm256_mul_pd(element_inner_pair_vec, mv111);
2055 __m256d data_e8 = _mm256_hadd_pd(data_e0, data_e2);
2056 __m256d data_e9 = _mm256_hadd_pd(data_e1, data_e3);
2057 __m256d data_e10 = _mm256_hadd_pd(data_e4, data_e6);
2058 __m256d data_e11 = _mm256_hadd_pd(data_e5, data_e7);
2059 __m256d data_e = _mm256_add_pd(data_e8, data_e9);
2060 data_e = _mm256_add_pd(data_e, data_e10);
2061 data_e = _mm256_add_pd(data_e, data_e11);
2063 __m256d data_f0 = _mm256_mul_pd(element_outer_vec, mv120);
2064 __m256d data_f1 = _mm256_mul_pd(element_inner_vec, mv130);
2065 __m256d data_f2 = _mm256_mul_pd(element_outer_vec, mv121);
2066 __m256d data_f3 = _mm256_mul_pd(element_inner_vec, mv131);
2067 __m256d data_f4 = _mm256_mul_pd(element_outer_pair_vec, mv140);
2068 __m256d data_f5 = _mm256_mul_pd(element_inner_pair_vec, mv150);
2069 __m256d data_f6 = _mm256_mul_pd(element_outer_pair_vec, mv141);
2070 __m256d data_f7 = _mm256_mul_pd(element_inner_pair_vec, mv151);
2071 __m256d data_f8 = _mm256_hadd_pd(data_f0, data_f2);
2072 __m256d data_f9 = _mm256_hadd_pd(data_f1, data_f3);
2073 __m256d data_f10 = _mm256_hadd_pd(data_f4, data_f6);
2074 __m256d data_f11 = _mm256_hadd_pd(data_f5, data_f7);
2075 __m256d data_f = _mm256_add_pd(data_f8, data_f9);
2076 data_f = _mm256_add_pd(data_f, data_f10);
2077 data_f = _mm256_add_pd(data_f, data_f11);
2079 _mm256_storeu_pd(element_outer, data_u);
2080 _mm256_storeu_pd(element_inner, data_d);
2081 _mm256_storeu_pd(element_outer_pair, data_e);
2082 _mm256_storeu_pd(element_inner_pair, data_f);
2096 int inner_qbit = involved_qbits[0];
2097 int middle_qbit = involved_qbits[1];
2098 int outer_qbit = involved_qbits[2];
2100 int index_step_inner = 1 << inner_qbit;
2101 int index_step_middle = 1 << middle_qbit;
2102 int index_step_outer = 1 << outer_qbit;
2105 int qubit_num = (
int) std::log2(input.
rows);
2108 std::vector<int> is_target(qubit_num, 0);
2109 for (
int q : involved_qbits) is_target[q] = 1;
2111 std::vector<int> non_targets;
2112 non_targets.reserve(qubit_num - 3);
2113 for (
int q = 0; q < qubit_num; ++q) {
2114 if (!is_target[q]) non_targets.push_back(q);
2117 if (inner_qbit == 0) {
2137 #pragma omp parallel for schedule(static) 2139 #pragma omp parallel for simd schedule(static) 2141 for (
int iter_idx = 0; iter_idx < matrix_size>>3; iter_idx++) {
2143 for (
size_t i = 0; i < non_targets.size(); ++i) {
2144 if (iter_idx & (1ULL << i)) {
2145 base |= (1 << non_targets[i]);
2149 double* ptr_000 = (
double*)input.
get_data() + 2 * base;
2150 double* ptr_010 = (
double*)input.
get_data() + 2 * (base | index_step_middle);
2151 double* ptr_100 = (
double*)input.
get_data() + 2 * (base | index_step_outer);
2152 double* ptr_110 = (
double*)input.
get_data() + 2 * (base | index_step_middle | index_step_outer);
2154 __m256d element_000_vec = _mm256_loadu_pd(ptr_000);
2155 __m256d element_010_vec = _mm256_loadu_pd(ptr_010);
2156 __m256d element_100_vec = _mm256_loadu_pd(ptr_100);
2157 __m256d element_110_vec = _mm256_loadu_pd(ptr_110);
2179 input[base] = results[0];
2180 input[base+1] = results[1];
2181 input[(base | index_step_middle)] = results[2];
2182 input[(base | index_step_middle)+1] = results[3];
2183 input[(base | index_step_outer)] = results[4];
2184 input[(base | index_step_outer)+1] = results[5];
2185 input[(base | index_step_middle | index_step_outer)] = results[6];
2186 input[(base | index_step_middle | index_step_outer)+1] = results[7];
2208 #pragma omp parallel for schedule(static) 2210 #pragma omp parallel for simd schedule(static) 2212 for (
int iter_idx = 0; iter_idx < matrix_size>>3; iter_idx+=2) {
2215 for (
size_t i = 0; i < non_targets.size(); ++i) {
2216 if (iter_idx & (1ULL << i)) {
2217 base |= (1 << non_targets[i]);
2221 double* element_outer = (
double*)input.
get_data() + 2 * base;
2222 double* element_inner = (
double*)input.
get_data() + 2 * (base | index_step_inner);
2223 double* element_middle = (
double*)input.
get_data() + 2 * (base | index_step_middle);
2224 double* element_middle_inner = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_middle);
2225 double* element_outer_pair = (
double*)input.
get_data() + 2 * (base | index_step_outer);
2226 double* element_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_outer);
2227 double* element_middle_pair = (
double*)input.
get_data() + 2 * (base | index_step_middle | index_step_outer);
2228 double* element_middle_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_middle | index_step_outer);
2230 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
2231 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
2232 __m256d element_middle_vec = _mm256_loadu_pd(element_middle);
2233 __m256d element_middle_inner_vec = _mm256_loadu_pd(element_middle_inner);
2234 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
2235 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
2236 __m256d element_middle_pair_vec = _mm256_loadu_pd(element_middle_pair);
2237 __m256d element_middle_inner_pair_vec = _mm256_loadu_pd(element_middle_inner_pair);
2250 _mm256_storeu_pd(element_outer, data_u);
2251 _mm256_storeu_pd(element_inner, data_d);
2252 _mm256_storeu_pd(element_middle, data_e);
2253 _mm256_storeu_pd(element_middle_inner, data_f);
2254 _mm256_storeu_pd(element_outer_pair, data_g);
2255 _mm256_storeu_pd(element_inner_pair, data_h);
2256 _mm256_storeu_pd(element_middle_pair, data_i);
2257 _mm256_storeu_pd(element_middle_inner_pair, data_j);
2269 int inner_qbit = involved_qbits[0];
2270 int middle_qbit = involved_qbits[1];
2271 int outer_qbit = involved_qbits[2];
2273 int index_step_inner = 1 << inner_qbit;
2274 int index_step_middle = 1 << middle_qbit;
2275 int index_step_outer = 1 << outer_qbit;
2278 int qubit_num = (
int) std::log2(input.
rows);
2281 std::vector<int> is_target(qubit_num, 0);
2282 for (
int q : involved_qbits) is_target[q] = 1;
2284 std::vector<int> non_targets;
2285 non_targets.reserve(qubit_num - 3);
2286 for (
int q = 0; q < qubit_num; ++q) {
2287 if (!is_target[q]) non_targets.push_back(q);
2290 if (inner_qbit == 0) {
2311 tbb::blocked_range<int>(0, matrix_size >> 3),
2312 [&](
const tbb::blocked_range<int>& range) {
2313 for (
int iter_idx = range.begin(); iter_idx != range.end(); ++iter_idx) {
2315 for (
size_t i = 0; i < non_targets.size(); ++i) {
2316 if (iter_idx & (1ULL << i)) {
2317 base |= (1 << non_targets[i]);
2321 double* ptr_000 = (
double*)input.
get_data() + 2 * base;
2322 double* ptr_010 = (
double*)input.
get_data() + 2 * (base | index_step_middle);
2323 double* ptr_100 = (
double*)input.
get_data() + 2 * (base | index_step_outer);
2324 double* ptr_110 = (
double*)input.
get_data() + 2 * (base | index_step_middle | index_step_outer);
2326 __m256d element_000_vec = _mm256_loadu_pd(ptr_000);
2327 __m256d element_010_vec = _mm256_loadu_pd(ptr_010);
2328 __m256d element_100_vec = _mm256_loadu_pd(ptr_100);
2329 __m256d element_110_vec = _mm256_loadu_pd(ptr_110);
2351 input[base] = results[0];
2352 input[base+1] = results[1];
2353 input[(base | index_step_middle)] = results[2];
2354 input[(base | index_step_middle)+1] = results[3];
2355 input[(base | index_step_outer)] = results[4];
2356 input[(base | index_step_outer)+1] = results[5];
2357 input[(base | index_step_middle | index_step_outer)] = results[6];
2358 input[(base | index_step_middle | index_step_outer)+1] = results[7];
2384 tbb::blocked_range<int>(0, matrix_size >> 3, 2),
2385 [&](
const tbb::blocked_range<int>& range) {
2386 for (
int iter_idx = range.begin(); iter_idx < range.end(); iter_idx += 2) {
2388 for (
size_t i = 0; i < non_targets.size(); ++i) {
2389 if (iter_idx & (1ULL << i)) {
2390 base |= (1 << non_targets[i]);
2394 double* element_outer = (
double*)input.
get_data() + 2 * base;
2395 double* element_inner = (
double*)input.
get_data() + 2 * (base | index_step_inner);
2396 double* element_middle = (
double*)input.
get_data() + 2 * (base | index_step_middle);
2397 double* element_middle_inner = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_middle);
2398 double* element_outer_pair = (
double*)input.
get_data() + 2 * (base | index_step_outer);
2399 double* element_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_outer);
2400 double* element_middle_pair = (
double*)input.
get_data() + 2 * (base | index_step_middle | index_step_outer);
2401 double* element_middle_inner_pair = (
double*)input.
get_data() + 2 * (base | index_step_inner | index_step_middle | index_step_outer);
2403 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
2404 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
2405 __m256d element_middle_vec = _mm256_loadu_pd(element_middle);
2406 __m256d element_middle_inner_vec = _mm256_loadu_pd(element_middle_inner);
2407 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
2408 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
2409 __m256d element_middle_pair_vec = _mm256_loadu_pd(element_middle_pair);
2410 __m256d element_middle_inner_pair_vec = _mm256_loadu_pd(element_middle_inner_pair);
2423 _mm256_storeu_pd(element_outer, data_u);
2424 _mm256_storeu_pd(element_inner, data_d);
2425 _mm256_storeu_pd(element_middle, data_e);
2426 _mm256_storeu_pd(element_middle_inner, data_f);
2427 _mm256_storeu_pd(element_outer_pair, data_g);
2428 _mm256_storeu_pd(element_inner_pair, data_h);
2429 _mm256_storeu_pd(element_middle_pair, data_i);
2430 _mm256_storeu_pd(element_middle_inner_pair, data_j);
2445 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
2447 int index_step_inner = 1 << involved_qbits[0];
2448 int index_step_middle1 = 1 << involved_qbits[1];
2449 int index_step_middle2 = 1 << involved_qbits[2];
2450 int index_step_outer = 1 << involved_qbits[3];
2453 int num_qubits = (
int)std::log2(matrix_size);
2454 int num_blocks = matrix_size >> 4;
2457 std::vector<int> is_target(num_qubits, 0);
2458 for (
int q : involved_qbits) is_target[q] = 1;
2460 std::vector<int> non_targets;
2461 non_targets.reserve(num_qubits - 4);
2462 for (
int q = 0; q < num_qubits; ++q) {
2463 if (!is_target[q]) non_targets.push_back(q);
2466 #pragma omp parallel for schedule(static) 2467 for (
int block_idx = 0; block_idx < num_blocks; block_idx++) {
2468 __m256d element_0000_vec_real = _mm256_setzero_pd();
2469 __m256d element_0010_vec_real = _mm256_setzero_pd();
2470 __m256d element_0100_vec_real = _mm256_setzero_pd();
2471 __m256d element_0110_vec_real = _mm256_setzero_pd();
2472 __m256d element_1000_vec_real = _mm256_setzero_pd();
2473 __m256d element_1010_vec_real = _mm256_setzero_pd();
2474 __m256d element_1100_vec_real = _mm256_setzero_pd();
2475 __m256d element_1110_vec_real = _mm256_setzero_pd();
2478 for (
size_t i = 0; i < non_targets.size(); ++i) {
2479 if (block_idx & (1ULL << i)) {
2480 base |= (1 << non_targets[i]);
2484 int current_idx_outer_loc = base;
2485 int current_idx_inner_loc = base | index_step_inner;
2486 int current_idx_middle1_loc = base | index_step_middle1;
2487 int current_idx_middle1_inner_loc = base | index_step_middle1 | index_step_inner;
2488 int current_idx_middle2_loc = base | index_step_middle2;
2489 int current_idx_middle2_inner_loc = base | index_step_middle2 | index_step_inner;
2490 int current_idx_middle12_loc = base | index_step_middle1 | index_step_middle2;
2491 int current_idx_middle12_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_inner;
2493 int current_idx_outer_pair_loc = base | index_step_outer;
2494 int current_idx_inner_pair_loc = base | index_step_outer | index_step_inner;
2495 int current_idx_middle1_pair_loc = base | index_step_outer | index_step_middle1;
2496 int current_idx_middle1_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_inner;
2497 int current_idx_middle2_pair_loc = base | index_step_outer | index_step_middle2;
2498 int current_idx_middle2_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_inner;
2499 int current_idx_middle12_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2;
2500 int current_idx_middle12_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_inner;
2502 if(involved_qbits[0] == 0){
2504 element_0000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_loc);
2505 element_0010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_loc);
2506 element_0100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_loc);
2507 element_0110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_loc);
2508 element_1000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_pair_loc);
2509 element_1010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc);
2510 element_1100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc);
2511 element_1110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc);
2514 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
2515 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner_loc;
2517 double* element_middle1 = (
double*)input.
get_data() + 2 * current_idx_middle1_loc;
2518 double* element_middle1_inner = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_loc;
2520 double* element_middle2 = (
double*)input.
get_data() + 2 * current_idx_middle2_loc;
2521 double* element_middle2_inner = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_loc;
2523 double* element_middle12 = (
double*)input.
get_data() + 2 * current_idx_middle12_loc;
2524 double* element_middle12_inner = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_loc;
2526 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
2527 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair_loc;
2529 double* element_middle1_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc;
2530 double* element_middle1_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_pair_loc;
2532 double* element_middle2_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc;
2533 double* element_middle2_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_pair_loc;
2535 double* element_middle12_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc;
2536 double* element_middle12_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_pair_loc;
2538 element_0000_vec_real =
get_AVX_vector(element_outer, element_inner);
2539 element_0010_vec_real =
get_AVX_vector(element_middle1, element_middle1_inner);
2540 element_0100_vec_real =
get_AVX_vector(element_middle2, element_middle2_inner);
2541 element_0110_vec_real =
get_AVX_vector(element_middle12, element_middle12_inner);
2542 element_1000_vec_real =
get_AVX_vector(element_outer_pair, element_inner_pair);
2543 element_1010_vec_real =
get_AVX_vector(element_middle1_pair, element_middle1_inner_pair);
2544 element_1100_vec_real =
get_AVX_vector(element_middle2_pair, element_middle2_inner_pair);
2545 element_1110_vec_real =
get_AVX_vector(element_middle12_pair, element_middle12_inner_pair);
2548 __m256d element_0000_vec_imag = _mm256_permute4x64_pd(element_0000_vec_real, 0b10110001);
2549 __m256d element_0010_vec_imag = _mm256_permute4x64_pd(element_0010_vec_real, 0b10110001);
2550 __m256d element_0100_vec_imag = _mm256_permute4x64_pd(element_0100_vec_real, 0b10110001);
2551 __m256d element_0110_vec_imag = _mm256_permute4x64_pd(element_0110_vec_real, 0b10110001);
2552 __m256d element_1000_vec_imag = _mm256_permute4x64_pd(element_1000_vec_real, 0b10110001);
2553 __m256d element_1010_vec_imag = _mm256_permute4x64_pd(element_1010_vec_real, 0b10110001);
2554 __m256d element_1100_vec_imag = _mm256_permute4x64_pd(element_1100_vec_real, 0b10110001);
2555 __m256d element_1110_vec_imag = _mm256_permute4x64_pd(element_1110_vec_real, 0b10110001);
2557 element_0000_vec_real = _mm256_mul_pd(element_0000_vec_real,neg);
2558 element_0010_vec_real = _mm256_mul_pd(element_0010_vec_real,neg);
2559 element_0100_vec_real = _mm256_mul_pd(element_0100_vec_real,neg);
2560 element_0110_vec_real = _mm256_mul_pd(element_0110_vec_real,neg);
2561 element_1000_vec_real = _mm256_mul_pd(element_1000_vec_real,neg);
2562 element_1010_vec_real = _mm256_mul_pd(element_1010_vec_real,neg);
2563 element_1100_vec_real = _mm256_mul_pd(element_1100_vec_real,neg);
2564 element_1110_vec_real = _mm256_mul_pd(element_1110_vec_real,neg);
2567 for (
int mult_idx = 0; mult_idx < 16; mult_idx++) {
2568 double* unitary_row_1 = (
double*)unitary.
get_data() + 32*mult_idx;
2569 double* unitary_row_2 = unitary_row_1 + 4;
2570 double* unitary_row_3 = unitary_row_1 + 8;
2571 double* unitary_row_4 = unitary_row_1 + 12;
2572 double* unitary_row_5 = unitary_row_1 + 16;
2573 double* unitary_row_6 = unitary_row_1 + 20;
2574 double* unitary_row_7 = unitary_row_1 + 24;
2575 double* unitary_row_8 = unitary_row_1 + 28;
2577 __m256d row1_vec = _mm256_loadu_pd(unitary_row_1);
2578 __m256d row2_vec = _mm256_loadu_pd(unitary_row_2);
2579 __m256d row3_vec = _mm256_loadu_pd(unitary_row_3);
2580 __m256d row4_vec = _mm256_loadu_pd(unitary_row_4);
2581 __m256d row5_vec = _mm256_loadu_pd(unitary_row_5);
2582 __m256d row6_vec = _mm256_loadu_pd(unitary_row_6);
2583 __m256d row7_vec = _mm256_loadu_pd(unitary_row_7);
2584 __m256d row8_vec = _mm256_loadu_pd(unitary_row_8);
2586 __m256d data_real = _mm256_setzero_pd();
2587 __m256d data_imag = _mm256_setzero_pd();
2589 data_real = _mm256_fmadd_pd(element_0000_vec_real, row1_vec, data_real);
2590 data_imag = _mm256_fmadd_pd(element_0000_vec_imag, row1_vec, data_imag);
2591 data_real = _mm256_fmadd_pd(element_0010_vec_real, row2_vec, data_real);
2592 data_imag = _mm256_fmadd_pd(element_0010_vec_imag, row2_vec, data_imag);
2593 data_real = _mm256_fmadd_pd(element_0100_vec_real, row3_vec, data_real);
2594 data_imag = _mm256_fmadd_pd(element_0100_vec_imag, row3_vec, data_imag);
2595 data_real = _mm256_fmadd_pd(element_0110_vec_real, row4_vec, data_real);
2596 data_imag = _mm256_fmadd_pd(element_0110_vec_imag, row4_vec, data_imag);
2597 data_real = _mm256_fmadd_pd(element_1000_vec_real, row5_vec, data_real);
2598 data_imag = _mm256_fmadd_pd(element_1000_vec_imag, row5_vec, data_imag);
2599 data_real = _mm256_fmadd_pd(element_1010_vec_real, row6_vec, data_real);
2600 data_imag = _mm256_fmadd_pd(element_1010_vec_imag, row6_vec, data_imag);
2601 data_real = _mm256_fmadd_pd(element_1100_vec_real, row7_vec, data_real);
2602 data_imag = _mm256_fmadd_pd(element_1100_vec_imag, row7_vec, data_imag);
2603 data_real = _mm256_fmadd_pd(element_1110_vec_real, row8_vec, data_real);
2604 data_imag = _mm256_fmadd_pd(element_1110_vec_imag, row8_vec, data_imag);
2606 __m256d final_vec = _mm256_hadd_pd(data_real, data_imag);
2607 final_vec = _mm256_permute4x64_pd(final_vec, 0b11011000);
2608 final_vec = _mm256_hadd_pd(final_vec, final_vec);
2609 __m128d low128 = _mm256_castpd256_pd128(final_vec);
2610 __m128d high128 = _mm256_extractf128_pd(final_vec, 1);
2611 results[mult_idx].
real = _mm_cvtsd_f64(low128);
2612 results[mult_idx].
imag = _mm_cvtsd_f64(high128);
2615 input[current_idx_outer_loc] = results[0];
2616 input[current_idx_inner_loc] = results[1];
2617 input[current_idx_middle1_loc] = results[2];
2618 input[current_idx_middle1_inner_loc] = results[3];
2619 input[current_idx_middle2_loc] = results[4];
2620 input[current_idx_middle2_inner_loc] = results[5];
2621 input[current_idx_middle12_loc] = results[6];
2622 input[current_idx_middle12_inner_loc] = results[7];
2623 input[current_idx_outer_pair_loc] = results[8];
2624 input[current_idx_inner_pair_loc] = results[9];
2625 input[current_idx_middle1_pair_loc] = results[10];
2626 input[current_idx_middle1_inner_pair_loc] = results[11];
2627 input[current_idx_middle2_pair_loc] = results[12];
2628 input[current_idx_middle2_inner_pair_loc] = results[13];
2629 input[current_idx_middle12_pair_loc] = results[14];
2630 input[current_idx_middle12_inner_pair_loc] = results[15];
2642 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
2644 int index_step_inner = 1 << involved_qbits[0];
2645 int index_step_middle1 = 1 << involved_qbits[1];
2646 int index_step_middle2 = 1 << involved_qbits[2];
2647 int index_step_outer = 1 << involved_qbits[3];
2650 int num_qubits = (
int)std::log2(matrix_size);
2653 std::vector<int> is_target(num_qubits, 0);
2654 for (
int q : involved_qbits) is_target[q] = 1;
2656 std::vector<int> non_targets;
2657 non_targets.reserve(num_qubits - 4);
2658 for (
int q = 0; q < num_qubits; ++q) {
2659 if (!is_target[q]) non_targets.push_back(q);
2663 tbb::blocked_range<int>(0, matrix_size >> 4),
2664 [&](
const tbb::blocked_range<int>& range) {
2665 for (
int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
2666 __m256d element_0000_vec_real = _mm256_setzero_pd();
2667 __m256d element_0010_vec_real = _mm256_setzero_pd();
2668 __m256d element_0100_vec_real = _mm256_setzero_pd();
2669 __m256d element_0110_vec_real = _mm256_setzero_pd();
2670 __m256d element_1000_vec_real = _mm256_setzero_pd();
2671 __m256d element_1010_vec_real = _mm256_setzero_pd();
2672 __m256d element_1100_vec_real = _mm256_setzero_pd();
2673 __m256d element_1110_vec_real = _mm256_setzero_pd();
2676 for (
size_t i = 0; i < non_targets.size(); ++i) {
2677 if (block_idx & (1ULL << i)) {
2678 base |= (1 << non_targets[i]);
2682 int current_idx_outer_loc = base;
2683 int current_idx_inner_loc = base | index_step_inner;
2684 int current_idx_middle1_loc = base | index_step_middle1;
2685 int current_idx_middle1_inner_loc = base | index_step_middle1 | index_step_inner;
2686 int current_idx_middle2_loc = base | index_step_middle2;
2687 int current_idx_middle2_inner_loc = base | index_step_middle2 | index_step_inner;
2688 int current_idx_middle12_loc = base | index_step_middle1 | index_step_middle2;
2689 int current_idx_middle12_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_inner;
2691 int current_idx_outer_pair_loc = base | index_step_outer;
2692 int current_idx_inner_pair_loc = base | index_step_outer | index_step_inner;
2693 int current_idx_middle1_pair_loc = base | index_step_outer | index_step_middle1;
2694 int current_idx_middle1_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_inner;
2695 int current_idx_middle2_pair_loc = base | index_step_outer | index_step_middle2;
2696 int current_idx_middle2_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_inner;
2697 int current_idx_middle12_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2;
2698 int current_idx_middle12_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_inner;
2700 if(involved_qbits[0] == 0){
2702 element_0000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_loc);
2703 element_0010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_loc);
2704 element_0100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_loc);
2705 element_0110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_loc);
2706 element_1000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_pair_loc);
2707 element_1010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc);
2708 element_1100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc);
2709 element_1110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc);
2712 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
2713 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner_loc;
2715 double* element_middle1 = (
double*)input.
get_data() + 2 * current_idx_middle1_loc;
2716 double* element_middle1_inner = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_loc;
2718 double* element_middle2 = (
double*)input.
get_data() + 2 * current_idx_middle2_loc;
2719 double* element_middle2_inner = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_loc;
2721 double* element_middle12 = (
double*)input.
get_data() + 2 * current_idx_middle12_loc;
2722 double* element_middle12_inner = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_loc;
2724 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
2725 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair_loc;
2727 double* element_middle1_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc;
2728 double* element_middle1_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_pair_loc;
2730 double* element_middle2_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc;
2731 double* element_middle2_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_pair_loc;
2733 double* element_middle12_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc;
2734 double* element_middle12_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_pair_loc;
2736 element_0000_vec_real =
get_AVX_vector(element_outer, element_inner);
2737 element_0010_vec_real =
get_AVX_vector(element_middle1, element_middle1_inner);
2738 element_0100_vec_real =
get_AVX_vector(element_middle2, element_middle2_inner);
2739 element_0110_vec_real =
get_AVX_vector(element_middle12, element_middle12_inner);
2740 element_1000_vec_real =
get_AVX_vector(element_outer_pair, element_inner_pair);
2741 element_1010_vec_real =
get_AVX_vector(element_middle1_pair, element_middle1_inner_pair);
2742 element_1100_vec_real =
get_AVX_vector(element_middle2_pair, element_middle2_inner_pair);
2743 element_1110_vec_real =
get_AVX_vector(element_middle12_pair, element_middle12_inner_pair);
2746 __m256d element_0000_vec_imag = _mm256_permute4x64_pd(element_0000_vec_real, 0b10110001);
2747 __m256d element_0010_vec_imag = _mm256_permute4x64_pd(element_0010_vec_real, 0b10110001);
2748 __m256d element_0100_vec_imag = _mm256_permute4x64_pd(element_0100_vec_real, 0b10110001);
2749 __m256d element_0110_vec_imag = _mm256_permute4x64_pd(element_0110_vec_real, 0b10110001);
2750 __m256d element_1000_vec_imag = _mm256_permute4x64_pd(element_1000_vec_real, 0b10110001);
2751 __m256d element_1010_vec_imag = _mm256_permute4x64_pd(element_1010_vec_real, 0b10110001);
2752 __m256d element_1100_vec_imag = _mm256_permute4x64_pd(element_1100_vec_real, 0b10110001);
2753 __m256d element_1110_vec_imag = _mm256_permute4x64_pd(element_1110_vec_real, 0b10110001);
2755 element_0000_vec_real = _mm256_mul_pd(element_0000_vec_real,neg);
2756 element_0010_vec_real = _mm256_mul_pd(element_0010_vec_real,neg);
2757 element_0100_vec_real = _mm256_mul_pd(element_0100_vec_real,neg);
2758 element_0110_vec_real = _mm256_mul_pd(element_0110_vec_real,neg);
2759 element_1000_vec_real = _mm256_mul_pd(element_1000_vec_real,neg);
2760 element_1010_vec_real = _mm256_mul_pd(element_1010_vec_real,neg);
2761 element_1100_vec_real = _mm256_mul_pd(element_1100_vec_real,neg);
2762 element_1110_vec_real = _mm256_mul_pd(element_1110_vec_real,neg);
2765 for (
int mult_idx = 0; mult_idx < 16; mult_idx++) {
2766 double* unitary_row_1 = (
double*)unitary.
get_data() + 32*mult_idx;
2767 double* unitary_row_2 = unitary_row_1 + 4;
2768 double* unitary_row_3 = unitary_row_1 + 8;
2769 double* unitary_row_4 = unitary_row_1 + 12;
2770 double* unitary_row_5 = unitary_row_1 + 16;
2771 double* unitary_row_6 = unitary_row_1 + 20;
2772 double* unitary_row_7 = unitary_row_1 + 24;
2773 double* unitary_row_8 = unitary_row_1 + 28;
2775 __m256d row1_vec = _mm256_loadu_pd(unitary_row_1);
2776 __m256d row2_vec = _mm256_loadu_pd(unitary_row_2);
2777 __m256d row3_vec = _mm256_loadu_pd(unitary_row_3);
2778 __m256d row4_vec = _mm256_loadu_pd(unitary_row_4);
2779 __m256d row5_vec = _mm256_loadu_pd(unitary_row_5);
2780 __m256d row6_vec = _mm256_loadu_pd(unitary_row_6);
2781 __m256d row7_vec = _mm256_loadu_pd(unitary_row_7);
2782 __m256d row8_vec = _mm256_loadu_pd(unitary_row_8);
2784 __m256d data_real = _mm256_setzero_pd();
2785 __m256d data_imag = _mm256_setzero_pd();
2787 data_real = _mm256_fmadd_pd(element_0000_vec_real, row1_vec, data_real);
2788 data_imag = _mm256_fmadd_pd(element_0000_vec_imag, row1_vec, data_imag);
2789 data_real = _mm256_fmadd_pd(element_0010_vec_real, row2_vec, data_real);
2790 data_imag = _mm256_fmadd_pd(element_0010_vec_imag, row2_vec, data_imag);
2791 data_real = _mm256_fmadd_pd(element_0100_vec_real, row3_vec, data_real);
2792 data_imag = _mm256_fmadd_pd(element_0100_vec_imag, row3_vec, data_imag);
2793 data_real = _mm256_fmadd_pd(element_0110_vec_real, row4_vec, data_real);
2794 data_imag = _mm256_fmadd_pd(element_0110_vec_imag, row4_vec, data_imag);
2795 data_real = _mm256_fmadd_pd(element_1000_vec_real, row5_vec, data_real);
2796 data_imag = _mm256_fmadd_pd(element_1000_vec_imag, row5_vec, data_imag);
2797 data_real = _mm256_fmadd_pd(element_1010_vec_real, row6_vec, data_real);
2798 data_imag = _mm256_fmadd_pd(element_1010_vec_imag, row6_vec, data_imag);
2799 data_real = _mm256_fmadd_pd(element_1100_vec_real, row7_vec, data_real);
2800 data_imag = _mm256_fmadd_pd(element_1100_vec_imag, row7_vec, data_imag);
2801 data_real = _mm256_fmadd_pd(element_1110_vec_real, row8_vec, data_real);
2802 data_imag = _mm256_fmadd_pd(element_1110_vec_imag, row8_vec, data_imag);
2804 __m256d final_vec = _mm256_hadd_pd(data_real, data_imag);
2805 final_vec = _mm256_permute4x64_pd(final_vec, 0b11011000);
2806 final_vec = _mm256_hadd_pd(final_vec, final_vec);
2807 __m128d low128 = _mm256_castpd256_pd128(final_vec);
2808 __m128d high128 = _mm256_extractf128_pd(final_vec, 1);
2809 results[mult_idx].
real = _mm_cvtsd_f64(low128);
2810 results[mult_idx].
imag = _mm_cvtsd_f64(high128);
2813 input[current_idx_outer_loc] = results[0];
2814 input[current_idx_inner_loc] = results[1];
2815 input[current_idx_middle1_loc] = results[2];
2816 input[current_idx_middle1_inner_loc] = results[3];
2817 input[current_idx_middle2_loc] = results[4];
2818 input[current_idx_middle2_inner_loc] = results[5];
2819 input[current_idx_middle12_loc] = results[6];
2820 input[current_idx_middle12_inner_loc] = results[7];
2821 input[current_idx_outer_pair_loc] = results[8];
2822 input[current_idx_inner_pair_loc] = results[9];
2823 input[current_idx_middle1_pair_loc] = results[10];
2824 input[current_idx_middle1_inner_pair_loc] = results[11];
2825 input[current_idx_middle2_pair_loc] = results[12];
2826 input[current_idx_middle2_inner_pair_loc] = results[13];
2827 input[current_idx_middle12_pair_loc] = results[14];
2828 input[current_idx_middle12_inner_pair_loc] = results[15];
2843 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
2845 int index_step_inner = 1 << involved_qbits[0];
2846 int index_step_middle1 = 1 << involved_qbits[1];
2847 int index_step_middle2 = 1 << involved_qbits[2];
2848 int index_step_middle3 = 1 << involved_qbits[3];
2849 int index_step_outer = 1 << involved_qbits[4];
2851 int num_qubits = (
int)std::log2(matrix_size);
2852 int num_blocks = matrix_size >> 5;
2853 std::vector<int> is_target(num_qubits, 0);
2854 for (
int q : involved_qbits) is_target[q] = 1;
2856 std::vector<int> non_targets;
2857 non_targets.reserve(num_qubits - 5);
2858 for (
int q = 0; q < num_qubits; ++q) {
2859 if (!is_target[q]) non_targets.push_back(q);
2861 #pragma omp parallel for schedule(static) 2862 for (
int block_idx = 0; block_idx < num_blocks; block_idx++) {
2865 for (
size_t i = 0; i < non_targets.size(); ++i) {
2866 if (block_idx & (1ULL << i)) {
2867 base |= (1 << non_targets[i]);
2871 int current_idx_outer_loc = base;
2872 int current_idx_inner_loc = base | index_step_inner;
2874 int current_idx_middle1_loc = base | index_step_middle1;
2875 int current_idx_middle1_inner_loc = base | index_step_middle1 | index_step_inner;
2877 int current_idx_middle2_loc = base | index_step_middle2;
2878 int current_idx_middle2_inner_loc = base | index_step_middle2 | index_step_inner;
2880 int current_idx_middle12_loc = base | index_step_middle1 | index_step_middle2;
2881 int current_idx_middle12_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_inner;
2883 int current_idx_middle3_loc = base | index_step_middle3;
2884 int current_idx_middle3_inner_loc = base | index_step_middle3 | index_step_inner;
2886 int current_idx_middle13_loc = base | index_step_middle1 | index_step_middle3;
2887 int current_idx_middle13_inner_loc = base | index_step_middle1 | index_step_middle3 | index_step_inner;
2889 int current_idx_middle23_loc = base | index_step_middle2 | index_step_middle3;
2890 int current_idx_middle23_inner_loc = base | index_step_middle2 | index_step_middle3 | index_step_inner;
2892 int current_idx_middle123_loc = base | index_step_middle1 | index_step_middle2 | index_step_middle3;
2893 int current_idx_middle123_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_middle3 | index_step_inner;
2895 int current_idx_outer_pair_loc = base | index_step_outer;
2896 int current_idx_inner_pair_loc = base | index_step_outer | index_step_inner;
2898 int current_idx_middle1_pair_loc = base | index_step_outer | index_step_middle1;
2899 int current_idx_middle1_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_inner;
2901 int current_idx_middle2_pair_loc = base | index_step_outer | index_step_middle2;
2902 int current_idx_middle2_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_inner;
2904 int current_idx_middle12_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2;
2905 int current_idx_middle12_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_inner;
2907 int current_idx_middle3_pair_loc = base | index_step_outer | index_step_middle3;
2908 int current_idx_middle3_inner_pair_loc = base | index_step_outer | index_step_middle3 | index_step_inner;
2910 int current_idx_middle13_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle3;
2911 int current_idx_middle13_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle3 | index_step_inner;
2913 int current_idx_middle23_pair_loc = base | index_step_outer | index_step_middle2 | index_step_middle3;
2914 int current_idx_middle23_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_middle3 | index_step_inner;
2916 int current_idx_middle123_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_middle3;
2917 int current_idx_middle123_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_middle3 | index_step_inner;
2919 __m256d element_00000_vec_real = _mm256_setzero_pd();
2920 __m256d element_00010_vec_real = _mm256_setzero_pd();
2921 __m256d element_00100_vec_real = _mm256_setzero_pd();
2922 __m256d element_00110_vec_real = _mm256_setzero_pd();
2923 __m256d element_01000_vec_real = _mm256_setzero_pd();
2924 __m256d element_01010_vec_real = _mm256_setzero_pd();
2925 __m256d element_01100_vec_real = _mm256_setzero_pd();
2926 __m256d element_01110_vec_real = _mm256_setzero_pd();
2927 __m256d element_10000_vec_real = _mm256_setzero_pd();
2928 __m256d element_10010_vec_real = _mm256_setzero_pd();
2929 __m256d element_10100_vec_real = _mm256_setzero_pd();
2930 __m256d element_10110_vec_real = _mm256_setzero_pd();
2931 __m256d element_11000_vec_real = _mm256_setzero_pd();
2932 __m256d element_11010_vec_real = _mm256_setzero_pd();
2933 __m256d element_11100_vec_real = _mm256_setzero_pd();
2934 __m256d element_11110_vec_real = _mm256_setzero_pd();
2936 if (involved_qbits[0] == 0) {
2938 element_00000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_loc);
2939 element_00010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_loc);
2940 element_00100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_loc);
2941 element_00110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_loc);
2942 element_01000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle3_loc);
2943 element_01010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle13_loc);
2944 element_01100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle23_loc);
2945 element_01110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle123_loc);
2946 element_10000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_pair_loc);
2947 element_10010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc);
2948 element_10100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc);
2949 element_10110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc);
2950 element_11000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle3_pair_loc);
2951 element_11010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle13_pair_loc);
2952 element_11100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle23_pair_loc);
2953 element_11110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle123_pair_loc);
2957 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
2958 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner_loc;
2960 double* element_middle1 = (
double*)input.
get_data() + 2 * current_idx_middle1_loc;
2961 double* element_middle1_inner = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_loc;
2963 double* element_middle2 = (
double*)input.
get_data() + 2 * current_idx_middle2_loc;
2964 double* element_middle2_inner = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_loc;
2966 double* element_middle12 = (
double*)input.
get_data() + 2 * current_idx_middle12_loc;
2967 double* element_middle12_inner = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_loc;
2969 double* element_middle3 = (
double*)input.
get_data() + 2 * current_idx_middle3_loc;
2970 double* element_middle3_inner = (
double*)input.
get_data() + 2 * current_idx_middle3_inner_loc;
2972 double* element_middle13 = (
double*)input.
get_data() + 2 * current_idx_middle13_loc;
2973 double* element_middle13_inner = (
double*)input.
get_data() + 2 * current_idx_middle13_inner_loc;
2975 double* element_middle23 = (
double*)input.
get_data() + 2 * current_idx_middle23_loc;
2976 double* element_middle23_inner = (
double*)input.
get_data() + 2 * current_idx_middle23_inner_loc;
2978 double* element_middle123 = (
double*)input.
get_data() + 2 * current_idx_middle123_loc;
2979 double* element_middle123_inner = (
double*)input.
get_data() + 2 * current_idx_middle123_inner_loc;
2981 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
2982 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair_loc;
2984 double* element_middle1_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc;
2985 double* element_middle1_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_pair_loc;
2987 double* element_middle2_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc;
2988 double* element_middle2_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_pair_loc;
2990 double* element_middle12_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc;
2991 double* element_middle12_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_pair_loc;
2993 double* element_middle3_pair = (
double*)input.
get_data() + 2 * current_idx_middle3_pair_loc;
2994 double* element_middle3_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle3_inner_pair_loc;
2996 double* element_middle13_pair = (
double*)input.
get_data() + 2 * current_idx_middle13_pair_loc;
2997 double* element_middle13_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle13_inner_pair_loc;
2999 double* element_middle23_pair = (
double*)input.
get_data() + 2 * current_idx_middle23_pair_loc;
3000 double* element_middle23_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle23_inner_pair_loc;
3002 double* element_middle123_pair = (
double*)input.
get_data() + 2 * current_idx_middle123_pair_loc;
3003 double* element_middle123_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle123_inner_pair_loc;
3005 element_00000_vec_real =
get_AVX_vector(element_outer, element_inner);
3006 element_00010_vec_real =
get_AVX_vector(element_middle1, element_middle1_inner);
3007 element_00100_vec_real =
get_AVX_vector(element_middle2, element_middle2_inner);
3008 element_00110_vec_real =
get_AVX_vector(element_middle12, element_middle12_inner);
3009 element_01000_vec_real =
get_AVX_vector(element_middle3, element_middle3_inner);
3010 element_01010_vec_real =
get_AVX_vector(element_middle13, element_middle13_inner);
3011 element_01100_vec_real =
get_AVX_vector(element_middle23, element_middle23_inner);
3012 element_01110_vec_real =
get_AVX_vector(element_middle123, element_middle123_inner);
3013 element_10000_vec_real =
get_AVX_vector(element_outer_pair, element_inner_pair);
3014 element_10010_vec_real =
get_AVX_vector(element_middle1_pair, element_middle1_inner_pair);
3015 element_10100_vec_real =
get_AVX_vector(element_middle2_pair, element_middle2_inner_pair);
3016 element_10110_vec_real =
get_AVX_vector(element_middle12_pair, element_middle12_inner_pair);
3017 element_11000_vec_real =
get_AVX_vector(element_middle3_pair, element_middle3_inner_pair);
3018 element_11010_vec_real =
get_AVX_vector(element_middle13_pair, element_middle13_inner_pair);
3019 element_11100_vec_real =
get_AVX_vector(element_middle23_pair, element_middle23_inner_pair);
3020 element_11110_vec_real =
get_AVX_vector(element_middle123_pair, element_middle123_inner_pair);
3023 __m256d element_00000_vec_imag = _mm256_permute4x64_pd(element_00000_vec_real, 0b10110001);
3024 __m256d element_00010_vec_imag = _mm256_permute4x64_pd(element_00010_vec_real, 0b10110001);
3025 __m256d element_00100_vec_imag = _mm256_permute4x64_pd(element_00100_vec_real, 0b10110001);
3026 __m256d element_00110_vec_imag = _mm256_permute4x64_pd(element_00110_vec_real, 0b10110001);
3027 __m256d element_01000_vec_imag = _mm256_permute4x64_pd(element_01000_vec_real, 0b10110001);
3028 __m256d element_01010_vec_imag = _mm256_permute4x64_pd(element_01010_vec_real, 0b10110001);
3029 __m256d element_01100_vec_imag = _mm256_permute4x64_pd(element_01100_vec_real, 0b10110001);
3030 __m256d element_01110_vec_imag = _mm256_permute4x64_pd(element_01110_vec_real, 0b10110001);
3031 __m256d element_10000_vec_imag = _mm256_permute4x64_pd(element_10000_vec_real, 0b10110001);
3032 __m256d element_10010_vec_imag = _mm256_permute4x64_pd(element_10010_vec_real, 0b10110001);
3033 __m256d element_10100_vec_imag = _mm256_permute4x64_pd(element_10100_vec_real, 0b10110001);
3034 __m256d element_10110_vec_imag = _mm256_permute4x64_pd(element_10110_vec_real, 0b10110001);
3035 __m256d element_11000_vec_imag = _mm256_permute4x64_pd(element_11000_vec_real, 0b10110001);
3036 __m256d element_11010_vec_imag = _mm256_permute4x64_pd(element_11010_vec_real, 0b10110001);
3037 __m256d element_11100_vec_imag = _mm256_permute4x64_pd(element_11100_vec_real, 0b10110001);
3038 __m256d element_11110_vec_imag = _mm256_permute4x64_pd(element_11110_vec_real, 0b10110001);
3040 element_00000_vec_real = _mm256_mul_pd(element_00000_vec_real,neg);
3041 element_00010_vec_real = _mm256_mul_pd(element_00010_vec_real,neg);
3042 element_00100_vec_real = _mm256_mul_pd(element_00100_vec_real,neg);
3043 element_00110_vec_real = _mm256_mul_pd(element_00110_vec_real,neg);
3044 element_01000_vec_real = _mm256_mul_pd(element_01000_vec_real,neg);
3045 element_01010_vec_real = _mm256_mul_pd(element_01010_vec_real,neg);
3046 element_01100_vec_real = _mm256_mul_pd(element_01100_vec_real,neg);
3047 element_01110_vec_real = _mm256_mul_pd(element_01110_vec_real,neg);
3048 element_10000_vec_real = _mm256_mul_pd(element_10000_vec_real,neg);
3049 element_10010_vec_real = _mm256_mul_pd(element_10010_vec_real,neg);
3050 element_10100_vec_real = _mm256_mul_pd(element_10100_vec_real,neg);
3051 element_10110_vec_real = _mm256_mul_pd(element_10110_vec_real,neg);
3052 element_11000_vec_real = _mm256_mul_pd(element_11000_vec_real,neg);
3053 element_11010_vec_real = _mm256_mul_pd(element_11010_vec_real,neg);
3054 element_11100_vec_real = _mm256_mul_pd(element_11100_vec_real,neg);
3055 element_11110_vec_real = _mm256_mul_pd(element_11110_vec_real,neg);
3058 for (
int mult_idx = 0; mult_idx < 32; mult_idx++) {
3059 double* unitary_row_1 = (
double*)unitary.
get_data() + 64*mult_idx;
3060 double* unitary_row_2 = unitary_row_1 + 4;
3061 double* unitary_row_3 = unitary_row_1 + 8;
3062 double* unitary_row_4 = unitary_row_1 + 12;
3063 double* unitary_row_5 = unitary_row_1 + 16;
3064 double* unitary_row_6 = unitary_row_1 + 20;
3065 double* unitary_row_7 = unitary_row_1 + 24;
3066 double* unitary_row_8 = unitary_row_1 + 28;
3067 double* unitary_row_9 = unitary_row_1 + 32;
3068 double* unitary_row_10 = unitary_row_1 + 36;
3069 double* unitary_row_11 = unitary_row_1 + 40;
3070 double* unitary_row_12 = unitary_row_1 + 44;
3071 double* unitary_row_13 = unitary_row_1 + 48;
3072 double* unitary_row_14 = unitary_row_1 + 52;
3073 double* unitary_row_15 = unitary_row_1 + 56;
3074 double* unitary_row_16 = unitary_row_1 + 60;
3076 __m256d row1_vec = _mm256_loadu_pd(unitary_row_1);
3077 __m256d row2_vec = _mm256_loadu_pd(unitary_row_2);
3078 __m256d row3_vec = _mm256_loadu_pd(unitary_row_3);
3079 __m256d row4_vec = _mm256_loadu_pd(unitary_row_4);
3080 __m256d row5_vec = _mm256_loadu_pd(unitary_row_5);
3081 __m256d row6_vec = _mm256_loadu_pd(unitary_row_6);
3082 __m256d row7_vec = _mm256_loadu_pd(unitary_row_7);
3083 __m256d row8_vec = _mm256_loadu_pd(unitary_row_8);
3084 __m256d row9_vec = _mm256_loadu_pd(unitary_row_9);
3085 __m256d row10_vec = _mm256_loadu_pd(unitary_row_10);
3086 __m256d row11_vec = _mm256_loadu_pd(unitary_row_11);
3087 __m256d row12_vec = _mm256_loadu_pd(unitary_row_12);
3088 __m256d row13_vec = _mm256_loadu_pd(unitary_row_13);
3089 __m256d row14_vec = _mm256_loadu_pd(unitary_row_14);
3090 __m256d row15_vec = _mm256_loadu_pd(unitary_row_15);
3091 __m256d row16_vec = _mm256_loadu_pd(unitary_row_16);
3093 __m256d data_real = _mm256_setzero_pd();
3094 __m256d data_imag = _mm256_setzero_pd();
3096 data_real = _mm256_fmadd_pd(element_00000_vec_real, row1_vec, data_real);
3097 data_imag = _mm256_fmadd_pd(element_00000_vec_imag, row1_vec, data_imag);
3098 data_real = _mm256_fmadd_pd(element_00010_vec_real, row2_vec, data_real);
3099 data_imag = _mm256_fmadd_pd(element_00010_vec_imag, row2_vec, data_imag);
3100 data_real = _mm256_fmadd_pd(element_00100_vec_real, row3_vec, data_real);
3101 data_imag = _mm256_fmadd_pd(element_00100_vec_imag, row3_vec, data_imag);
3102 data_real = _mm256_fmadd_pd(element_00110_vec_real, row4_vec, data_real);
3103 data_imag = _mm256_fmadd_pd(element_00110_vec_imag, row4_vec, data_imag);
3104 data_real = _mm256_fmadd_pd(element_01000_vec_real, row5_vec, data_real);
3105 data_imag = _mm256_fmadd_pd(element_01000_vec_imag, row5_vec, data_imag);
3106 data_real = _mm256_fmadd_pd(element_01010_vec_real, row6_vec, data_real);
3107 data_imag = _mm256_fmadd_pd(element_01010_vec_imag, row6_vec, data_imag);
3108 data_real = _mm256_fmadd_pd(element_01100_vec_real, row7_vec, data_real);
3109 data_imag = _mm256_fmadd_pd(element_01100_vec_imag, row7_vec, data_imag);
3110 data_real = _mm256_fmadd_pd(element_01110_vec_real, row8_vec, data_real);
3111 data_imag = _mm256_fmadd_pd(element_01110_vec_imag, row8_vec, data_imag);
3112 data_real = _mm256_fmadd_pd(element_10000_vec_real, row9_vec, data_real);
3113 data_imag = _mm256_fmadd_pd(element_10000_vec_imag, row9_vec, data_imag);
3114 data_real = _mm256_fmadd_pd(element_10010_vec_real, row10_vec, data_real);
3115 data_imag = _mm256_fmadd_pd(element_10010_vec_imag, row10_vec, data_imag);
3116 data_real = _mm256_fmadd_pd(element_10100_vec_real, row11_vec, data_real);
3117 data_imag = _mm256_fmadd_pd(element_10100_vec_imag, row11_vec, data_imag);
3118 data_real = _mm256_fmadd_pd(element_10110_vec_real, row12_vec, data_real);
3119 data_imag = _mm256_fmadd_pd(element_10110_vec_imag, row12_vec, data_imag);
3120 data_real = _mm256_fmadd_pd(element_11000_vec_real, row13_vec, data_real);
3121 data_imag = _mm256_fmadd_pd(element_11000_vec_imag, row13_vec, data_imag);
3122 data_real = _mm256_fmadd_pd(element_11010_vec_real, row14_vec, data_real);
3123 data_imag = _mm256_fmadd_pd(element_11010_vec_imag, row14_vec, data_imag);
3124 data_real = _mm256_fmadd_pd(element_11100_vec_real, row15_vec, data_real);
3125 data_imag = _mm256_fmadd_pd(element_11100_vec_imag, row15_vec, data_imag);
3126 data_real = _mm256_fmadd_pd(element_11110_vec_real, row16_vec, data_real);
3127 data_imag = _mm256_fmadd_pd(element_11110_vec_imag, row16_vec, data_imag);
3129 __m256d final_vec = _mm256_hadd_pd(data_real, data_imag);
3130 final_vec = _mm256_permute4x64_pd(final_vec, 0b11011000);
3131 final_vec = _mm256_hadd_pd(final_vec, final_vec);
3132 __m128d low128 = _mm256_castpd256_pd128(final_vec);
3133 __m128d high128 = _mm256_extractf128_pd(final_vec, 1);
3134 results[mult_idx].
real = _mm_cvtsd_f64(low128);
3135 results[mult_idx].
imag = _mm_cvtsd_f64(high128);
3137 input[current_idx_outer_loc] = results[0];
3138 input[current_idx_inner_loc] = results[1];
3139 input[current_idx_middle1_loc] = results[2];
3140 input[current_idx_middle1_inner_loc] = results[3];
3141 input[current_idx_middle2_loc] = results[4];
3142 input[current_idx_middle2_inner_loc] = results[5];
3143 input[current_idx_middle12_loc] = results[6];
3144 input[current_idx_middle12_inner_loc] = results[7];
3145 input[current_idx_middle3_loc] = results[8];
3146 input[current_idx_middle3_inner_loc] = results[9];
3147 input[current_idx_middle13_loc] = results[10];
3148 input[current_idx_middle13_inner_loc] = results[11];
3149 input[current_idx_middle23_loc] = results[12];
3150 input[current_idx_middle23_inner_loc] = results[13];
3151 input[current_idx_middle123_loc] = results[14];
3152 input[current_idx_middle123_inner_loc] = results[15];
3153 input[current_idx_outer_pair_loc] = results[16];
3154 input[current_idx_inner_pair_loc] = results[17];
3155 input[current_idx_middle1_pair_loc] = results[18];
3156 input[current_idx_middle1_inner_pair_loc] = results[19];
3157 input[current_idx_middle2_pair_loc] = results[20];
3158 input[current_idx_middle2_inner_pair_loc] = results[21];
3159 input[current_idx_middle12_pair_loc] = results[22];
3160 input[current_idx_middle12_inner_pair_loc] = results[23];
3161 input[current_idx_middle3_pair_loc] = results[24];
3162 input[current_idx_middle3_inner_pair_loc] = results[25];
3163 input[current_idx_middle13_pair_loc] = results[26];
3164 input[current_idx_middle13_inner_pair_loc] = results[27];
3165 input[current_idx_middle23_pair_loc] = results[28];
3166 input[current_idx_middle23_inner_pair_loc] = results[29];
3167 input[current_idx_middle123_pair_loc] = results[30];
3168 input[current_idx_middle123_inner_pair_loc] = results[31];
3180 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
3182 int index_step_inner = 1 << involved_qbits[0];
3183 int index_step_middle1 = 1 << involved_qbits[1];
3184 int index_step_middle2 = 1 << involved_qbits[2];
3185 int index_step_middle3 = 1 << involved_qbits[3];
3186 int index_step_outer = 1 << involved_qbits[4];
3188 int num_qubits = (
int)std::log2(matrix_size);
3189 std::vector<int> is_target(num_qubits, 0);
3190 for (
int q : involved_qbits) is_target[q] = 1;
3192 std::vector<int> non_targets;
3193 non_targets.reserve(num_qubits - 5);
3194 for (
int q = 0; q < num_qubits; ++q) {
3195 if (!is_target[q]) non_targets.push_back(q);
3198 tbb::blocked_range<int>(0, matrix_size >> 5),
3199 [&](
const tbb::blocked_range<int>& range) {
3200 for (
int block_idx = range.begin(); block_idx != range.end(); ++block_idx) {
3203 for (
size_t i = 0; i < non_targets.size(); ++i) {
3204 if (block_idx & (1ULL << i)) {
3205 base |= (1 << non_targets[i]);
3209 int current_idx_outer_loc = base;
3210 int current_idx_inner_loc = base | index_step_inner;
3212 int current_idx_middle1_loc = base | index_step_middle1;
3213 int current_idx_middle1_inner_loc = base | index_step_middle1 | index_step_inner;
3215 int current_idx_middle2_loc = base | index_step_middle2;
3216 int current_idx_middle2_inner_loc = base | index_step_middle2 | index_step_inner;
3218 int current_idx_middle12_loc = base | index_step_middle1 | index_step_middle2;
3219 int current_idx_middle12_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_inner;
3221 int current_idx_middle3_loc = base | index_step_middle3;
3222 int current_idx_middle3_inner_loc = base | index_step_middle3 | index_step_inner;
3224 int current_idx_middle13_loc = base | index_step_middle1 | index_step_middle3;
3225 int current_idx_middle13_inner_loc = base | index_step_middle1 | index_step_middle3 | index_step_inner;
3227 int current_idx_middle23_loc = base | index_step_middle2 | index_step_middle3;
3228 int current_idx_middle23_inner_loc = base | index_step_middle2 | index_step_middle3 | index_step_inner;
3230 int current_idx_middle123_loc = base | index_step_middle1 | index_step_middle2 | index_step_middle3;
3231 int current_idx_middle123_inner_loc = base | index_step_middle1 | index_step_middle2 | index_step_middle3 | index_step_inner;
3233 int current_idx_outer_pair_loc = base | index_step_outer;
3234 int current_idx_inner_pair_loc = base | index_step_outer | index_step_inner;
3236 int current_idx_middle1_pair_loc = base | index_step_outer | index_step_middle1;
3237 int current_idx_middle1_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_inner;
3239 int current_idx_middle2_pair_loc = base | index_step_outer | index_step_middle2;
3240 int current_idx_middle2_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_inner;
3242 int current_idx_middle12_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2;
3243 int current_idx_middle12_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_inner;
3245 int current_idx_middle3_pair_loc = base | index_step_outer | index_step_middle3;
3246 int current_idx_middle3_inner_pair_loc = base | index_step_outer | index_step_middle3 | index_step_inner;
3248 int current_idx_middle13_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle3;
3249 int current_idx_middle13_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle3 | index_step_inner;
3251 int current_idx_middle23_pair_loc = base | index_step_outer | index_step_middle2 | index_step_middle3;
3252 int current_idx_middle23_inner_pair_loc = base | index_step_outer | index_step_middle2 | index_step_middle3 | index_step_inner;
3254 int current_idx_middle123_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_middle3;
3255 int current_idx_middle123_inner_pair_loc = base | index_step_outer | index_step_middle1 | index_step_middle2 | index_step_middle3 | index_step_inner;
3257 __m256d element_00000_vec_real = _mm256_setzero_pd();
3258 __m256d element_00010_vec_real = _mm256_setzero_pd();
3259 __m256d element_00100_vec_real = _mm256_setzero_pd();
3260 __m256d element_00110_vec_real = _mm256_setzero_pd();
3261 __m256d element_01000_vec_real = _mm256_setzero_pd();
3262 __m256d element_01010_vec_real = _mm256_setzero_pd();
3263 __m256d element_01100_vec_real = _mm256_setzero_pd();
3264 __m256d element_01110_vec_real = _mm256_setzero_pd();
3265 __m256d element_10000_vec_real = _mm256_setzero_pd();
3266 __m256d element_10010_vec_real = _mm256_setzero_pd();
3267 __m256d element_10100_vec_real = _mm256_setzero_pd();
3268 __m256d element_10110_vec_real = _mm256_setzero_pd();
3269 __m256d element_11000_vec_real = _mm256_setzero_pd();
3270 __m256d element_11010_vec_real = _mm256_setzero_pd();
3271 __m256d element_11100_vec_real = _mm256_setzero_pd();
3272 __m256d element_11110_vec_real = _mm256_setzero_pd();
3274 if (involved_qbits[0] == 0) {
3276 element_00000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_loc);
3277 element_00010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_loc);
3278 element_00100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_loc);
3279 element_00110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_loc);
3280 element_01000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle3_loc);
3281 element_01010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle13_loc);
3282 element_01100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle23_loc);
3283 element_01110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle123_loc);
3284 element_10000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_outer_pair_loc);
3285 element_10010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc);
3286 element_10100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc);
3287 element_10110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc);
3288 element_11000_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle3_pair_loc);
3289 element_11010_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle13_pair_loc);
3290 element_11100_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle23_pair_loc);
3291 element_11110_vec_real = _mm256_loadu_pd((
double*)input.
get_data() + 2 * current_idx_middle123_pair_loc);
3295 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer_loc;
3296 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner_loc;
3298 double* element_middle1 = (
double*)input.
get_data() + 2 * current_idx_middle1_loc;
3299 double* element_middle1_inner = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_loc;
3301 double* element_middle2 = (
double*)input.
get_data() + 2 * current_idx_middle2_loc;
3302 double* element_middle2_inner = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_loc;
3304 double* element_middle12 = (
double*)input.
get_data() + 2 * current_idx_middle12_loc;
3305 double* element_middle12_inner = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_loc;
3307 double* element_middle3 = (
double*)input.
get_data() + 2 * current_idx_middle3_loc;
3308 double* element_middle3_inner = (
double*)input.
get_data() + 2 * current_idx_middle3_inner_loc;
3310 double* element_middle13 = (
double*)input.
get_data() + 2 * current_idx_middle13_loc;
3311 double* element_middle13_inner = (
double*)input.
get_data() + 2 * current_idx_middle13_inner_loc;
3313 double* element_middle23 = (
double*)input.
get_data() + 2 * current_idx_middle23_loc;
3314 double* element_middle23_inner = (
double*)input.
get_data() + 2 * current_idx_middle23_inner_loc;
3316 double* element_middle123 = (
double*)input.
get_data() + 2 * current_idx_middle123_loc;
3317 double* element_middle123_inner = (
double*)input.
get_data() + 2 * current_idx_middle123_inner_loc;
3319 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair_loc;
3320 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair_loc;
3322 double* element_middle1_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_pair_loc;
3323 double* element_middle1_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle1_inner_pair_loc;
3325 double* element_middle2_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_pair_loc;
3326 double* element_middle2_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle2_inner_pair_loc;
3328 double* element_middle12_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_pair_loc;
3329 double* element_middle12_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle12_inner_pair_loc;
3331 double* element_middle3_pair = (
double*)input.
get_data() + 2 * current_idx_middle3_pair_loc;
3332 double* element_middle3_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle3_inner_pair_loc;
3334 double* element_middle13_pair = (
double*)input.
get_data() + 2 * current_idx_middle13_pair_loc;
3335 double* element_middle13_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle13_inner_pair_loc;
3337 double* element_middle23_pair = (
double*)input.
get_data() + 2 * current_idx_middle23_pair_loc;
3338 double* element_middle23_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle23_inner_pair_loc;
3340 double* element_middle123_pair = (
double*)input.
get_data() + 2 * current_idx_middle123_pair_loc;
3341 double* element_middle123_inner_pair = (
double*)input.
get_data() + 2 * current_idx_middle123_inner_pair_loc;
3343 element_00000_vec_real =
get_AVX_vector(element_outer, element_inner);
3344 element_00010_vec_real =
get_AVX_vector(element_middle1, element_middle1_inner);
3345 element_00100_vec_real =
get_AVX_vector(element_middle2, element_middle2_inner);
3346 element_00110_vec_real =
get_AVX_vector(element_middle12, element_middle12_inner);
3347 element_01000_vec_real =
get_AVX_vector(element_middle3, element_middle3_inner);
3348 element_01010_vec_real =
get_AVX_vector(element_middle13, element_middle13_inner);
3349 element_01100_vec_real =
get_AVX_vector(element_middle23, element_middle23_inner);
3350 element_01110_vec_real =
get_AVX_vector(element_middle123, element_middle123_inner);
3351 element_10000_vec_real =
get_AVX_vector(element_outer_pair, element_inner_pair);
3352 element_10010_vec_real =
get_AVX_vector(element_middle1_pair, element_middle1_inner_pair);
3353 element_10100_vec_real =
get_AVX_vector(element_middle2_pair, element_middle2_inner_pair);
3354 element_10110_vec_real =
get_AVX_vector(element_middle12_pair, element_middle12_inner_pair);
3355 element_11000_vec_real =
get_AVX_vector(element_middle3_pair, element_middle3_inner_pair);
3356 element_11010_vec_real =
get_AVX_vector(element_middle13_pair, element_middle13_inner_pair);
3357 element_11100_vec_real =
get_AVX_vector(element_middle23_pair, element_middle23_inner_pair);
3358 element_11110_vec_real =
get_AVX_vector(element_middle123_pair, element_middle123_inner_pair);
3361 __m256d element_00000_vec_imag = _mm256_permute4x64_pd(element_00000_vec_real, 0b10110001);
3362 __m256d element_00010_vec_imag = _mm256_permute4x64_pd(element_00010_vec_real, 0b10110001);
3363 __m256d element_00100_vec_imag = _mm256_permute4x64_pd(element_00100_vec_real, 0b10110001);
3364 __m256d element_00110_vec_imag = _mm256_permute4x64_pd(element_00110_vec_real, 0b10110001);
3365 __m256d element_01000_vec_imag = _mm256_permute4x64_pd(element_01000_vec_real, 0b10110001);
3366 __m256d element_01010_vec_imag = _mm256_permute4x64_pd(element_01010_vec_real, 0b10110001);
3367 __m256d element_01100_vec_imag = _mm256_permute4x64_pd(element_01100_vec_real, 0b10110001);
3368 __m256d element_01110_vec_imag = _mm256_permute4x64_pd(element_01110_vec_real, 0b10110001);
3369 __m256d element_10000_vec_imag = _mm256_permute4x64_pd(element_10000_vec_real, 0b10110001);
3370 __m256d element_10010_vec_imag = _mm256_permute4x64_pd(element_10010_vec_real, 0b10110001);
3371 __m256d element_10100_vec_imag = _mm256_permute4x64_pd(element_10100_vec_real, 0b10110001);
3372 __m256d element_10110_vec_imag = _mm256_permute4x64_pd(element_10110_vec_real, 0b10110001);
3373 __m256d element_11000_vec_imag = _mm256_permute4x64_pd(element_11000_vec_real, 0b10110001);
3374 __m256d element_11010_vec_imag = _mm256_permute4x64_pd(element_11010_vec_real, 0b10110001);
3375 __m256d element_11100_vec_imag = _mm256_permute4x64_pd(element_11100_vec_real, 0b10110001);
3376 __m256d element_11110_vec_imag = _mm256_permute4x64_pd(element_11110_vec_real, 0b10110001);
3378 element_00000_vec_real = _mm256_mul_pd(element_00000_vec_real,neg);
3379 element_00010_vec_real = _mm256_mul_pd(element_00010_vec_real,neg);
3380 element_00100_vec_real = _mm256_mul_pd(element_00100_vec_real,neg);
3381 element_00110_vec_real = _mm256_mul_pd(element_00110_vec_real,neg);
3382 element_01000_vec_real = _mm256_mul_pd(element_01000_vec_real,neg);
3383 element_01010_vec_real = _mm256_mul_pd(element_01010_vec_real,neg);
3384 element_01100_vec_real = _mm256_mul_pd(element_01100_vec_real,neg);
3385 element_01110_vec_real = _mm256_mul_pd(element_01110_vec_real,neg);
3386 element_10000_vec_real = _mm256_mul_pd(element_10000_vec_real,neg);
3387 element_10010_vec_real = _mm256_mul_pd(element_10010_vec_real,neg);
3388 element_10100_vec_real = _mm256_mul_pd(element_10100_vec_real,neg);
3389 element_10110_vec_real = _mm256_mul_pd(element_10110_vec_real,neg);
3390 element_11000_vec_real = _mm256_mul_pd(element_11000_vec_real,neg);
3391 element_11010_vec_real = _mm256_mul_pd(element_11010_vec_real,neg);
3392 element_11100_vec_real = _mm256_mul_pd(element_11100_vec_real,neg);
3393 element_11110_vec_real = _mm256_mul_pd(element_11110_vec_real,neg);
3396 for (
int mult_idx = 0; mult_idx < 32; mult_idx++) {
3397 double* unitary_row_1 = (
double*)unitary.
get_data() + 64*mult_idx;
3398 double* unitary_row_2 = unitary_row_1 + 4;
3399 double* unitary_row_3 = unitary_row_1 + 8;
3400 double* unitary_row_4 = unitary_row_1 + 12;
3401 double* unitary_row_5 = unitary_row_1 + 16;
3402 double* unitary_row_6 = unitary_row_1 + 20;
3403 double* unitary_row_7 = unitary_row_1 + 24;
3404 double* unitary_row_8 = unitary_row_1 + 28;
3405 double* unitary_row_9 = unitary_row_1 + 32;
3406 double* unitary_row_10 = unitary_row_1 + 36;
3407 double* unitary_row_11 = unitary_row_1 + 40;
3408 double* unitary_row_12 = unitary_row_1 + 44;
3409 double* unitary_row_13 = unitary_row_1 + 48;
3410 double* unitary_row_14 = unitary_row_1 + 52;
3411 double* unitary_row_15 = unitary_row_1 + 56;
3412 double* unitary_row_16 = unitary_row_1 + 60;
3414 __m256d row1_vec = _mm256_loadu_pd(unitary_row_1);
3415 __m256d row2_vec = _mm256_loadu_pd(unitary_row_2);
3416 __m256d row3_vec = _mm256_loadu_pd(unitary_row_3);
3417 __m256d row4_vec = _mm256_loadu_pd(unitary_row_4);
3418 __m256d row5_vec = _mm256_loadu_pd(unitary_row_5);
3419 __m256d row6_vec = _mm256_loadu_pd(unitary_row_6);
3420 __m256d row7_vec = _mm256_loadu_pd(unitary_row_7);
3421 __m256d row8_vec = _mm256_loadu_pd(unitary_row_8);
3422 __m256d row9_vec = _mm256_loadu_pd(unitary_row_9);
3423 __m256d row10_vec = _mm256_loadu_pd(unitary_row_10);
3424 __m256d row11_vec = _mm256_loadu_pd(unitary_row_11);
3425 __m256d row12_vec = _mm256_loadu_pd(unitary_row_12);
3426 __m256d row13_vec = _mm256_loadu_pd(unitary_row_13);
3427 __m256d row14_vec = _mm256_loadu_pd(unitary_row_14);
3428 __m256d row15_vec = _mm256_loadu_pd(unitary_row_15);
3429 __m256d row16_vec = _mm256_loadu_pd(unitary_row_16);
3431 __m256d data_real = _mm256_setzero_pd();
3432 __m256d data_imag = _mm256_setzero_pd();
3434 data_real = _mm256_fmadd_pd(element_00000_vec_real, row1_vec, data_real);
3435 data_imag = _mm256_fmadd_pd(element_00000_vec_imag, row1_vec, data_imag);
3436 data_real = _mm256_fmadd_pd(element_00010_vec_real, row2_vec, data_real);
3437 data_imag = _mm256_fmadd_pd(element_00010_vec_imag, row2_vec, data_imag);
3438 data_real = _mm256_fmadd_pd(element_00100_vec_real, row3_vec, data_real);
3439 data_imag = _mm256_fmadd_pd(element_00100_vec_imag, row3_vec, data_imag);
3440 data_real = _mm256_fmadd_pd(element_00110_vec_real, row4_vec, data_real);
3441 data_imag = _mm256_fmadd_pd(element_00110_vec_imag, row4_vec, data_imag);
3442 data_real = _mm256_fmadd_pd(element_01000_vec_real, row5_vec, data_real);
3443 data_imag = _mm256_fmadd_pd(element_01000_vec_imag, row5_vec, data_imag);
3444 data_real = _mm256_fmadd_pd(element_01010_vec_real, row6_vec, data_real);
3445 data_imag = _mm256_fmadd_pd(element_01010_vec_imag, row6_vec, data_imag);
3446 data_real = _mm256_fmadd_pd(element_01100_vec_real, row7_vec, data_real);
3447 data_imag = _mm256_fmadd_pd(element_01100_vec_imag, row7_vec, data_imag);
3448 data_real = _mm256_fmadd_pd(element_01110_vec_real, row8_vec, data_real);
3449 data_imag = _mm256_fmadd_pd(element_01110_vec_imag, row8_vec, data_imag);
3450 data_real = _mm256_fmadd_pd(element_10000_vec_real, row9_vec, data_real);
3451 data_imag = _mm256_fmadd_pd(element_10000_vec_imag, row9_vec, data_imag);
3452 data_real = _mm256_fmadd_pd(element_10010_vec_real, row10_vec, data_real);
3453 data_imag = _mm256_fmadd_pd(element_10010_vec_imag, row10_vec, data_imag);
3454 data_real = _mm256_fmadd_pd(element_10100_vec_real, row11_vec, data_real);
3455 data_imag = _mm256_fmadd_pd(element_10100_vec_imag, row11_vec, data_imag);
3456 data_real = _mm256_fmadd_pd(element_10110_vec_real, row12_vec, data_real);
3457 data_imag = _mm256_fmadd_pd(element_10110_vec_imag, row12_vec, data_imag);
3458 data_real = _mm256_fmadd_pd(element_11000_vec_real, row13_vec, data_real);
3459 data_imag = _mm256_fmadd_pd(element_11000_vec_imag, row13_vec, data_imag);
3460 data_real = _mm256_fmadd_pd(element_11010_vec_real, row14_vec, data_real);
3461 data_imag = _mm256_fmadd_pd(element_11010_vec_imag, row14_vec, data_imag);
3462 data_real = _mm256_fmadd_pd(element_11100_vec_real, row15_vec, data_real);
3463 data_imag = _mm256_fmadd_pd(element_11100_vec_imag, row15_vec, data_imag);
3464 data_real = _mm256_fmadd_pd(element_11110_vec_real, row16_vec, data_real);
3465 data_imag = _mm256_fmadd_pd(element_11110_vec_imag, row16_vec, data_imag);
3467 __m256d final_vec = _mm256_hadd_pd(data_real, data_imag);
3468 final_vec = _mm256_permute4x64_pd(final_vec, 0b11011000);
3469 final_vec = _mm256_hadd_pd(final_vec, final_vec);
3470 __m128d low128 = _mm256_castpd256_pd128(final_vec);
3471 __m128d high128 = _mm256_extractf128_pd(final_vec, 1);
3472 results[mult_idx].
real = _mm_cvtsd_f64(low128);
3473 results[mult_idx].
imag = _mm_cvtsd_f64(high128);
3475 input[current_idx_outer_loc] = results[0];
3476 input[current_idx_inner_loc] = results[1];
3477 input[current_idx_middle1_loc] = results[2];
3478 input[current_idx_middle1_inner_loc] = results[3];
3479 input[current_idx_middle2_loc] = results[4];
3480 input[current_idx_middle2_inner_loc] = results[5];
3481 input[current_idx_middle12_loc] = results[6];
3482 input[current_idx_middle12_inner_loc] = results[7];
3483 input[current_idx_middle3_loc] = results[8];
3484 input[current_idx_middle3_inner_loc] = results[9];
3485 input[current_idx_middle13_loc] = results[10];
3486 input[current_idx_middle13_inner_loc] = results[11];
3487 input[current_idx_middle23_loc] = results[12];
3488 input[current_idx_middle23_inner_loc] = results[13];
3489 input[current_idx_middle123_loc] = results[14];
3490 input[current_idx_middle123_inner_loc] = results[15];
3491 input[current_idx_outer_pair_loc] = results[16];
3492 input[current_idx_inner_pair_loc] = results[17];
3493 input[current_idx_middle1_pair_loc] = results[18];
3494 input[current_idx_middle1_inner_pair_loc] = results[19];
3495 input[current_idx_middle2_pair_loc] = results[20];
3496 input[current_idx_middle2_inner_pair_loc] = results[21];
3497 input[current_idx_middle12_pair_loc] = results[22];
3498 input[current_idx_middle12_inner_pair_loc] = results[23];
3499 input[current_idx_middle3_pair_loc] = results[24];
3500 input[current_idx_middle3_inner_pair_loc] = results[25];
3501 input[current_idx_middle13_pair_loc] = results[26];
3502 input[current_idx_middle13_inner_pair_loc] = results[27];
3503 input[current_idx_middle23_pair_loc] = results[28];
3504 input[current_idx_middle23_inner_pair_loc] = results[29];
3505 input[current_idx_middle123_pair_loc] = results[30];
3506 input[current_idx_middle123_inner_pair_loc] = results[31];
3521 int inner_qbit = involved_qbits[0];
3522 int outer_qbit = involved_qbits[1];
3523 int index_step_outer = 1 << outer_qbit;
3524 int index_step_inner = 1 << inner_qbit;
3525 int current_idx = 0;
3526 __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0);
3527 for (
int current_idx_pair_outer=current_idx + index_step_outer; current_idx_pair_outer<input.
rows; current_idx_pair_outer=current_idx_pair_outer+(index_step_outer << 1)){
3529 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner=current_idx_inner+(index_step_inner<<1)){
3530 for (
int idx=0; idx<index_step_inner; idx++){
3532 int current_idx_outer_loc = current_idx + current_idx_inner + idx;
3533 int current_idx_inner_loc = current_idx + current_idx_inner + idx + index_step_inner;
3534 int current_idx_outer_pair_loc = current_idx_pair_outer + idx + current_idx_inner;
3535 int current_idx_inner_pair_loc = current_idx_pair_outer + idx + current_idx_inner + index_step_inner;
3537 int row_offset_outer = current_idx_outer_loc*input.
cols;
3538 int row_offset_inner = current_idx_inner_loc*input.
cols;
3539 int row_offset_outer_pair = current_idx_outer_pair_loc*input.
cols;
3540 int row_offset_inner_pair = current_idx_inner_pair_loc*input.
cols;
3541 #pragma omp parallel for 3542 for (
int col_idx=0; col_idx<input.
cols;col_idx++){
3544 int current_idx_outer = row_offset_outer + col_idx;
3545 int current_idx_inner = row_offset_inner + col_idx;
3546 int current_idx_outer_pair = row_offset_outer_pair + col_idx;
3547 int current_idx_inner_pair = row_offset_inner_pair + col_idx;
3549 double results[8] = {0.,0.,0.,0.,0.,0.,0.,0.};
3551 double* element_outer = (
double*)input.
get_data() + 2 * current_idx_outer;
3552 double* element_inner = (
double*)input.
get_data() + 2 * current_idx_inner;
3553 double* element_outer_pair = (
double*)input.
get_data() + 2 * current_idx_outer_pair;
3554 double* element_inner_pair = (
double*)input.
get_data() + 2 * current_idx_inner_pair;
3556 __m256d element_outer_vec = _mm256_loadu_pd(element_outer);
3557 element_outer_vec = _mm256_permute4x64_pd(element_outer_vec,0b11011000);
3558 __m256d element_inner_vec = _mm256_loadu_pd(element_inner);
3559 element_inner_vec = _mm256_permute4x64_pd(element_inner_vec,0b11011000);
3560 __m256d outer_inner_vec = _mm256_shuffle_pd(element_outer_vec,element_inner_vec,0b0000);
3561 outer_inner_vec = _mm256_permute4x64_pd(outer_inner_vec,0b11011000);
3564 __m256d element_outer_pair_vec = _mm256_loadu_pd(element_outer_pair);
3565 element_outer_pair_vec = _mm256_permute4x64_pd(element_outer_pair_vec,0b11011000);
3566 __m256d element_inner_pair_vec = _mm256_loadu_pd(element_inner_pair);
3567 element_inner_pair_vec = _mm256_permute4x64_pd(element_inner_pair_vec,0b11011000);
3568 __m256d outer_inner_pair_vec = _mm256_shuffle_pd(element_outer_pair_vec,element_inner_pair_vec,0b0000);
3569 outer_inner_pair_vec = _mm256_permute4x64_pd(outer_inner_pair_vec,0b11011000);
3574 for (
int mult_idx=0; mult_idx<4; mult_idx++){
3575 double* unitary_row_01 = (
double*)two_qbit_unitary.
get_data() + 8*mult_idx;
3576 double* unitary_row_23 = (
double*)two_qbit_unitary.
get_data() + 8*mult_idx + 4;
3578 __m256d unitary_row_01_vec = _mm256_loadu_pd(unitary_row_01);
3579 __m256d unitary_row_23_vec = _mm256_loadu_pd(unitary_row_23);
3581 __m256d result_upper_vec =
complex_mult_AVX(outer_inner_vec,unitary_row_01_vec,neg);
3583 __m256d result_lower_vec =
complex_mult_AVX(outer_inner_pair_vec,unitary_row_23_vec,neg);
3585 __m256d result_vec = _mm256_hadd_pd(result_upper_vec,result_lower_vec);
3586 result_vec = _mm256_hadd_pd(result_vec,result_vec);
3587 double*
result = (
double*)&result_vec;
3588 results[mult_idx*2] = result[0];
3589 results[mult_idx*2+1] = result[2];
3591 input[current_idx_outer].real = results[0];
3592 input[current_idx_outer].imag = results[1];
3593 input[current_idx_inner].real = results[2];
3594 input[current_idx_inner].imag = results[3];
3595 input[current_idx_outer_pair].real = results[4];
3596 input[current_idx_outer_pair].imag = results[5];
3597 input[current_idx_inner_pair].real = results[6];
3598 input[current_idx_inner_pair].imag = results[7];
3603 current_idx = current_idx + (index_step_outer << 1);
3621 int current_idx = 0;
3624 __m256d u3_1bit_00r_vec = _mm256_broadcast_sd(&u3_1qbit1[0].
real);
3625 __m256d u3_1bit_00i_vec = _mm256_broadcast_sd(&u3_1qbit1[0].imag);
3626 __m256d u3_1bit_01r_vec = _mm256_broadcast_sd(&u3_1qbit1[1].real);
3627 __m256d u3_1bit_01i_vec = _mm256_broadcast_sd(&u3_1qbit1[1].imag);
3628 __m256d u3_1bit_10r_vec = _mm256_broadcast_sd(&u3_1qbit1[2].real);
3629 __m256d u3_1bit_10i_vec = _mm256_broadcast_sd(&u3_1qbit1[2].imag);
3630 __m256d u3_1bit_11r_vec = _mm256_broadcast_sd(&u3_1qbit1[3].real);
3631 __m256d u3_1bit_11i_vec = _mm256_broadcast_sd(&u3_1qbit1[3].imag);
3633 __m256d u3_1bit2_00r_vec = _mm256_broadcast_sd(&u3_1qbit2[0].real);
3634 __m256d u3_1bit2_00i_vec = _mm256_broadcast_sd(&u3_1qbit2[0].imag);
3635 __m256d u3_1bit2_01r_vec = _mm256_broadcast_sd(&u3_1qbit2[1].real);
3636 __m256d u3_1bit2_01i_vec = _mm256_broadcast_sd(&u3_1qbit2[1].imag);
3637 __m256d u3_1bit2_10r_vec = _mm256_broadcast_sd(&u3_1qbit2[2].real);
3638 __m256d u3_1bit2_10i_vec = _mm256_broadcast_sd(&u3_1qbit2[2].imag);
3639 __m256d u3_1bit2_11r_vec = _mm256_broadcast_sd(&u3_1qbit2[3].real);
3640 __m256d u3_1bit2_11i_vec = _mm256_broadcast_sd(&u3_1qbit2[3].imag);
3643 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) ) {
3646 for (
int idx = 0; idx < index_step_target; idx++) {
3649 int current_idx_loc = current_idx + idx;
3650 int current_idx_pair_loc = current_idx_pair + idx;
3652 int row_offset = current_idx_loc * input.
stride;
3653 int row_offset_pair = current_idx_pair_loc * input.
stride;
3654 for (
int col_idx = 0; col_idx < 2 * (input.
cols - 3); col_idx = col_idx + 8) {
3655 double* element = (
double*)input.
get_data() + 2 * row_offset;
3656 double* element_pair = (
double*)input.
get_data() + 2 * row_offset_pair;
3657 if ((current_idx_loc >> control_qbit) & 1) {
3661 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
3662 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
3663 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
3664 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
3667 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
3668 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
3669 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
3670 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
3671 element_pair_vec = tmp;
3673 __m256d vec3 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec);
3674 vec3 = _mm256_fnmadd_pd(u3_1bit_00i_vec, element_vec2, vec3);
3675 __m256d vec4 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec);
3676 vec4 = _mm256_fnmadd_pd(u3_1bit_01i_vec, element_pair_vec2, vec4);
3677 vec3 = _mm256_add_pd(vec3, vec4);
3678 __m256d vec5 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec2);
3679 vec5 = _mm256_fmadd_pd(u3_1bit_00i_vec, element_vec, vec5);
3680 __m256d vec6 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec2);
3681 vec6 = _mm256_fmadd_pd(u3_1bit_01i_vec, element_pair_vec, vec6);
3682 vec5 = _mm256_add_pd(vec5, vec6);
3685 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
3686 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
3688 _mm256_storeu_pd(element + col_idx, vec3);
3689 _mm256_storeu_pd(element + col_idx + 4, vec5);
3691 __m256d vec7 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec);
3692 vec7 = _mm256_fnmadd_pd(u3_1bit_10i_vec, element_vec2, vec7);
3693 __m256d vec8 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec);
3694 vec8 = _mm256_fnmadd_pd(u3_1bit_11i_vec, element_pair_vec2, vec8);
3695 vec7 = _mm256_add_pd(vec7, vec8);
3696 __m256d vec9 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec2);
3697 vec9 = _mm256_fmadd_pd(u3_1bit_10i_vec, element_vec, vec9);
3698 __m256d vec10 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec2);
3699 vec10 = _mm256_fmadd_pd(u3_1bit_11i_vec, element_pair_vec, vec10);
3700 vec9 = _mm256_add_pd(vec9, vec10);
3703 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
3704 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
3706 _mm256_storeu_pd(element_pair + col_idx, vec7);
3707 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
3716 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
3717 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
3718 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
3719 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
3722 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
3723 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
3724 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
3725 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
3726 element_pair_vec = tmp;
3728 __m256d vec3 = _mm256_mul_pd(u3_1bit2_00r_vec, element_vec);
3729 vec3 = _mm256_fnmadd_pd(u3_1bit2_00i_vec, element_vec2, vec3);
3730 __m256d vec4 = _mm256_mul_pd(u3_1bit2_01r_vec, element_pair_vec);
3731 vec4 = _mm256_fnmadd_pd(u3_1bit2_01i_vec, element_pair_vec2, vec4);
3732 vec3 = _mm256_add_pd(vec3, vec4);
3733 __m256d vec5 = _mm256_mul_pd(u3_1bit2_00r_vec, element_vec2);
3734 vec5 = _mm256_fmadd_pd(u3_1bit2_00i_vec, element_vec, vec5);
3735 __m256d vec6 = _mm256_mul_pd(u3_1bit2_01r_vec, element_pair_vec2);
3736 vec6 = _mm256_fmadd_pd(u3_1bit2_01i_vec, element_pair_vec, vec6);
3737 vec5 = _mm256_add_pd(vec5, vec6);
3740 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
3741 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
3743 _mm256_storeu_pd(element + col_idx, vec3);
3744 _mm256_storeu_pd(element + col_idx + 4, vec5);
3746 __m256d vec7 = _mm256_mul_pd(u3_1bit2_10r_vec, element_vec);
3747 vec7 = _mm256_fnmadd_pd(u3_1bit2_10i_vec, element_vec2, vec7);
3748 __m256d vec8 = _mm256_mul_pd(u3_1bit2_11r_vec, element_pair_vec);
3749 vec8 = _mm256_fnmadd_pd(u3_1bit2_11i_vec, element_pair_vec2, vec8);
3750 vec7 = _mm256_add_pd(vec7, vec8);
3751 __m256d vec9 = _mm256_mul_pd(u3_1bit2_10r_vec, element_vec2);
3752 vec9 = _mm256_fmadd_pd(u3_1bit2_10i_vec, element_vec, vec9);
3753 __m256d vec10 = _mm256_mul_pd(u3_1bit2_11r_vec, element_pair_vec2);
3754 vec10 = _mm256_fmadd_pd(u3_1bit2_11i_vec, element_pair_vec, vec10);
3755 vec9 = _mm256_add_pd(vec9, vec10);
3758 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
3759 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
3761 _mm256_storeu_pd(element_pair + col_idx, vec7);
3762 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
3766 int remainder = input.
cols % 4;
3767 if (remainder != 0) {
3769 for (
int col_idx = input.
cols-remainder; col_idx < input.
cols; col_idx++) {
3770 int index = row_offset+col_idx;
3771 int index_pair = row_offset_pair+col_idx;
3772 if ( (current_idx_loc >> control_qbit) & 1 ) {
3782 input[index].real = tmp1.
real + tmp2.
real;
3783 input[index].imag = tmp1.
imag + tmp2.
imag;
3785 tmp1 =
mult(u3_1qbit1[2], element);
3786 tmp2 =
mult(u3_1qbit1[3], element_pair);
3788 input[index_pair].real = tmp1.
real + tmp2.
real;
3789 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
3800 input[index].real = tmp1.
real + tmp2.
real;
3801 input[index].imag = tmp1.
imag + tmp2.
imag;
3803 tmp1 =
mult(u3_1qbit2[2], element);
3804 tmp2 =
mult(u3_1qbit2[3], element_pair);
3806 input[index_pair].real = tmp1.
real + tmp2.
real;
3807 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
3819 current_idx = current_idx + (index_step_target << 1);
3844 __m256d u3_1bit_00r_vec = _mm256_broadcast_sd(&u3_1qbit1[0].
real);
3845 __m256d u3_1bit_00i_vec = _mm256_broadcast_sd(&u3_1qbit1[0].imag);
3846 __m256d u3_1bit_01r_vec = _mm256_broadcast_sd(&u3_1qbit1[1].real);
3847 __m256d u3_1bit_01i_vec = _mm256_broadcast_sd(&u3_1qbit1[1].imag);
3848 __m256d u3_1bit_10r_vec = _mm256_broadcast_sd(&u3_1qbit1[2].real);
3849 __m256d u3_1bit_10i_vec = _mm256_broadcast_sd(&u3_1qbit1[2].imag);
3850 __m256d u3_1bit_11r_vec = _mm256_broadcast_sd(&u3_1qbit1[3].real);
3851 __m256d u3_1bit_11i_vec = _mm256_broadcast_sd(&u3_1qbit1[3].imag);
3853 __m256d u3_1bit2_00r_vec = _mm256_broadcast_sd(&u3_1qbit2[0].real);
3854 __m256d u3_1bit2_00i_vec = _mm256_broadcast_sd(&u3_1qbit2[0].imag);
3855 __m256d u3_1bit2_01r_vec = _mm256_broadcast_sd(&u3_1qbit2[1].real);
3856 __m256d u3_1bit2_01i_vec = _mm256_broadcast_sd(&u3_1qbit2[1].imag);
3857 __m256d u3_1bit2_10r_vec = _mm256_broadcast_sd(&u3_1qbit2[2].real);
3858 __m256d u3_1bit2_10i_vec = _mm256_broadcast_sd(&u3_1qbit2[2].imag);
3859 __m256d u3_1bit2_11r_vec = _mm256_broadcast_sd(&u3_1qbit2[3].real);
3860 __m256d u3_1bit2_11i_vec = _mm256_broadcast_sd(&u3_1qbit2[3].imag);
3863 int parallel_outer_cycles = matrix_size/(index_step_target << 1);
3864 int outer_grain_size;
3865 if ( index_step_target <= 2 ) {
3866 outer_grain_size = 32;
3868 else if ( index_step_target <= 4 ) {
3869 outer_grain_size = 16;
3871 else if ( index_step_target <= 8 ) {
3872 outer_grain_size = 8;
3874 else if ( index_step_target <= 16 ) {
3875 outer_grain_size = 4;
3878 outer_grain_size = 2;
3882 tbb::parallel_for( tbb::blocked_range<int>(0,parallel_outer_cycles,outer_grain_size), [&](tbb::blocked_range<int> r) {
3884 int current_idx = r.begin()*(index_step_target << 1);
3885 int current_idx_pair = index_step_target + r.begin()*(index_step_target << 1);
3887 for (
int rdx=r.begin(); rdx<r.end(); rdx++) {
3890 tbb::parallel_for( tbb::blocked_range<int>(0,index_step_target,32), [&](tbb::blocked_range<int> r) {
3891 for (
int idx=r.begin(); idx<r.end(); ++idx) {
3894 int current_idx_loc = current_idx + idx;
3895 int current_idx_pair_loc = current_idx_pair + idx;
3897 int row_offset = current_idx_loc * input.
stride;
3898 int row_offset_pair = current_idx_pair_loc * input.
stride;
3900 if ((current_idx_loc >> control_qbit) & 1) {
3903 double* element = (
double*)input.
get_data() + 2 * row_offset;
3904 double* element_pair = (
double*)input.
get_data() + 2 * row_offset_pair;
3907 for (
int col_idx = 0; col_idx < 2 * (input.
cols - 3); col_idx = col_idx + 8) {
3910 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
3911 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
3912 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
3913 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
3916 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
3917 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
3918 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
3919 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
3920 element_pair_vec = tmp;
3922 __m256d vec3 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec);
3923 vec3 = _mm256_fnmadd_pd(u3_1bit_00i_vec, element_vec2, vec3);
3924 __m256d vec4 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec);
3925 vec4 = _mm256_fnmadd_pd(u3_1bit_01i_vec, element_pair_vec2, vec4);
3926 vec3 = _mm256_add_pd(vec3, vec4);
3927 __m256d vec5 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec2);
3928 vec5 = _mm256_fmadd_pd(u3_1bit_00i_vec, element_vec, vec5);
3929 __m256d vec6 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec2);
3930 vec6 = _mm256_fmadd_pd(u3_1bit_01i_vec, element_pair_vec, vec6);
3931 vec5 = _mm256_add_pd(vec5, vec6);
3934 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
3935 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
3937 _mm256_storeu_pd(element + col_idx, vec3);
3938 _mm256_storeu_pd(element + col_idx + 4, vec5);
3940 __m256d vec7 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec);
3941 vec7 = _mm256_fnmadd_pd(u3_1bit_10i_vec, element_vec2, vec7);
3942 __m256d vec8 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec);
3943 vec8 = _mm256_fnmadd_pd(u3_1bit_11i_vec, element_pair_vec2, vec8);
3944 vec7 = _mm256_add_pd(vec7, vec8);
3945 __m256d vec9 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec2);
3946 vec9 = _mm256_fmadd_pd(u3_1bit_10i_vec, element_vec, vec9);
3947 __m256d vec10 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec2);
3948 vec10 = _mm256_fmadd_pd(u3_1bit_11i_vec, element_pair_vec, vec10);
3949 vec9 = _mm256_add_pd(vec9, vec10);
3952 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
3953 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
3955 _mm256_storeu_pd(element_pair + col_idx, vec7);
3956 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
3959 int remainder = input.
cols % 4;
3960 if (remainder != 0) {
3962 for (
int col_idx = input.
cols-remainder; col_idx < input.
cols; col_idx++) {
3963 int index = row_offset + col_idx;
3964 int index_pair = row_offset_pair + col_idx;
3972 input[index].real = tmp1.
real + tmp2.
real;
3973 input[index].imag = tmp1.
imag + tmp2.
imag;
3975 tmp1 =
mult(u3_1qbit1[2], element);
3976 tmp2 =
mult(u3_1qbit1[3], element_pair);
3978 input[index_pair].real = tmp1.
real + tmp2.
real;
3979 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
3988 double* element = (
double*)input.
get_data() + 2 * row_offset;
3989 double* element_pair = (
double*)input.
get_data() + 2 * row_offset_pair;
3992 for (
int col_idx = 0; col_idx < 2 * (input.
cols - 3); col_idx = col_idx + 8) {
3995 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
3996 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
3997 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
3998 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
4001 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
4002 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
4003 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
4004 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
4005 element_pair_vec = tmp;
4007 __m256d vec3 = _mm256_mul_pd(u3_1bit2_00r_vec, element_vec);
4008 vec3 = _mm256_fnmadd_pd(u3_1bit2_00i_vec, element_vec2, vec3);
4009 __m256d vec4 = _mm256_mul_pd(u3_1bit2_01r_vec, element_pair_vec);
4010 vec4 = _mm256_fnmadd_pd(u3_1bit2_01i_vec, element_pair_vec2, vec4);
4011 vec3 = _mm256_add_pd(vec3, vec4);
4012 __m256d vec5 = _mm256_mul_pd(u3_1bit2_00r_vec, element_vec2);
4013 vec5 = _mm256_fmadd_pd(u3_1bit2_00i_vec, element_vec, vec5);
4014 __m256d vec6 = _mm256_mul_pd(u3_1bit2_01r_vec, element_pair_vec2);
4015 vec6 = _mm256_fmadd_pd(u3_1bit2_01i_vec, element_pair_vec, vec6);
4016 vec5 = _mm256_add_pd(vec5, vec6);
4019 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
4020 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
4022 _mm256_storeu_pd(element + col_idx, vec3);
4023 _mm256_storeu_pd(element + col_idx + 4, vec5);
4025 __m256d vec7 = _mm256_mul_pd(u3_1bit2_10r_vec, element_vec);
4026 vec7 = _mm256_fnmadd_pd(u3_1bit2_10i_vec, element_vec2, vec7);
4027 __m256d vec8 = _mm256_mul_pd(u3_1bit2_11r_vec, element_pair_vec);
4028 vec8 = _mm256_fnmadd_pd(u3_1bit2_11i_vec, element_pair_vec2, vec8);
4029 vec7 = _mm256_add_pd(vec7, vec8);
4030 __m256d vec9 = _mm256_mul_pd(u3_1bit2_10r_vec, element_vec2);
4031 vec9 = _mm256_fmadd_pd(u3_1bit2_10i_vec, element_vec, vec9);
4032 __m256d vec10 = _mm256_mul_pd(u3_1bit2_11r_vec, element_pair_vec2);
4033 vec10 = _mm256_fmadd_pd(u3_1bit2_11i_vec, element_pair_vec, vec10);
4034 vec9 = _mm256_add_pd(vec9, vec10);
4037 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
4038 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
4040 _mm256_storeu_pd(element_pair + col_idx, vec7);
4041 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
4044 int remainder = input.
cols % 4;
4045 if (remainder != 0) {
4047 for (
int col_idx = input.
cols-remainder; col_idx < input.
cols; col_idx++) {
4048 int index = row_offset + col_idx;
4049 int index_pair = row_offset_pair + col_idx;
4057 input[index].real = tmp1.
real + tmp2.
real;
4058 input[index].imag = tmp1.
imag + tmp2.
imag;
4060 tmp1 =
mult(u3_1qbit2[2], element);
4061 tmp2 =
mult(u3_1qbit2[3], element_pair);
4063 input[index_pair].real = tmp1.
real + tmp2.
real;
4064 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
4079 current_idx = current_idx + (index_step_target << 1);
4080 current_idx_pair = current_idx_pair + (index_step_target << 1);
4095 auto select_gate = [&](
int block_start) ->
Matrix& {
4096 return ((control_qbit < 0) || ((block_start >>
control_qbit) & 1)) ? u3_1qbit1 : u3_1qbit2;
4099 auto apply_gate_block = [&](
Matrix& gate,
double* element,
double* element_pair,
int avx_limit,
int& col_idx) {
4100 const __m256d u00r = _mm256_broadcast_sd(&gate[0].
real);
4101 const __m256d u00i = _mm256_broadcast_sd(&gate[0].imag);
4102 const __m256d u01r = _mm256_broadcast_sd(&gate[1].real);
4103 const __m256d u01i = _mm256_broadcast_sd(&gate[1].imag);
4104 const __m256d u10r = _mm256_broadcast_sd(&gate[2].real);
4105 const __m256d u10i = _mm256_broadcast_sd(&gate[2].imag);
4106 const __m256d u11r = _mm256_broadcast_sd(&gate[3].real);
4107 const __m256d u11i = _mm256_broadcast_sd(&gate[3].imag);
4109 for (col_idx = 0; col_idx < avx_limit; col_idx += 8) {
4110 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
4111 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
4112 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
4113 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
4116 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
4117 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
4118 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
4119 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
4120 element_pair_vec = tmp;
4122 __m256d vec3 = _mm256_mul_pd(u00r, element_vec);
4123 vec3 = _mm256_fnmadd_pd(u00i, element_vec2, vec3);
4124 __m256d vec4 = _mm256_mul_pd(u10r, element_pair_vec);
4125 vec4 = _mm256_fnmadd_pd(u10i, element_pair_vec2, vec4);
4126 vec3 = _mm256_add_pd(vec3, vec4);
4127 __m256d vec5 = _mm256_mul_pd(u00r, element_vec2);
4128 vec5 = _mm256_fmadd_pd(u00i, element_vec, vec5);
4129 __m256d vec6 = _mm256_mul_pd(u10r, element_pair_vec2);
4130 vec6 = _mm256_fmadd_pd(u10i, element_pair_vec, vec6);
4131 vec5 = _mm256_add_pd(vec5, vec6);
4133 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
4134 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
4136 _mm256_storeu_pd(element + col_idx, vec3);
4137 _mm256_storeu_pd(element + col_idx + 4, vec5);
4139 __m256d vec7 = _mm256_mul_pd(u01r, element_vec);
4140 vec7 = _mm256_fnmadd_pd(u01i, element_vec2, vec7);
4141 __m256d vec8 = _mm256_mul_pd(u11r, element_pair_vec);
4142 vec8 = _mm256_fnmadd_pd(u11i, element_pair_vec2, vec8);
4143 vec7 = _mm256_add_pd(vec7, vec8);
4144 __m256d vec9 = _mm256_mul_pd(u01r, element_vec2);
4145 vec9 = _mm256_fmadd_pd(u01i, element_vec, vec9);
4146 __m256d vec10 = _mm256_mul_pd(u11r, element_pair_vec2);
4147 vec10 = _mm256_fmadd_pd(u11i, element_pair_vec, vec10);
4148 vec9 = _mm256_add_pd(vec9, vec10);
4150 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
4151 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
4153 _mm256_storeu_pd(element_pair + col_idx, vec7);
4154 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
4158 for (
int row_idx = 0; row_idx < input.
rows; ++row_idx) {
4159 const int row_offset = row_idx * input.
stride;
4160 int current_idx = 0;
4161 int current_idx_pair = index_step_target;
4163 while (current_idx_pair < input.
cols) {
4164 const bool mixed = (control_qbit >= 0 && control_qbit <
target_qbit);
4167 Matrix& gate = select_gate(current_idx);
4168 double* element = (
double*)input.
get_data() + 2 * (row_offset + current_idx);
4169 double* element_pair = (
double*)input.
get_data() + 2 * (row_offset + current_idx_pair);
4171 const int avx_limit = 2 * (index_step_target - 3);
4172 apply_gate_block(gate, element, element_pair, avx_limit, col_idx);
4174 for (
int c = col_idx / 2; c < index_step_target; ++c) {
4175 const int index = row_offset + current_idx + c;
4176 const int index_pair = row_offset + current_idx_pair + c;
4181 input[index].real = tmp1.
real + tmp2.
real;
4182 input[index].imag = tmp1.
imag + tmp2.
imag;
4183 tmp1 =
mult(gate[1], a);
4184 tmp2 =
mult(gate[3], b);
4185 input[index_pair].real = tmp1.
real + tmp2.
real;
4186 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
4189 for (
int idx = 0; idx < index_step_target; ++idx) {
4190 const int current_idx_loc = current_idx + idx;
4191 const int current_idx_pair_loc = current_idx_pair + idx;
4192 Matrix& gate = select_gate(current_idx_loc);
4193 const int index = row_offset + current_idx_loc;
4194 const int index_pair = row_offset + current_idx_pair_loc;
4199 input[index].real = tmp1.
real + tmp2.
real;
4200 input[index].imag = tmp1.
imag + tmp2.
imag;
4201 tmp1 =
mult(gate[1], a);
4202 tmp2 =
mult(gate[3], b);
4203 input[index_pair].real = tmp1.
real + tmp2.
real;
4204 input[index_pair].imag = tmp1.
imag + tmp2.
imag;
4208 current_idx += (index_step_target << 1);
4209 current_idx_pair += (index_step_target << 1);
4223 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, 16), [&](
const tbb::blocked_range<int>& range) {
4224 for (int row_idx = range.begin(); row_idx < range.end(); ++row_idx) {
4225 const int row_offset = row_idx * input.stride;
4226 int current_idx = 0;
4227 int current_idx_pair = index_step_target;
4229 while (current_idx_pair < input.cols) {
4230 const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
4233 Matrix& gate = ((control_qbit < 0) || ((current_idx >> control_qbit) & 1)) ? u3_1qbit1 : u3_1qbit2;
4234 const __m256d u00r = _mm256_broadcast_sd(&gate[0].real);
4235 const __m256d u00i = _mm256_broadcast_sd(&gate[0].imag);
4236 const __m256d u01r = _mm256_broadcast_sd(&gate[1].real);
4237 const __m256d u01i = _mm256_broadcast_sd(&gate[1].imag);
4238 const __m256d u10r = _mm256_broadcast_sd(&gate[2].real);
4239 const __m256d u10i = _mm256_broadcast_sd(&gate[2].imag);
4240 const __m256d u11r = _mm256_broadcast_sd(&gate[3].real);
4241 const __m256d u11i = _mm256_broadcast_sd(&gate[3].imag);
4242 double* element = (double*)input.get_data() + 2 * (row_offset + current_idx);
4243 double* element_pair = (double*)input.get_data() + 2 * (row_offset + current_idx_pair);
4245 const int avx_limit = 2 * (index_step_target - 3);
4247 for (; col_idx < avx_limit; col_idx += 8) {
4248 __m256d element_vec = _mm256_loadu_pd(element + col_idx);
4249 __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
4250 __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
4251 element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
4253 __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
4254 __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
4255 tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
4256 element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
4257 element_pair_vec = tmp;
4259 __m256d vec3 = _mm256_mul_pd(u00r, element_vec);
4260 vec3 = _mm256_fnmadd_pd(u00i, element_vec2, vec3);
4261 __m256d vec4 = _mm256_mul_pd(u10r, element_pair_vec);
4262 vec4 = _mm256_fnmadd_pd(u10i, element_pair_vec2, vec4);
4263 vec3 = _mm256_add_pd(vec3, vec4);
4264 __m256d vec5 = _mm256_mul_pd(u00r, element_vec2);
4265 vec5 = _mm256_fmadd_pd(u00i, element_vec, vec5);
4266 __m256d vec6 = _mm256_mul_pd(u10r, element_pair_vec2);
4267 vec6 = _mm256_fmadd_pd(u10i, element_pair_vec, vec6);
4268 vec5 = _mm256_add_pd(vec5, vec6);
4269 tmp = _mm256_shuffle_pd(vec3, vec5, 0);
4270 vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
4272 _mm256_storeu_pd(element + col_idx, vec3);
4273 _mm256_storeu_pd(element + col_idx + 4, vec5);
4275 __m256d vec7 = _mm256_mul_pd(u01r, element_vec);
4276 vec7 = _mm256_fnmadd_pd(u01i, element_vec2, vec7);
4277 __m256d vec8 = _mm256_mul_pd(u11r, element_pair_vec);
4278 vec8 = _mm256_fnmadd_pd(u11i, element_pair_vec2, vec8);
4279 vec7 = _mm256_add_pd(vec7, vec8);
4280 __m256d vec9 = _mm256_mul_pd(u01r, element_vec2);
4281 vec9 = _mm256_fmadd_pd(u01i, element_vec, vec9);
4282 __m256d vec10 = _mm256_mul_pd(u11r, element_pair_vec2);
4283 vec10 = _mm256_fmadd_pd(u11i, element_pair_vec, vec10);
4284 vec9 = _mm256_add_pd(vec9, vec10);
4285 tmp = _mm256_shuffle_pd(vec7, vec9, 0);
4286 vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
4288 _mm256_storeu_pd(element_pair + col_idx, vec7);
4289 _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
4292 for (int c = col_idx / 2; c < index_step_target; ++c) {
4293 const int index = row_offset + current_idx + c;
4294 const int index_pair = row_offset + current_idx_pair + c;
4295 QGD_Complex16 a = input[index];
4296 QGD_Complex16 b = input[index_pair];
4297 QGD_Complex16 tmp1 = mult(gate[0], a);
4298 QGD_Complex16 tmp2 = mult(gate[2], b);
4299 input[index].real = tmp1.real + tmp2.real;
4300 input[index].imag = tmp1.imag + tmp2.imag;
4301 tmp1 = mult(gate[1], a);
4302 tmp2 = mult(gate[3], b);
4303 input[index_pair].real = tmp1.real + tmp2.real;
4304 input[index_pair].imag = tmp1.imag + tmp2.imag;
4307 for (int idx = 0; idx < index_step_target; ++idx) {
4308 const int current_idx_loc = current_idx + idx;
4309 const int current_idx_pair_loc = current_idx_pair + idx;
4310 Matrix& gate = ((current_idx_loc >> control_qbit) & 1) ? u3_1qbit1 : u3_1qbit2;
4311 const int index = row_offset + current_idx_loc;
4312 const int index_pair = row_offset + current_idx_pair_loc;
4313 QGD_Complex16 a = input[index];
4314 QGD_Complex16 b = input[index_pair];
4315 QGD_Complex16 tmp1 = mult(gate[0], a);
4316 QGD_Complex16 tmp2 = mult(gate[2], b);
4317 input[index].real = tmp1.real + tmp2.real;
4318 input[index].imag = tmp1.imag + tmp2.imag;
4319 tmp1 = mult(gate[1], a);
4320 tmp2 = mult(gate[3], b);
4321 input[index_pair].real = tmp1.real + tmp2.real;
4322 input[index_pair].imag = tmp1.imag + tmp2.imag;
4326 current_idx += (index_step_target << 1);
4327 current_idx_pair += (index_step_target << 1);
4346 if (input.
cols == 1) {
4347 switch (involved_qbits.size()) {
4364 switch (involved_qbits.size()) {
4390 if (input.
cols == 1) {
4391 switch (involved_qbits.size()) {
4408 switch (involved_qbits.size()) {
4410 apply_fixed_qbit_unitary_AVX_OpenMP32<2>(
unitary, input, involved_qbits,
matrix_size);
4414 apply_fixed_qbit_unitary_AVX_OpenMP32<3>(
unitary, input, involved_qbits,
matrix_size);
4418 apply_fixed_qbit_unitary_AVX_OpenMP32<4>(
unitary, input, involved_qbits,
matrix_size);
4422 apply_fixed_qbit_unitary_AVX_OpenMP32<5>(
unitary, input, involved_qbits,
matrix_size);
4434 if (input.
cols == 1) {
4435 switch (involved_qbits.size()) {
4452 switch (involved_qbits.size()) {
4484 constexpr
int block_size = 1 <<
n;
4486 for (
int rdx = 0; rdx < block_size; ++rdx) {
4487 for (
int cdx = 0; cdx < block_size; cdx += 2) {
4488 mv_xy[rdx * block_size + cdx] = _mm256_set_pd(
4489 -gate_kernel_unitary[block_size * rdx + cdx + 1].imag,
4490 gate_kernel_unitary[block_size * rdx + cdx + 1].
real,
4491 -gate_kernel_unitary[block_size * rdx + cdx].imag,
4492 gate_kernel_unitary[block_size * rdx + cdx].
real 4495 mv_xy[rdx * block_size + cdx + 1] = _mm256_set_pd(
4496 gate_kernel_unitary[block_size * rdx + cdx + 1].real,
4497 gate_kernel_unitary[block_size * rdx + cdx + 1].imag,
4498 gate_kernel_unitary[block_size * rdx + cdx].real,
4499 gate_kernel_unitary[block_size * rdx + cdx].imag
4507 constexpr
int block_size = 1 <<
n;
4508 const double* data_ptr = (
const double*)input.
get_data();
4509 const int stride = input.
stride;
4511 const int idx0 = indices[cdx] * stride + col;
4512 const int idx1 = indices[cdx + 1] * stride + col;
4514 const __m256d
data = _mm256_set_pd(
4515 data_ptr[2 * idx1 + 1],
4516 data_ptr[2 * idx1 + 0],
4517 data_ptr[2 * idx0 + 1],
4518 data_ptr[2 * idx0 + 0]
4521 const __m256d mv_x0 = mv_xy[block_size * rdx + cdx];
4522 const __m256d mv_x1 = mv_xy[block_size * rdx + cdx + 1];
4523 result = _mm256_add_pd(result, _mm256_hadd_pd(_mm256_mul_pd(data, mv_x0), _mm256_mul_pd(data, mv_x1)));
4528 constexpr
int block_size = 1 <<
n;
4529 const int qubit_num = (
int) std::log2(matrix_size);
4530 const int num_blocks = matrix_size >>
n;
4531 std::sort(involved_qbits.begin(), involved_qbits.end());
4533 int non_targets[64];
4534 int non_target_count = 0;
4535 for (
int q = 0; q < qubit_num; ++q) {
4536 bool is_target =
false;
4537 for (
int target : involved_qbits) {
4538 is_target = is_target || (q == target);
4541 non_targets[non_target_count++] = q;
4545 int block_pattern[block_size];
4546 for (
int k = 0;
k < block_size; ++
k) {
4548 for (
int bit = 0; bit <
n; ++bit) {
4549 if (
k & (1 << bit)) {
4550 idx |= (1 << involved_qbits[bit]);
4553 block_pattern[
k] = idx;
4556 int indices[block_size];
4558 __m256d mv_xy[block_size * block_size];
4559 construct_mv_xy_vectors_fixed<n>(gate_kernel_unitary, mv_xy);
4561 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
4563 for (
int i = 0; i < non_target_count; ++i) {
4564 if (iter_idx & (1ULL << i)) {
4565 base |= (1 << non_targets[i]);
4568 for (
int k = 0;
k < block_size; ++
k) {
4569 indices[
k] = base | block_pattern[
k];
4572 for (
int col = 0; col < input.
cols; ++col) {
4573 for (
int rdx = 0; rdx < block_size; ++rdx) {
4574 __m256d
result = _mm256_setzero_pd();
4576 for (
int cdx = 0; cdx < block_size; cdx += 2) {
4577 complex_prod_AVX_fixed<n>(mv_xy, rdx, cdx, indices, input, col,
result);
4580 __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
4581 __m256d sum = _mm256_add_pd(result, perm);
4582 __m128d low128 = _mm256_castpd256_pd128(sum);
4583 out[rdx].
real = _mm_cvtsd_f64(low128);
4584 out[rdx].
imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
4587 for (
int rdx = 0; rdx < block_size; ++rdx) {
4588 input[indices[rdx] * input.
stride + col] = out[rdx];
4596 constexpr
int block_size = 1 <<
n;
4597 const int qubit_num = (
int) std::log2(matrix_size);
4598 const int num_blocks = matrix_size >>
n;
4599 std::sort(involved_qbits.begin(), involved_qbits.end());
4601 int non_targets[64];
4602 int non_target_count = 0;
4603 for (
int q = 0; q < qubit_num; ++q) {
4604 bool is_target =
false;
4605 for (
int target : involved_qbits) {
4606 is_target = is_target || (q == target);
4609 non_targets[non_target_count++] = q;
4613 int block_pattern[block_size];
4614 for (
int k = 0;
k < block_size; ++
k) {
4616 for (
int bit = 0; bit <
n; ++bit) {
4617 if (
k & (1 << bit)) {
4618 idx |= (1 << involved_qbits[bit]);
4621 block_pattern[
k] = idx;
4624 __m256d mv_xy[block_size * block_size];
4625 construct_mv_xy_vectors_fixed<n>(gate_kernel_unitary, mv_xy);
4627 #pragma omp parallel for schedule(static) 4628 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
4629 int indices[block_size];
4633 for (
int i = 0; i < non_target_count; ++i) {
4634 if (iter_idx & (1ULL << i)) {
4635 base |= (1 << non_targets[i]);
4638 for (
int k = 0;
k < block_size; ++
k) {
4639 indices[
k] = base | block_pattern[
k];
4642 for (
int col = 0; col < input.
cols; ++col) {
4643 for (
int rdx = 0; rdx < block_size; ++rdx) {
4644 __m256d
result = _mm256_setzero_pd();
4646 for (
int cdx = 0; cdx < block_size; cdx += 2) {
4647 complex_prod_AVX_fixed<n>(mv_xy, rdx, cdx, indices, input, col,
result);
4650 __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
4651 __m256d sum = _mm256_add_pd(result, perm);
4652 __m128d low128 = _mm256_castpd256_pd128(sum);
4653 out[rdx].
real = _mm_cvtsd_f64(low128);
4654 out[rdx].
imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
4657 for (
int rdx = 0; rdx < block_size; ++rdx) {
4658 input[indices[rdx] * input.
stride + col] = out[rdx];
4666 constexpr
int block_size = 1 <<
n;
4667 const int qubit_num = (
int) std::log2(matrix_size);
4668 const int num_blocks = matrix_size >>
n;
4669 std::sort(involved_qbits.begin(), involved_qbits.end());
4671 int non_targets[64];
4672 int non_target_count = 0;
4673 for (
int q = 0; q < qubit_num; ++q) {
4674 bool is_target =
false;
4675 for (
int target : involved_qbits) {
4676 is_target = is_target || (q == target);
4679 non_targets[non_target_count++] = q;
4683 int block_pattern[block_size];
4684 for (
int k = 0;
k < block_size; ++
k) {
4686 for (
int bit = 0; bit <
n; ++bit) {
4687 if (
k & (1 << bit)) {
4688 idx |= (1 << involved_qbits[bit]);
4691 block_pattern[
k] = idx;
4694 __m256d mv_xy[block_size * block_size];
4695 construct_mv_xy_vectors_fixed<n>(gate_kernel_unitary, mv_xy);
4697 tbb::parallel_for(tbb::blocked_range<int>(0, num_blocks, 16), [&](
const tbb::blocked_range<int>& range) {
4698 for (
int iter_idx = range.begin(); iter_idx < range.end(); ++iter_idx) {
4699 std::array<int, 1 << n> indices;
4700 std::array<QGD_Complex16, 1 << n> out;
4703 for (
int i = 0; i < non_target_count; ++i) {
4704 if (iter_idx & (1ULL << i)) {
4705 base |= (1 << non_targets[i]);
4708 for (
int k = 0;
k < block_size; ++
k) {
4709 indices[
k] = base | block_pattern[
k];
4712 for (
int col = 0; col < input.
cols; ++col) {
4713 for (
int rdx = 0; rdx < block_size; ++rdx) {
4714 __m256d
result = _mm256_setzero_pd();
4716 for (
int cdx = 0; cdx < block_size; cdx += 2) {
4717 complex_prod_AVX_fixed<n>(mv_xy, rdx, cdx, indices.data(), input, col,
result);
4720 __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
4721 __m256d sum = _mm256_add_pd(result, perm);
4722 __m128d low128 = _mm256_castpd256_pd128(sum);
4723 out[rdx].real = _mm_cvtsd_f64(low128);
4724 out[rdx].imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
4727 for (
int rdx = 0; rdx < block_size; ++rdx) {
4728 input[indices[rdx] * input.
stride + col] = out[rdx];
4737 template<
typename MatrixT>
4738 MatrixT transpose_local_kernel(
const MatrixT&
unitary) {
4739 MatrixT transposed = unitary.copy();
4740 for (
int row = 0; row < unitary.rows; ++row) {
4741 for (
int col = 0; col < unitary.cols; ++col) {
4742 transposed[row * unitary.cols + col] = unitary[col * unitary.cols + row];
4748 inline Matrix transpose_local_kernel_4x4(
const Matrix& unitary) {
4750 for (
int row = 0; row < 4; ++row) {
4751 for (
int col = 0; col < 4; ++col) {
4752 transposed[row * 4 + col] = unitary[col * 4 + row];
4760 for (
int row = 0; row < 4; ++row) {
4761 for (
int col = 0; col < 4; ++col) {
4762 transposed[row * 4 + col] = unitary[col * 4 + row];
4768 inline void complex_prod_AVX_from_right_generic(
const __m256d* mv_xy,
int block_size,
int rdx,
int cdx,
const int* indices,
const Matrix& input,
int row_offset, __m256d&
result) {
4769 const int idx0 = row_offset + indices[cdx];
4770 const int idx1 = row_offset + indices[cdx + 1];
4771 const double* data_ptr = (
const double*)input.
get_data();
4773 const __m256d
data = _mm256_set_pd(
4774 data_ptr[2 * idx1 + 1],
4775 data_ptr[2 * idx1 + 0],
4776 data_ptr[2 * idx0 + 1],
4777 data_ptr[2 * idx0 + 0]
4780 const __m256d mv_x0 = mv_xy[block_size * rdx + cdx];
4781 const __m256d mv_x1 = mv_xy[block_size * rdx + cdx + 1];
4782 const __m256d data_u0 = _mm256_mul_pd(data, mv_x0);
4783 const __m256d data_u1 = _mm256_mul_pd(data, mv_x1);
4784 result = _mm256_add_pd(result, _mm256_hadd_pd(data_u0, data_u1));
4787 inline void complex_prod_AVX_from_right(
const __m256d* mv_xy,
int rdx,
int cdx,
const int* indices,
const Matrix& input,
int row_offset, __m256d& result) {
4788 const int block_size = 4;
4789 const int idx0 = row_offset + indices[cdx];
4790 const int idx1 = row_offset + indices[cdx + 1];
4791 const double* data_ptr = (
const double*)input.
get_data();
4793 const __m256d
data = _mm256_set_pd(
4794 data_ptr[2 * idx1 + 1],
4795 data_ptr[2 * idx1 + 0],
4796 data_ptr[2 * idx0 + 1],
4797 data_ptr[2 * idx0 + 0]
4800 const __m256d mv_x0 = mv_xy[block_size * rdx + cdx];
4801 const __m256d mv_x1 = mv_xy[block_size * rdx + cdx + 1];
4802 const __m256d data_u0 = _mm256_mul_pd(data, mv_x0);
4803 const __m256d data_u1 = _mm256_mul_pd(data, mv_x1);
4804 result = _mm256_add_pd(result, _mm256_hadd_pd(data_u0, data_u1));
4807 inline void apply_nqbit_kernel_to_matrix_input_from_right_AVX_impl(
Matrix& unitary,
Matrix& input, std::vector<int> involved_qbits,
const int& matrix_size) {
4808 if (input.
cols == 1) {
4809 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
4812 const int n =
static_cast<int>(involved_qbits.size());
4813 const int block_size = 1 <<
n;
4814 if (n < 2 || n > 5 || unitary.
rows != block_size || unitary.
cols != block_size) {
4815 throw std::invalid_argument(
"AVX right-apply large-kernel path received an unsupported local kernel size.");
4818 const int qubit_num = (
int)std::log2(matrix_size);
4819 std::sort(involved_qbits.begin(), involved_qbits.end());
4821 std::vector<int> non_targets;
4822 non_targets.reserve(qubit_num - n);
4823 for (
int q = 0; q < qubit_num; ++q) {
4824 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
4825 non_targets.push_back(q);
4829 std::vector<int> block_pattern(block_size);
4831 std::vector<int> indices(block_size);
4832 std::vector<QGD_Complex16> out(block_size);
4833 const int num_blocks = matrix_size >>
n;
4834 const Matrix transposed = transpose_local_kernel(unitary);
4837 for (
int row = 0; row < input.
rows; ++row) {
4838 const int row_offset = row * input.
stride;
4840 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
4843 for (
int rdx = 0; rdx < block_size; ++rdx) {
4844 __m256d result = _mm256_setzero_pd();
4846 for (
int cdx = 0; cdx < block_size; cdx += 2) {
4847 complex_prod_AVX_from_right_generic(mv_xy, block_size, rdx, cdx, indices.data(), input, row_offset,
result);
4850 const __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
4851 const __m256d sum = _mm256_add_pd(result, perm);
4852 const __m128d low128 = _mm256_castpd256_pd128(sum);
4853 out[rdx].real = _mm_cvtsd_f64(low128);
4854 out[rdx].imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
4857 for (
int rdx = 0; rdx < block_size; ++rdx) {
4858 input[row_offset + indices[rdx]] = out[rdx];
4866 inline void apply_nqbit_kernel_to_matrix_input_from_right_AVX_OpenMP_impl(
Matrix& unitary,
Matrix& input, std::vector<int> involved_qbits,
const int& matrix_size) {
4867 if (input.
cols == 1) {
4868 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
4871 const int n =
static_cast<int>(involved_qbits.size());
4872 const int block_size = 1 <<
n;
4873 if (n < 2 || n > 5 || unitary.
rows != block_size || unitary.
cols != block_size) {
4874 throw std::invalid_argument(
"AVX right-apply large-kernel path received an unsupported local kernel size.");
4877 const int qubit_num = (
int)std::log2(matrix_size);
4878 std::sort(involved_qbits.begin(), involved_qbits.end());
4880 std::vector<int> non_targets;
4881 non_targets.reserve(qubit_num - n);
4882 for (
int q = 0; q < qubit_num; ++q) {
4883 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
4884 non_targets.push_back(q);
4888 std::vector<int> block_pattern(block_size);
4890 const int num_blocks = matrix_size >>
n;
4891 const Matrix transposed = transpose_local_kernel(unitary);
4894 #pragma omp parallel for schedule(static) 4895 for (
int row = 0; row < input.
rows; ++row) {
4896 const int row_offset = row * input.
stride;
4897 std::vector<int> indices(block_size);
4898 std::vector<QGD_Complex16> out(block_size);
4900 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
4903 for (
int rdx = 0; rdx < block_size; ++rdx) {
4904 __m256d result = _mm256_setzero_pd();
4906 for (
int cdx = 0; cdx < block_size; cdx += 2) {
4907 complex_prod_AVX_from_right_generic(mv_xy, block_size, rdx, cdx, indices.data(), input, row_offset,
result);
4910 const __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
4911 const __m256d sum = _mm256_add_pd(result, perm);
4912 const __m128d low128 = _mm256_castpd256_pd128(sum);
4913 out[rdx].real = _mm_cvtsd_f64(low128);
4914 out[rdx].imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
4917 for (
int rdx = 0; rdx < block_size; ++rdx) {
4918 input[row_offset + indices[rdx]] = out[rdx];
4926 inline void apply_nqbit_kernel_to_matrix_input_from_right_AVX_TBB_impl(
Matrix& unitary,
Matrix& input, std::vector<int> involved_qbits,
const int& matrix_size) {
4927 if (input.
cols == 1) {
4928 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
4931 const int n =
static_cast<int>(involved_qbits.size());
4932 const int block_size = 1 <<
n;
4933 if (n < 2 || n > 5 || unitary.
rows != block_size || unitary.
cols != block_size) {
4934 throw std::invalid_argument(
"AVX right-apply large-kernel path received an unsupported local kernel size.");
4937 const int qubit_num = (
int)std::log2(matrix_size);
4938 std::sort(involved_qbits.begin(), involved_qbits.end());
4940 std::vector<int> non_targets;
4941 non_targets.reserve(qubit_num - n);
4942 for (
int q = 0; q < qubit_num; ++q) {
4943 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
4944 non_targets.push_back(q);
4948 std::vector<int> block_pattern(block_size);
4950 const int num_blocks = matrix_size >>
n;
4951 const Matrix transposed = transpose_local_kernel(unitary);
4954 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, 16), [&](
const tbb::blocked_range<int>& range) {
4955 for (int row = range.begin(); row < range.end(); ++row) {
4956 const int row_offset = row * input.stride;
4957 std::vector<int> indices(block_size);
4958 std::vector<QGD_Complex16> out(block_size);
4960 for (int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
4961 get_block_indices_fast(iter_idx, involved_qbits, non_targets, block_pattern, indices);
4963 for (int rdx = 0; rdx < block_size; ++rdx) {
4964 __m256d result = _mm256_setzero_pd();
4966 for (int cdx = 0; cdx < block_size; cdx += 2) {
4967 complex_prod_AVX_from_right_generic(mv_xy, block_size, rdx, cdx, indices.data(), input, row_offset, result);
4970 const __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
4971 const __m256d sum = _mm256_add_pd(result, perm);
4972 const __m128d low128 = _mm256_castpd256_pd128(sum);
4973 out[rdx].real = _mm_cvtsd_f64(low128);
4974 out[rdx].imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
4977 for (int rdx = 0; rdx < block_size; ++rdx) {
4978 input[row_offset + indices[rdx]] = out[rdx];
4987 inline void build_2qbit_block_pattern(std::vector<int>& involved_qbits,
int* non_targets,
int& non_target_count,
int* block_pattern,
int qubit_num) {
4988 std::sort(involved_qbits.begin(), involved_qbits.end());
4990 non_target_count = 0;
4991 for (
int q = 0; q < qubit_num; ++q) {
4992 if (q != involved_qbits[0] && q != involved_qbits[1]) {
4993 non_targets[non_target_count++] = q;
4997 for (
int k = 0;
k < 4; ++
k) {
4999 for (
int bit = 0; bit < 2; ++bit) {
5000 if (
k & (1 << bit)) {
5001 idx |= (1 << involved_qbits[bit]);
5004 block_pattern[
k] = idx;
5008 inline void apply_2qbit_kernel_to_matrix_input_from_right_AVX_impl(
Matrix&
unitary,
Matrix& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5009 if (input.
cols == 1) {
5010 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5012 if (involved_qbits.size() != 2 || unitary.
rows != 4 || unitary.
cols != 4) {
5013 throw std::invalid_argument(
"AVX right-apply large-kernel path currently supports 4x4 local kernels only.");
5016 const int qubit_num = (
int)std::log2(matrix_size);
5017 int non_targets[64];
5018 int non_target_count = 0;
5019 int block_pattern[4];
5020 build_2qbit_block_pattern(involved_qbits, non_targets, non_target_count, block_pattern, qubit_num);
5022 const Matrix transposed = transpose_local_kernel_4x4(unitary);
5026 const int num_blocks = matrix_size >> 2;
5028 for (
int row = 0; row < input.
rows; ++row) {
5029 const int row_offset = row * input.
stride;
5031 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5033 for (
int i = 0; i < non_target_count; ++i) {
5034 if (iter_idx & (1ULL << i)) {
5035 base |= (1 << non_targets[i]);
5038 for (
int k = 0;
k < 4; ++
k) {
5039 indices[
k] = base | block_pattern[
k];
5042 for (
int rdx = 0; rdx < 4; ++rdx) {
5043 __m256d
result = _mm256_setzero_pd();
5045 for (
int cdx = 0; cdx < 4; cdx += 2) {
5046 complex_prod_AVX_from_right(mv_xy, rdx, cdx, indices, input, row_offset, result);
5049 const __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
5050 const __m256d sum = _mm256_add_pd(result, perm);
5051 const __m128d low128 = _mm256_castpd256_pd128(sum);
5052 out[rdx].
real = _mm_cvtsd_f64(low128);
5053 out[rdx].
imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
5056 for (
int rdx = 0; rdx < 4; ++rdx) {
5057 input[row_offset + indices[rdx]] = out[rdx];
5065 inline void apply_2qbit_kernel_to_matrix_input_from_right_AVX_OpenMP_impl(
Matrix& unitary,
Matrix& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5066 if (input.
cols == 1) {
5067 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5069 if (involved_qbits.size() != 2 || unitary.
rows != 4 || unitary.
cols != 4) {
5070 throw std::invalid_argument(
"AVX right-apply large-kernel path currently supports 4x4 local kernels only.");
5073 const int qubit_num = (
int)std::log2(matrix_size);
5074 int non_targets[64];
5075 int non_target_count = 0;
5076 int block_pattern[4];
5077 build_2qbit_block_pattern(involved_qbits, non_targets, non_target_count, block_pattern, qubit_num);
5079 const Matrix transposed = transpose_local_kernel_4x4(unitary);
5081 const int num_blocks = matrix_size >> 2;
5083 #pragma omp parallel for schedule(static) 5084 for (
int row = 0; row < input.
rows; ++row) {
5085 const int row_offset = row * input.
stride;
5089 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5091 for (
int i = 0; i < non_target_count; ++i) {
5092 if (iter_idx & (1ULL << i)) {
5093 base |= (1 << non_targets[i]);
5096 for (
int k = 0;
k < 4; ++
k) {
5097 indices[
k] = base | block_pattern[
k];
5100 for (
int rdx = 0; rdx < 4; ++rdx) {
5101 __m256d
result = _mm256_setzero_pd();
5102 for (
int cdx = 0; cdx < 4; cdx += 2) {
5103 complex_prod_AVX_from_right(mv_xy, rdx, cdx, indices, input, row_offset, result);
5106 const __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
5107 const __m256d sum = _mm256_add_pd(result, perm);
5108 const __m128d low128 = _mm256_castpd256_pd128(sum);
5109 out[rdx].
real = _mm_cvtsd_f64(low128);
5110 out[rdx].
imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
5113 for (
int rdx = 0; rdx < 4; ++rdx) {
5114 input[row_offset + indices[rdx]] = out[rdx];
5122 inline void apply_2qbit_kernel_to_matrix_input_from_right_AVX_TBB_impl(
Matrix& unitary,
Matrix& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5123 if (input.
cols == 1) {
5124 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5126 if (involved_qbits.size() != 2 || unitary.
rows != 4 || unitary.
cols != 4) {
5127 throw std::invalid_argument(
"AVX right-apply large-kernel path currently supports 4x4 local kernels only.");
5130 const int qubit_num = (
int)std::log2(matrix_size);
5131 int non_targets[64];
5132 int non_target_count = 0;
5133 int block_pattern[4];
5134 build_2qbit_block_pattern(involved_qbits, non_targets, non_target_count, block_pattern, qubit_num);
5136 const Matrix transposed = transpose_local_kernel_4x4(unitary);
5138 const int num_blocks = matrix_size >> 2;
5140 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, 16), [&](
const tbb::blocked_range<int>& range) {
5141 for (int row = range.begin(); row < range.end(); ++row) {
5142 const int row_offset = row * input.stride;
5144 QGD_Complex16 out[4];
5146 for (int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5148 for (int i = 0; i < non_target_count; ++i) {
5149 if (iter_idx & (1ULL << i)) {
5150 base |= (1 << non_targets[i]);
5153 for (int k = 0; k < 4; ++k) {
5154 indices[k] = base | block_pattern[k];
5157 for (int rdx = 0; rdx < 4; ++rdx) {
5158 __m256d result = _mm256_setzero_pd();
5159 for (int cdx = 0; cdx < 4; cdx += 2) {
5160 complex_prod_AVX_from_right(mv_xy, rdx, cdx, indices, input, row_offset, result);
5163 const __m256d perm = _mm256_permute2f128_pd(result, result, 0x01);
5164 const __m256d sum = _mm256_add_pd(result, perm);
5165 const __m128d low128 = _mm256_castpd256_pd128(sum);
5166 out[rdx].real = _mm_cvtsd_f64(low128);
5167 out[rdx].imag = _mm_cvtsd_f64(_mm_unpackhi_pd(low128, low128));
5170 for (int rdx = 0; rdx < 4; ++rdx) {
5171 input[row_offset + indices[rdx]] = out[rdx];
5181 inline void complex_prod_AVX32_fixed_from_right(
const __m256* mv_xy,
int rdx,
int cdx,
const int* indices,
const Matrix_float& input,
int row_offset, __m256&
result) {
5182 constexpr
int block_size = 1 <<
n;
5183 const float* data_ptr = (
const float*)input.
get_data();
5185 const int idx0 = row_offset + indices[cdx];
5186 const int idx1 = row_offset + indices[cdx + 1];
5187 const int idx2 = row_offset + indices[cdx + 2];
5188 const int idx3 = row_offset + indices[cdx + 3];
5190 const __m256
data = _mm256_set_ps(
5191 data_ptr[2 * idx3 + 1],
5192 data_ptr[2 * idx3 + 0],
5193 data_ptr[2 * idx2 + 1],
5194 data_ptr[2 * idx2 + 0],
5195 data_ptr[2 * idx1 + 1],
5196 data_ptr[2 * idx1 + 0],
5197 data_ptr[2 * idx0 + 1],
5198 data_ptr[2 * idx0 + 0]
5201 const __m256 mv_x0 = mv_xy[block_size * rdx + cdx];
5202 const __m256 mv_x1 = mv_xy[block_size * rdx + cdx + 1];
5203 result = _mm256_add_ps(result, _mm256_hadd_ps(_mm256_mul_ps(data, mv_x0), _mm256_mul_ps(data, mv_x1)));
5206 inline void complex_prod_AVX32_from_right(
const __m256* mv_xy,
int block_size,
int rdx,
int cdx,
const int* indices,
const Matrix_float& input,
int row_offset, __m256&
result) {
5207 const float* data_ptr = (
const float*)input.
get_data();
5209 const int idx0 = row_offset + indices[cdx];
5210 const int idx1 = row_offset + indices[cdx + 1];
5211 const int idx2 = row_offset + indices[cdx + 2];
5212 const int idx3 = row_offset + indices[cdx + 3];
5214 const __m256
data = _mm256_set_ps(
5215 data_ptr[2 * idx3 + 1],
5216 data_ptr[2 * idx3 + 0],
5217 data_ptr[2 * idx2 + 1],
5218 data_ptr[2 * idx2 + 0],
5219 data_ptr[2 * idx1 + 1],
5220 data_ptr[2 * idx1 + 0],
5221 data_ptr[2 * idx0 + 1],
5222 data_ptr[2 * idx0 + 0]
5225 const __m256 mv_x0 = mv_xy[block_size * rdx + cdx];
5226 const __m256 mv_x1 = mv_xy[block_size * rdx + cdx + 1];
5227 result = _mm256_add_ps(result, _mm256_hadd_ps(_mm256_mul_ps(data, mv_x0), _mm256_mul_ps(data, mv_x1)));
5230 inline void apply_2qbit_kernel_to_matrix_input_from_right_AVX32_impl(
Matrix_float& unitary,
Matrix_float& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5231 if (input.
cols == 1) {
5232 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5234 if (involved_qbits.size() != 2 || unitary.
rows != 4 || unitary.
cols != 4) {
5235 throw std::invalid_argument(
"AVX32 right-apply large-kernel path currently supports 4x4 local kernels only.");
5238 const int qubit_num = (
int)std::log2(matrix_size);
5239 int non_targets[64];
5240 int non_target_count = 0;
5241 int block_pattern[4];
5242 build_2qbit_block_pattern(involved_qbits, non_targets, non_target_count, block_pattern, qubit_num);
5244 const Matrix_float transposed = transpose_local_kernel_4x4(unitary);
5246 construct_mv_xy_vectors32_fixed<2>(transposed, mv_xy);
5249 const int num_blocks = matrix_size >> 2;
5251 for (
int row = 0; row < input.
rows; ++row) {
5252 const int row_offset = row * input.
stride;
5254 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5256 for (
int i = 0; i < non_target_count; ++i) {
5257 if (iter_idx & (1ULL << i)) {
5258 base |= (1 << non_targets[i]);
5261 for (
int k = 0;
k < 4; ++
k) {
5262 indices[
k] = base | block_pattern[
k];
5265 for (
int rdx = 0; rdx < 4; ++rdx) {
5266 __m256
result = _mm256_setzero_ps();
5267 complex_prod_AVX32_fixed_from_right<2>(mv_xy, rdx, 0, indices, input, row_offset,
result);
5269 alignas(32)
float acc[8];
5270 _mm256_store_ps(acc, result);
5271 out[rdx].
real = acc[0] + acc[1] + acc[4] + acc[5];
5272 out[rdx].
imag = acc[2] + acc[3] + acc[6] + acc[7];
5275 for (
int rdx = 0; rdx < 4; ++rdx) {
5276 input[row_offset + indices[rdx]] = out[rdx];
5282 inline void apply_2qbit_kernel_to_matrix_input_from_right_AVX_OpenMP32_impl(
Matrix_float& unitary,
Matrix_float& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5283 if (input.
cols == 1) {
5284 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5286 if (involved_qbits.size() != 2 || unitary.
rows != 4 || unitary.
cols != 4) {
5287 throw std::invalid_argument(
"AVX32 right-apply large-kernel path currently supports 4x4 local kernels only.");
5290 const int qubit_num = (
int)std::log2(matrix_size);
5291 int non_targets[64];
5292 int non_target_count = 0;
5293 int block_pattern[4];
5294 build_2qbit_block_pattern(involved_qbits, non_targets, non_target_count, block_pattern, qubit_num);
5296 const Matrix_float transposed = transpose_local_kernel_4x4(unitary);
5298 construct_mv_xy_vectors32_fixed<2>(transposed, mv_xy);
5299 const int num_blocks = matrix_size >> 2;
5301 #pragma omp parallel for schedule(static) 5302 for (
int row = 0; row < input.
rows; ++row) {
5303 const int row_offset = row * input.
stride;
5307 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5309 for (
int i = 0; i < non_target_count; ++i) {
5310 if (iter_idx & (1ULL << i)) {
5311 base |= (1 << non_targets[i]);
5314 for (
int k = 0;
k < 4; ++
k) {
5315 indices[
k] = base | block_pattern[
k];
5318 for (
int rdx = 0; rdx < 4; ++rdx) {
5319 __m256
result = _mm256_setzero_ps();
5320 complex_prod_AVX32_fixed_from_right<2>(mv_xy, rdx, 0, indices, input, row_offset,
result);
5322 alignas(32)
float acc[8];
5323 _mm256_store_ps(acc, result);
5324 out[rdx].
real = acc[0] + acc[1] + acc[4] + acc[5];
5325 out[rdx].
imag = acc[2] + acc[3] + acc[6] + acc[7];
5328 for (
int rdx = 0; rdx < 4; ++rdx) {
5329 input[row_offset + indices[rdx]] = out[rdx];
5335 inline void apply_2qbit_kernel_to_matrix_input_from_right_AVX_TBB32_impl(
Matrix_float& unitary,
Matrix_float& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5336 if (input.
cols == 1) {
5337 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5339 if (involved_qbits.size() != 2 || unitary.
rows != 4 || unitary.
cols != 4) {
5340 throw std::invalid_argument(
"AVX32 right-apply large-kernel path currently supports 4x4 local kernels only.");
5343 const int qubit_num = (
int)std::log2(matrix_size);
5344 int non_targets[64];
5345 int non_target_count = 0;
5346 int block_pattern[4];
5347 build_2qbit_block_pattern(involved_qbits, non_targets, non_target_count, block_pattern, qubit_num);
5349 const Matrix_float transposed = transpose_local_kernel_4x4(unitary);
5351 construct_mv_xy_vectors32_fixed<2>(transposed, mv_xy);
5352 const int num_blocks = matrix_size >> 2;
5354 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, 16), [&](
const tbb::blocked_range<int>& range) {
5355 for (int row = range.begin(); row < range.end(); ++row) {
5356 const int row_offset = row * input.stride;
5358 QGD_Complex8 out[4];
5360 for (int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5362 for (int i = 0; i < non_target_count; ++i) {
5363 if (iter_idx & (1ULL << i)) {
5364 base |= (1 << non_targets[i]);
5367 for (int k = 0; k < 4; ++k) {
5368 indices[k] = base | block_pattern[k];
5371 for (int rdx = 0; rdx < 4; ++rdx) {
5372 __m256 result = _mm256_setzero_ps();
5373 complex_prod_AVX32_fixed_from_right<2>(mv_xy, rdx, 0, indices, input, row_offset, result);
5375 alignas(32) float acc[8];
5376 _mm256_store_ps(acc, result);
5377 out[rdx].real = acc[0] + acc[1] + acc[4] + acc[5];
5378 out[rdx].imag = acc[2] + acc[3] + acc[6] + acc[7];
5381 for (int rdx = 0; rdx < 4; ++rdx) {
5382 input[row_offset + indices[rdx]] = out[rdx];
5389 inline void apply_nqbit_kernel_to_matrix_input_from_right_AVX32_impl(
Matrix_float& unitary,
Matrix_float& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5390 if (input.
cols == 1) {
5391 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5394 const int n =
static_cast<int>(involved_qbits.size());
5395 const int block_size = 1 <<
n;
5396 if (n < 2 || n > 5 || unitary.
rows != block_size || unitary.
cols != block_size) {
5397 throw std::invalid_argument(
"AVX32 right-apply large-kernel path received an unsupported local kernel size.");
5400 const int qubit_num = (
int)std::log2(matrix_size);
5401 std::sort(involved_qbits.begin(), involved_qbits.end());
5403 std::vector<int> non_targets;
5404 non_targets.reserve(qubit_num - n);
5405 for (
int q = 0; q < qubit_num; ++q) {
5406 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
5407 non_targets.push_back(q);
5411 std::vector<int> block_pattern(block_size);
5413 std::vector<int> indices(block_size);
5414 std::vector<QGD_Complex8> out(block_size);
5415 const int num_blocks = matrix_size >>
n;
5416 const Matrix_float transposed = transpose_local_kernel(unitary);
5419 for (
int row = 0; row < input.
rows; ++row) {
5420 const int row_offset = row * input.
stride;
5422 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5425 for (
int rdx = 0; rdx < block_size; ++rdx) {
5426 __m256
result = _mm256_setzero_ps();
5428 for (
int cdx = 0; cdx < block_size; cdx += 4) {
5429 complex_prod_AVX32_from_right(mv_xy, block_size, rdx, cdx, indices.data(), input, row_offset,
result);
5432 alignas(32)
float acc[8];
5433 _mm256_store_ps(acc, result);
5434 out[rdx].real = acc[0] + acc[1] + acc[4] + acc[5];
5435 out[rdx].imag = acc[2] + acc[3] + acc[6] + acc[7];
5438 for (
int rdx = 0; rdx < block_size; ++rdx) {
5439 input[row_offset + indices[rdx]] = out[rdx];
5447 inline void apply_nqbit_kernel_to_matrix_input_from_right_AVX_OpenMP32_impl(
Matrix_float& unitary,
Matrix_float& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5448 if (input.
cols == 1) {
5449 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5452 const int n =
static_cast<int>(involved_qbits.size());
5453 const int block_size = 1 <<
n;
5454 if (n < 2 || n > 5 || unitary.
rows != block_size || unitary.
cols != block_size) {
5455 throw std::invalid_argument(
"AVX32 right-apply large-kernel path received an unsupported local kernel size.");
5458 const int qubit_num = (
int)std::log2(matrix_size);
5459 std::sort(involved_qbits.begin(), involved_qbits.end());
5461 std::vector<int> non_targets;
5462 non_targets.reserve(qubit_num - n);
5463 for (
int q = 0; q < qubit_num; ++q) {
5464 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
5465 non_targets.push_back(q);
5469 std::vector<int> block_pattern(block_size);
5471 const int num_blocks = matrix_size >>
n;
5472 const Matrix_float transposed = transpose_local_kernel(unitary);
5475 #pragma omp parallel for schedule(static) 5476 for (
int row = 0; row < input.
rows; ++row) {
5477 const int row_offset = row * input.
stride;
5478 std::vector<int> indices(block_size);
5479 std::vector<QGD_Complex8> out(block_size);
5481 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5484 for (
int rdx = 0; rdx < block_size; ++rdx) {
5485 __m256
result = _mm256_setzero_ps();
5487 for (
int cdx = 0; cdx < block_size; cdx += 4) {
5488 complex_prod_AVX32_from_right(mv_xy, block_size, rdx, cdx, indices.data(), input, row_offset,
result);
5491 alignas(32)
float acc[8];
5492 _mm256_store_ps(acc, result);
5493 out[rdx].real = acc[0] + acc[1] + acc[4] + acc[5];
5494 out[rdx].imag = acc[2] + acc[3] + acc[6] + acc[7];
5497 for (
int rdx = 0; rdx < block_size; ++rdx) {
5498 input[row_offset + indices[rdx]] = out[rdx];
5506 inline void apply_nqbit_kernel_to_matrix_input_from_right_AVX_TBB32_impl(
Matrix_float& unitary,
Matrix_float& input, std::vector<int> involved_qbits,
const int& matrix_size) {
5507 if (input.
cols == 1) {
5508 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
5511 const int n =
static_cast<int>(involved_qbits.size());
5512 const int block_size = 1 <<
n;
5513 if (n < 2 || n > 5 || unitary.
rows != block_size || unitary.
cols != block_size) {
5514 throw std::invalid_argument(
"AVX32 right-apply large-kernel path received an unsupported local kernel size.");
5517 const int qubit_num = (
int)std::log2(matrix_size);
5518 std::sort(involved_qbits.begin(), involved_qbits.end());
5520 std::vector<int> non_targets;
5521 non_targets.reserve(qubit_num - n);
5522 for (
int q = 0; q < qubit_num; ++q) {
5523 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
5524 non_targets.push_back(q);
5528 std::vector<int> block_pattern(block_size);
5530 const int num_blocks = matrix_size >>
n;
5531 const Matrix_float transposed = transpose_local_kernel(unitary);
5534 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, 16), [&](
const tbb::blocked_range<int>& range) {
5535 for (int row = range.begin(); row < range.end(); ++row) {
5536 const int row_offset = row * input.stride;
5537 std::vector<int> indices(block_size);
5538 std::vector<QGD_Complex8> out(block_size);
5540 for (int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5541 get_block_indices_fast(iter_idx, involved_qbits, non_targets, block_pattern, indices);
5543 for (int rdx = 0; rdx < block_size; ++rdx) {
5544 __m256 result = _mm256_setzero_ps();
5546 for (int cdx = 0; cdx < block_size; cdx += 4) {
5547 complex_prod_AVX32_from_right(mv_xy, block_size, rdx, cdx, indices.data(), input, row_offset, result);
5550 alignas(32) float acc[8];
5551 _mm256_store_ps(acc, result);
5552 out[rdx].real = acc[0] + acc[1] + acc[4] + acc[5];
5553 out[rdx].imag = acc[2] + acc[3] + acc[6] + acc[7];
5556 for (int rdx = 0; rdx < block_size; ++rdx) {
5557 input[row_offset + indices[rdx]] = out[rdx];
5569 if (input.
cols != 1 && involved_qbits.size() == 2 && unitary.
rows == 4 && unitary.
cols == 4) {
5570 apply_2qbit_kernel_to_matrix_input_from_right_AVX_impl(unitary, input, std::move(involved_qbits), matrix_size);
5574 if (input.
cols != 1 && involved_qbits.size() >= 3 && involved_qbits.size() <= 5) {
5575 apply_nqbit_kernel_to_matrix_input_from_right_AVX_impl(unitary, input, std::move(involved_qbits), matrix_size);
5583 if (input.
cols != 1 && involved_qbits.size() == 2 && unitary.
rows == 4 && unitary.
cols == 4) {
5584 apply_2qbit_kernel_to_matrix_input_from_right_AVX_OpenMP_impl(unitary, input, std::move(involved_qbits), matrix_size);
5588 if (input.
cols != 1 && involved_qbits.size() >= 3 && involved_qbits.size() <= 5) {
5589 apply_nqbit_kernel_to_matrix_input_from_right_AVX_OpenMP_impl(unitary, input, std::move(involved_qbits), matrix_size);
5597 if (input.
cols != 1 && involved_qbits.size() == 2 && unitary.
rows == 4 && unitary.
cols == 4) {
5598 apply_2qbit_kernel_to_matrix_input_from_right_AVX_TBB_impl(unitary, input, std::move(involved_qbits), matrix_size);
5602 if (input.
cols != 1 && involved_qbits.size() >= 3 && involved_qbits.size() <= 5) {
5603 apply_nqbit_kernel_to_matrix_input_from_right_AVX_TBB_impl(unitary, input, std::move(involved_qbits), matrix_size);
5611 if (input.
cols != 1 && involved_qbits.size() == 2 && unitary.
rows == 4 && unitary.
cols == 4) {
5612 apply_2qbit_kernel_to_matrix_input_from_right_AVX32_impl(unitary, input, std::move(involved_qbits), matrix_size);
5616 if (input.
cols != 1 && involved_qbits.size() >= 3 && involved_qbits.size() <= 5) {
5617 apply_nqbit_kernel_to_matrix_input_from_right_AVX32_impl(unitary, input, std::move(involved_qbits), matrix_size);
5625 if (input.
cols != 1 && involved_qbits.size() == 2 && unitary.
rows == 4 && unitary.
cols == 4) {
5626 apply_2qbit_kernel_to_matrix_input_from_right_AVX_OpenMP32_impl(unitary, input, std::move(involved_qbits), matrix_size);
5630 if (input.
cols != 1 && involved_qbits.size() >= 3 && involved_qbits.size() <= 5) {
5631 apply_nqbit_kernel_to_matrix_input_from_right_AVX_OpenMP32_impl(unitary, input, std::move(involved_qbits), matrix_size);
5639 if (input.
cols != 1 && involved_qbits.size() == 2 && unitary.
rows == 4 && unitary.
cols == 4) {
5640 apply_2qbit_kernel_to_matrix_input_from_right_AVX_TBB32_impl(unitary, input, std::move(involved_qbits), matrix_size);
5644 if (input.
cols != 1 && involved_qbits.size() >= 3 && involved_qbits.size() <= 5) {
5645 apply_nqbit_kernel_to_matrix_input_from_right_AVX_TBB32_impl(unitary, input, std::move(involved_qbits), matrix_size);
5653 __m256* mv_xy = (__m256*) _mm_malloc(
sizeof(__m256) * matrix_size *
matrix_size, 32);
5657 mv_xy[rdx * matrix_size + cdx] = _mm256_set_ps(
5658 -gate_kernel_unitary[matrix_size * rdx + cdx + 3].imag,
5659 gate_kernel_unitary[matrix_size * rdx + cdx + 3].
real,
5660 -gate_kernel_unitary[matrix_size * rdx + cdx + 2].imag,
5661 gate_kernel_unitary[matrix_size * rdx + cdx + 2].
real,
5662 -gate_kernel_unitary[matrix_size * rdx + cdx + 1].imag,
5663 gate_kernel_unitary[matrix_size * rdx + cdx + 1].
real,
5664 -gate_kernel_unitary[matrix_size * rdx + cdx].imag,
5665 gate_kernel_unitary[matrix_size * rdx + cdx].
real 5668 mv_xy[rdx * matrix_size + cdx + 1] = _mm256_set_ps(
5669 gate_kernel_unitary[matrix_size * rdx + cdx + 3].real,
5670 gate_kernel_unitary[matrix_size * rdx + cdx + 3].imag,
5671 gate_kernel_unitary[matrix_size * rdx + cdx + 2].real,
5672 gate_kernel_unitary[matrix_size * rdx + cdx + 2].imag,
5673 gate_kernel_unitary[matrix_size * rdx + cdx + 1].real,
5674 gate_kernel_unitary[matrix_size * rdx + cdx + 1].imag,
5675 gate_kernel_unitary[matrix_size * rdx + cdx].real,
5676 gate_kernel_unitary[matrix_size * rdx + cdx].imag
5686 constexpr
int block_size = 1 <<
n;
5688 for (
int rdx = 0; rdx < block_size; ++rdx) {
5689 for (
int cdx = 0; cdx < block_size; cdx += 4) {
5690 mv_xy[rdx * block_size + cdx] = _mm256_set_ps(
5691 -gate_kernel_unitary[block_size * rdx + cdx + 3].imag,
5692 gate_kernel_unitary[block_size * rdx + cdx + 3].
real,
5693 -gate_kernel_unitary[block_size * rdx + cdx + 2].imag,
5694 gate_kernel_unitary[block_size * rdx + cdx + 2].
real,
5695 -gate_kernel_unitary[block_size * rdx + cdx + 1].imag,
5696 gate_kernel_unitary[block_size * rdx + cdx + 1].
real,
5697 -gate_kernel_unitary[block_size * rdx + cdx].imag,
5698 gate_kernel_unitary[block_size * rdx + cdx].
real 5701 mv_xy[rdx * block_size + cdx + 1] = _mm256_set_ps(
5702 gate_kernel_unitary[block_size * rdx + cdx + 3].real,
5703 gate_kernel_unitary[block_size * rdx + cdx + 3].imag,
5704 gate_kernel_unitary[block_size * rdx + cdx + 2].real,
5705 gate_kernel_unitary[block_size * rdx + cdx + 2].imag,
5706 gate_kernel_unitary[block_size * rdx + cdx + 1].real,
5707 gate_kernel_unitary[block_size * rdx + cdx + 1].imag,
5708 gate_kernel_unitary[block_size * rdx + cdx].real,
5709 gate_kernel_unitary[block_size * rdx + cdx].imag
5717 constexpr
int block_size = 1 <<
n;
5718 const float* data_ptr = (
const float*)input.
get_data();
5719 const int stride = input.
stride;
5721 const int idx0 = indices[cdx] * stride + col;
5722 const int idx1 = indices[cdx + 1] * stride + col;
5723 const int idx2 = indices[cdx + 2] * stride + col;
5724 const int idx3 = indices[cdx + 3] * stride + col;
5726 const __m256
data = _mm256_set_ps(
5727 data_ptr[2 * idx3 + 1],
5728 data_ptr[2 * idx3 + 0],
5729 data_ptr[2 * idx2 + 1],
5730 data_ptr[2 * idx2 + 0],
5731 data_ptr[2 * idx1 + 1],
5732 data_ptr[2 * idx1 + 0],
5733 data_ptr[2 * idx0 + 1],
5734 data_ptr[2 * idx0 + 0]
5737 const __m256 mv_x0 = mv_xy[block_size * rdx + cdx];
5738 const __m256 mv_x1 = mv_xy[block_size * rdx + cdx + 1];
5739 result = _mm256_add_ps(result, _mm256_hadd_ps(_mm256_mul_ps(data, mv_x0), _mm256_mul_ps(data, mv_x1)));
5744 constexpr
int block_size = 1 <<
n;
5745 const int qubit_num = (
int) std::log2(matrix_size);
5746 const int num_blocks = matrix_size >>
n;
5747 std::sort(involved_qbits.begin(), involved_qbits.end());
5749 int non_targets[64];
5750 int non_target_count = 0;
5751 for (
int q = 0; q < qubit_num; ++q) {
5752 bool is_target =
false;
5753 for (
int target : involved_qbits) {
5754 is_target = is_target || (q == target);
5757 non_targets[non_target_count++] = q;
5761 int block_pattern[block_size];
5762 for (
int k = 0;
k < block_size; ++
k) {
5764 for (
int bit = 0; bit <
n; ++bit) {
5765 if (
k & (1 << bit)) {
5766 idx |= (1 << involved_qbits[bit]);
5769 block_pattern[
k] = idx;
5772 int indices[block_size];
5774 __m256 mv_xy[block_size * block_size];
5775 construct_mv_xy_vectors32_fixed<n>(gate_kernel_unitary, mv_xy);
5777 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5779 for (
int i = 0; i < non_target_count; ++i) {
5780 if (iter_idx & (1ULL << i)) {
5781 base |= (1 << non_targets[i]);
5784 for (
int k = 0;
k < block_size; ++
k) {
5785 indices[
k] = base | block_pattern[
k];
5788 for (
int col = 0; col < input.
cols; ++col) {
5789 for (
int rdx = 0; rdx < block_size; ++rdx) {
5790 __m256
result = _mm256_setzero_ps();
5792 for (
int cdx = 0; cdx < block_size; cdx += 4) {
5793 complex_prod_AVX32_fixed<n>(mv_xy, rdx, cdx, indices, input, col,
result);
5796 alignas(32)
float acc[8];
5797 _mm256_store_ps(acc, result);
5798 out[rdx].
real = acc[0] + acc[1] + acc[4] + acc[5];
5799 out[rdx].
imag = acc[2] + acc[3] + acc[6] + acc[7];
5802 for (
int rdx = 0; rdx < block_size; ++rdx) {
5803 input[indices[rdx] * input.
stride + col] = out[rdx];
5811 constexpr
int block_size = 1 <<
n;
5812 const int qubit_num = (
int) std::log2(matrix_size);
5813 const int num_blocks = matrix_size >>
n;
5814 std::sort(involved_qbits.begin(), involved_qbits.end());
5816 int non_targets[64];
5817 int non_target_count = 0;
5818 for (
int q = 0; q < qubit_num; ++q) {
5819 bool is_target =
false;
5820 for (
int target : involved_qbits) {
5821 is_target = is_target || (q == target);
5824 non_targets[non_target_count++] = q;
5828 int block_pattern[block_size];
5829 for (
int k = 0;
k < block_size; ++
k) {
5831 for (
int bit = 0; bit <
n; ++bit) {
5832 if (
k & (1 << bit)) {
5833 idx |= (1 << involved_qbits[bit]);
5836 block_pattern[
k] = idx;
5839 __m256 mv_xy[block_size * block_size];
5840 construct_mv_xy_vectors32_fixed<n>(gate_kernel_unitary, mv_xy);
5842 #pragma omp parallel for schedule(static) 5843 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
5844 int indices[block_size];
5848 for (
int i = 0; i < non_target_count; ++i) {
5849 if (iter_idx & (1ULL << i)) {
5850 base |= (1 << non_targets[i]);
5853 for (
int k = 0;
k < block_size; ++
k) {
5854 indices[
k] = base | block_pattern[
k];
5857 for (
int col = 0; col < input.
cols; ++col) {
5858 for (
int rdx = 0; rdx < block_size; ++rdx) {
5859 __m256
result = _mm256_setzero_ps();
5861 for (
int cdx = 0; cdx < block_size; cdx += 4) {
5862 complex_prod_AVX32_fixed<n>(mv_xy, rdx, cdx, indices, input, col,
result);
5865 alignas(32)
float acc[8];
5866 _mm256_store_ps(acc, result);
5867 out[rdx].
real = acc[0] + acc[1] + acc[4] + acc[5];
5868 out[rdx].
imag = acc[2] + acc[3] + acc[6] + acc[7];
5871 for (
int rdx = 0; rdx < block_size; ++rdx) {
5872 input[indices[rdx] * input.
stride + col] = out[rdx];
5880 constexpr
int block_size = 1 <<
n;
5881 const int qubit_num = (
int) std::log2(matrix_size);
5882 const int num_blocks = matrix_size >>
n;
5883 std::sort(involved_qbits.begin(), involved_qbits.end());
5885 int non_targets[64];
5886 int non_target_count = 0;
5887 for (
int q = 0; q < qubit_num; ++q) {
5888 bool is_target =
false;
5889 for (
int target : involved_qbits) {
5890 is_target = is_target || (q == target);
5893 non_targets[non_target_count++] = q;
5897 int block_pattern[block_size];
5898 for (
int k = 0;
k < block_size; ++
k) {
5900 for (
int bit = 0; bit <
n; ++bit) {
5901 if (
k & (1 << bit)) {
5902 idx |= (1 << involved_qbits[bit]);
5905 block_pattern[
k] = idx;
5908 __m256 mv_xy[block_size * block_size];
5909 construct_mv_xy_vectors32_fixed<n>(gate_kernel_unitary, mv_xy);
5911 tbb::parallel_for(tbb::blocked_range<int>(0, num_blocks, 16), [&](
const tbb::blocked_range<int>& range) {
5912 for (
int iter_idx = range.begin(); iter_idx < range.end(); ++iter_idx) {
5913 std::array<int, 1 << n> indices;
5914 std::array<QGD_Complex8, 1 << n> out;
5917 for (
int i = 0; i < non_target_count; ++i) {
5918 if (iter_idx & (1ULL << i)) {
5919 base |= (1 << non_targets[i]);
5922 for (
int k = 0;
k < block_size; ++
k) {
5923 indices[
k] = base | block_pattern[
k];
5926 for (
int col = 0; col < input.
cols; ++col) {
5927 for (
int rdx = 0; rdx < block_size; ++rdx) {
5928 __m256
result = _mm256_setzero_ps();
5930 for (
int cdx = 0; cdx < block_size; cdx += 4) {
5931 complex_prod_AVX32_fixed<n>(mv_xy, rdx, cdx, indices.data(), input, col,
result);
5934 alignas(32)
float acc[8];
5935 _mm256_store_ps(acc, result);
5936 out[rdx].real = acc[0] + acc[1] + acc[4] + acc[5];
5937 out[rdx].imag = acc[2] + acc[3] + acc[6] + acc[7];
5940 for (
int rdx = 0; rdx < block_size; ++rdx) {
5941 input[indices[rdx] * input.
stride + col] = out[rdx];
5949 apply_fixed_qbit_unitary_AVX32<2>(two_qbit_unitary, input, std::move(involved_qbits),
matrix_size);
5953 apply_fixed_qbit_unitary_AVX_OpenMP32<2>(two_qbit_unitary, input, std::move(involved_qbits),
matrix_size);
5957 apply_fixed_qbit_unitary_AVX_TBB32<2>(two_qbit_unitary, input, std::move(involved_qbits),
matrix_size);
5961 apply_fixed_qbit_unitary_AVX_OpenMP32<2>(two_qbit_unitary, input, std::move(involved_qbits),
matrix_size);
5965 apply_fixed_qbit_unitary_AVX32<3>(
unitary, input, std::move(involved_qbits),
matrix_size);
5969 apply_fixed_qbit_unitary_AVX_OpenMP32<3>(
unitary, input, std::move(involved_qbits),
matrix_size);
5973 apply_fixed_qbit_unitary_AVX_TBB32<3>(
unitary, input, std::move(involved_qbits),
matrix_size);
5977 apply_fixed_qbit_unitary_AVX32<4>(
unitary, input, std::move(involved_qbits),
matrix_size);
5981 apply_fixed_qbit_unitary_AVX_OpenMP32<4>(
unitary, input, std::move(involved_qbits),
matrix_size);
5985 apply_fixed_qbit_unitary_AVX_TBB32<4>(
unitary, input, std::move(involved_qbits),
matrix_size);
5989 apply_fixed_qbit_unitary_AVX32<5>(
unitary, input, std::move(involved_qbits),
matrix_size);
5993 apply_fixed_qbit_unitary_AVX_OpenMP32<5>(
unitary, input, std::move(involved_qbits),
matrix_size);
5997 apply_fixed_qbit_unitary_AVX_TBB32<5>(
unitary, input, std::move(involved_qbits),
matrix_size);
6002 const int parallel_outer_cycles = matrix_size / (index_step_target << 1);
6004 int outer_grain_size;
6005 if (index_step_target <= 2) {
6006 outer_grain_size = 32;
6008 else if (index_step_target <= 4) {
6009 outer_grain_size = 16;
6011 else if (index_step_target <= 8) {
6012 outer_grain_size = 8;
6015 outer_grain_size = 1;
6018 tbb::parallel_for(tbb::blocked_range<int>(0, parallel_outer_cycles, outer_grain_size), [&](
const tbb::blocked_range<int>& range) {
6019 for (
int outer_idx = range.begin(); outer_idx < range.end(); ++outer_idx) {
6020 const int current_idx = outer_idx * (index_step_target << 1);
6021 const int current_idx_pair = current_idx + index_step_target;
6023 for (
int idx = 0; idx < index_step_target; ++idx) {
6024 const int current_idx_loc = current_idx + idx;
6025 const int current_idx_pair_loc = current_idx_pair + idx;
6026 const int row_offset = current_idx_loc * input.
stride;
6027 const int row_offset_pair = current_idx_pair_loc * input.
stride;
6030 for (
int col = 0; col < input.
cols; ++col) {
6031 const int index = row_offset + col;
6032 const int index_pair = row_offset_pair + col;
6037 float real = std::fma(gate[0].real, a.
real, -gate[0].imag * a.
imag);
6038 real = std::fma(gate[1].real, b.
real, real);
6039 real = std::fma(-gate[1].imag, b.
imag, real);
6040 float imag = std::fma(gate[0].real, a.
imag, gate[0].imag * a.
real);
6041 imag = std::fma(gate[1].real, b.
imag, imag);
6042 imag = std::fma(gate[1].imag, b.
real, imag);
6043 input[index].real =
real;
6044 input[index].imag = imag;
6046 real = std::fma(gate[2].real, a.
real, -gate[2].imag * a.
imag);
6047 real = std::fma(gate[3].real, b.
real, real);
6048 real = std::fma(-gate[3].imag, b.
imag, real);
6049 imag = std::fma(gate[2].real, a.
imag, gate[2].imag * a.
real);
6050 imag = std::fma(gate[3].real, b.
imag, imag);
6051 imag = std::fma(gate[3].imag, b.
real, imag);
6052 input[index_pair].real =
real;
6053 input[index_pair].imag = imag;
6063 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
6064 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
6065 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
6068 tbb::parallel_for(tbb::blocked_range<int>(0, input.
rows, 16), [&](
const tbb::blocked_range<int>& range) {
6069 for (int row_idx = range.begin(); row_idx < range.end(); ++row_idx) {
6070 const int row_offset = row_idx * input.stride;
6071 int current_idx = 0;
6072 int current_idx_pair = index_step_target;
6074 while (current_idx_pair < input.cols) {
6075 const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
6078 Matrix_float& gate = ((control_qbit < 0) || ((current_idx >> control_qbit) & 1)) ? u3_1qbit1 : u3_1qbit2;
6079 const __m256 u00r = _mm256_set1_ps(gate[0].real);
6080 const __m256 u00i = _mm256_set1_ps(gate[0].imag);
6081 const __m256 u01r = _mm256_set1_ps(gate[1].real);
6082 const __m256 u01i = _mm256_set1_ps(gate[1].imag);
6083 const __m256 u10r = _mm256_set1_ps(gate[2].real);
6084 const __m256 u10i = _mm256_set1_ps(gate[2].imag);
6085 const __m256 u11r = _mm256_set1_ps(gate[3].real);
6086 const __m256 u11i = _mm256_set1_ps(gate[3].imag);
6087 float* element = (float*)input.get_data() + 2 * (row_offset + current_idx);
6088 float* element_pair = (float*)input.get_data() + 2 * (row_offset + current_idx_pair);
6090 const int avx_limit = 2 * index_step_target - 8;
6092 for (; col_idx <= avx_limit; col_idx += 8) {
6093 const __m256 e = _mm256_loadu_ps(element + col_idx);
6094 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
6095 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
6096 const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
6097 _mm256_storeu_ps(element + col_idx, out0);
6098 _mm256_storeu_ps(element_pair + col_idx, out1);
6101 for (int c = col_idx / 2; c < index_step_target; ++c) {
6102 const int index = row_offset + current_idx + c;
6103 const int index_pair = row_offset + current_idx_pair + c;
6104 QGD_Complex8 a = input[index];
6105 QGD_Complex8 b = input[index_pair];
6106 float real = std::fma(gate[0].real, a.real, -gate[0].imag * a.imag);
6107 real = std::fma(gate[2].real, b.real, real);
6108 real = std::fma(-gate[2].imag, b.imag, real);
6109 float imag = std::fma(gate[0].real, a.imag, gate[0].imag * a.real);
6110 imag = std::fma(gate[2].real, b.imag, imag);
6111 imag = std::fma(gate[2].imag, b.real, imag);
6112 input[index].real = real;
6113 input[index].imag = imag;
6114 real = std::fma(gate[1].real, a.real, -gate[1].imag * a.imag);
6115 real = std::fma(gate[3].real, b.real, real);
6116 real = std::fma(-gate[3].imag, b.imag, real);
6117 imag = std::fma(gate[1].real, a.imag, gate[1].imag * a.real);
6118 imag = std::fma(gate[3].real, b.imag, imag);
6119 imag = std::fma(gate[3].imag, b.real, imag);
6120 input[index_pair].real = real;
6121 input[index_pair].imag = imag;
6124 for (int idx = 0; idx < index_step_target; ++idx) {
6125 const int current_idx_loc = current_idx + idx;
6126 const int current_idx_pair_loc = current_idx_pair + idx;
6127 Matrix_float& gate = ((current_idx_loc >> control_qbit) & 1) ? u3_1qbit1 : u3_1qbit2;
6128 const int index = row_offset + current_idx_loc;
6129 const int index_pair = row_offset + current_idx_pair_loc;
6130 QGD_Complex8 a = input[index];
6131 QGD_Complex8 b = input[index_pair];
6132 float real = std::fma(gate[0].real, a.real, -gate[0].imag * a.imag);
6133 real = std::fma(gate[2].real, b.real, real);
6134 real = std::fma(-gate[2].imag, b.imag, real);
6135 float imag = std::fma(gate[0].real, a.imag, gate[0].imag * a.real);
6136 imag = std::fma(gate[2].real, b.imag, imag);
6137 imag = std::fma(gate[2].imag, b.real, imag);
6138 input[index].real = real;
6139 input[index].imag = imag;
6140 real = std::fma(gate[1].real, a.real, -gate[1].imag * a.imag);
6141 real = std::fma(gate[3].real, b.real, real);
6142 real = std::fma(-gate[3].imag, b.imag, real);
6143 imag = std::fma(gate[1].real, a.imag, gate[1].imag * a.real);
6144 imag = std::fma(gate[3].real, b.imag, imag);
6145 imag = std::fma(gate[3].imag, b.real, imag);
6146 input[index_pair].real = real;
6147 input[index_pair].imag = imag;
6151 current_idx += (index_step_target << 1);
6152 current_idx_pair += (index_step_target << 1);
6162 int current_idx = 0;
6164 for (
int current_idx_pair = current_idx + index_step_target; current_idx_pair <
matrix_size; current_idx_pair += (index_step_target << 1)) {
6165 for (
int idx = 0; idx < index_step_target; ++idx) {
6166 const int current_idx_loc = current_idx + idx;
6167 const int current_idx_pair_loc = current_idx_pair + idx;
6168 const int row_offset = current_idx_loc * input.
stride;
6169 const int row_offset_pair = current_idx_pair_loc * input.
stride;
6172 for (
int col = 0; col < input.
cols; ++col) {
6173 const int index = row_offset + col;
6174 const int index_pair = row_offset_pair + col;
6179 float real = std::fma(gate[0].real, a.
real, -gate[0].imag * a.
imag);
6180 real = std::fma(gate[1].real, b.
real, real);
6181 real = std::fma(-gate[1].imag, b.
imag, real);
6182 float imag = std::fma(gate[0].real, a.
imag, gate[0].imag * a.
real);
6183 imag = std::fma(gate[1].real, b.
imag, imag);
6184 imag = std::fma(gate[1].imag, b.
real, imag);
6185 input[index].real =
real;
6186 input[index].imag = imag;
6188 real = std::fma(gate[2].real, a.
real, -gate[2].imag * a.
imag);
6189 real = std::fma(gate[3].real, b.
real, real);
6190 real = std::fma(-gate[3].imag, b.
imag, real);
6191 imag = std::fma(gate[2].real, a.
imag, gate[2].imag * a.
real);
6192 imag = std::fma(gate[3].real, b.
imag, imag);
6193 imag = std::fma(gate[3].imag, b.
real, imag);
6194 input[index_pair].real =
real;
6195 input[index_pair].imag = imag;
6199 current_idx += (index_step_target << 1);
6206 auto cmul_ps = [](__m256 ar, __m256 ai, __m256
x) {
6207 const __m256 swapped = _mm256_permute_ps(
x, 0xB1);
6208 return _mm256_fmaddsub_ps(ar,
x, _mm256_mul_ps(ai, swapped));
6212 for (; row_idx < input.
rows; ++row_idx) {
6213 const int row_offset = row_idx * input.
stride;
6214 int current_idx = 0;
6215 int current_idx_pair = index_step_target;
6217 while (current_idx_pair < input.
cols) {
6218 const bool mixed = (control_qbit >= 0 && control_qbit <
target_qbit);
6222 const __m256 u00r = _mm256_set1_ps(gate[0].
real);
6223 const __m256 u00i = _mm256_set1_ps(gate[0].imag);
6224 const __m256 u01r = _mm256_set1_ps(gate[1].real);
6225 const __m256 u01i = _mm256_set1_ps(gate[1].imag);
6226 const __m256 u10r = _mm256_set1_ps(gate[2].real);
6227 const __m256 u10i = _mm256_set1_ps(gate[2].imag);
6228 const __m256 u11r = _mm256_set1_ps(gate[3].real);
6229 const __m256 u11i = _mm256_set1_ps(gate[3].imag);
6230 float* element = (
float*)input.
get_data() + 2 * (row_offset + current_idx);
6231 float* element_pair = (
float*)input.
get_data() + 2 * (row_offset + current_idx_pair);
6233 const int avx_limit = 2 * index_step_target - 8;
6235 for (; col_idx <= avx_limit; col_idx += 8) {
6236 const __m256 e = _mm256_loadu_ps(element + col_idx);
6237 const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
6238 const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
6239 const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
6240 _mm256_storeu_ps(element + col_idx, out0);
6241 _mm256_storeu_ps(element_pair + col_idx, out1);
6244 for (
int c = col_idx / 2; c < index_step_target; ++c) {
6245 const int index = row_offset + current_idx + c;
6246 const int index_pair = row_offset + current_idx_pair + c;
6249 float real = std::fma(gate[0].real, a.
real, -gate[0].imag * a.
imag);
6250 real = std::fma(gate[2].real, b.
real, real);
6251 real = std::fma(-gate[2].imag, b.
imag, real);
6252 float imag = std::fma(gate[0].real, a.
imag, gate[0].imag * a.
real);
6253 imag = std::fma(gate[2].real, b.
imag, imag);
6254 imag = std::fma(gate[2].imag, b.
real, imag);
6255 input[index].real =
real;
6256 input[index].imag = imag;
6257 real = std::fma(gate[1].real, a.
real, -gate[1].imag * a.
imag);
6258 real = std::fma(gate[3].real, b.
real, real);
6259 real = std::fma(-gate[3].imag, b.
imag, real);
6260 imag = std::fma(gate[1].real, a.
imag, gate[1].imag * a.
real);
6261 imag = std::fma(gate[3].real, b.
imag, imag);
6262 imag = std::fma(gate[3].imag, b.
real, imag);
6263 input[index_pair].real =
real;
6264 input[index_pair].imag = imag;
6267 for (
int idx = 0; idx < index_step_target; ++idx) {
6268 const int current_idx_loc = current_idx + idx;
6269 const int current_idx_pair_loc = current_idx_pair + idx;
6271 const int index = row_offset + current_idx_loc;
6272 const int index_pair = row_offset + current_idx_pair_loc;
6275 float real = std::fma(gate[0].real, a.
real, -gate[0].imag * a.
imag);
6276 real = std::fma(gate[2].real, b.
real, real);
6277 real = std::fma(-gate[2].imag, b.
imag, real);
6278 float imag = std::fma(gate[0].real, a.
imag, gate[0].imag * a.
real);
6279 imag = std::fma(gate[2].real, b.
imag, imag);
6280 imag = std::fma(gate[2].imag, b.
real, imag);
6281 input[index].real =
real;
6282 input[index].imag = imag;
6283 real = std::fma(gate[1].real, a.
real, -gate[1].imag * a.
imag);
6284 real = std::fma(gate[3].real, b.
real, real);
6285 real = std::fma(-gate[3].imag, b.
imag, real);
6286 imag = std::fma(gate[1].real, a.
imag, gate[1].imag * a.
real);
6287 imag = std::fma(gate[3].real, b.
imag, imag);
6288 imag = std::fma(gate[3].imag, b.
real, imag);
6289 input[index_pair].real =
real;
6290 input[index_pair].imag = imag;
6294 current_idx += (index_step_target << 1);
6295 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.
data
load the unitary from file
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.
Matrix copy() const
Call to create a copy of the matrix.
Double-precision complex matrix (float64).
Single-precision complex matrix (float32).
double real
the real part of a complex number
Matrix_float copy() const
Call to create a copy of the matrix.
double imag
the imaginary part of a complex number