Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
apply_kernel_to_input_AVX.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 @author: Peter Rakyta, Ph.D.
18 */
25 #include <immintrin.h>
26 #include "tbb/tbb.h"
27 
37 void
38 apply_kernel_to_input_AVX_small(Matrix& u3_1qbit, Matrix& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
39 
40  input.ensure_aligned();
41 
42  int index_step_target = 1 << target_qbit;
43  int current_idx = 0;
44 
45  // load elements of the U3 unitary into 256bit registers (4 registers)
46  __m128d* u3_1qubit_tmp = (__m128d*) & u3_1qbit[0];
47  __m256d u3_1qbit_00_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
48 
49  u3_1qubit_tmp = (__m128d*) & u3_1qbit[1];
50  __m256d u3_1qbit_01_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
51 
52  u3_1qubit_tmp = (__m128d*) & u3_1qbit[2];
53  __m256d u3_1qbit_10_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
54 
55  u3_1qubit_tmp = (__m128d*) & u3_1qbit[3];
56  __m256d u3_1qbit_11_vec = _mm256_broadcast_pd(u3_1qubit_tmp);
57 
58 
59  for ( int current_idx_pair=current_idx + index_step_target; current_idx_pair<matrix_size; current_idx_pair=current_idx_pair+(index_step_target << 1) ) {
60 
61  for (int idx = 0; idx < index_step_target; idx++) {
62  //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {
63 
64  int current_idx_loc = current_idx + idx;
65  int current_idx_pair_loc = current_idx_pair + idx;
66 
67  int row_offset = current_idx_loc * input.stride;
68  int row_offset_pair = current_idx_pair_loc * input.stride;
69 
70  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
71 
72 
73  double* element = (double*)input.get_data() + 2 * row_offset;
74  double* element_pair = (double*)input.get_data() + 2 * row_offset_pair;
75 
76 
77  __m256d neg = _mm256_setr_pd(1.0, -1.0, 1.0, -1.0); // 5th register
78 
79 
80  for (int col_idx = 0; col_idx < 2 * (input.cols - 1); col_idx = col_idx + 4) {
81 
82  // extract successive elements from arrays element, element_pair
83  __m256d element_vec = _mm256_loadu_pd(element + col_idx); // 6th register
84  __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx); // 7th register
85 
87 
88  // 1 calculate the multiplications u3_1qbit_00*element_vec
89  __m256d vec3 = _mm256_mul_pd(u3_1qbit_00_vec, element_vec); // 8th register
90 
91  // 2 Switch the real and imaginary elements of element_vec
92  __m256d element_vec_permuted = _mm256_permute_pd(element_vec, 0x5); // 9th register
93 
94  // 3 Negate the imaginary elements of element_vec_permuted
95  element_vec_permuted = _mm256_mul_pd(element_vec_permuted, neg);
96 
97  // 4 Multiply elements of u3_1qbit_00*element_vec_permuted
98  __m256d vec4 = _mm256_mul_pd(u3_1qbit_00_vec, element_vec_permuted);
99 
100  // 5 Horizontally subtract the elements in vec3 and vec4
101  vec3 = _mm256_hsub_pd(vec3, vec4);
102 
103 
105 
106  // 1 calculate the multiplications u3_1qbit_01*element_pair_vec
107  __m256d vec5 = _mm256_mul_pd(u3_1qbit_01_vec, element_pair_vec); // 10th register
108 
109  // 2 Switch the real and imaginary elements of element_vec
110  __m256d element_pair_vec_permuted = _mm256_permute_pd(element_pair_vec, 0x5); // 11th register
111 
112  // 3 Negate the imaginary elements of element_vec_permuted
113  element_pair_vec_permuted = _mm256_mul_pd(element_pair_vec_permuted, neg);
114 
115  // 4 Multiply elements of u3_1qbit_01*element_vec_pair_permuted
116  vec4 = _mm256_mul_pd(u3_1qbit_01_vec, element_pair_vec_permuted);
117 
118  // 5 Horizontally subtract the elements in vec5 and vec4
119  vec5 = _mm256_hsub_pd(vec5, vec4);
120 
122  vec3 = _mm256_add_pd(vec3, vec5);
123 
124 
125  // 6 store the transformed elements in vec3
126  _mm256_storeu_pd(element + col_idx, vec3);
127 
128 
130 
131  // 1 calculate the multiplications u3_1qbit_10*element_vec
132  vec3 = _mm256_mul_pd(u3_1qbit_10_vec, element_vec);
133 
134  // 4 Multiply elements of u3_1qbit_10*element_vec_permuted
135  vec4 = _mm256_mul_pd(u3_1qbit_10_vec, element_vec_permuted);
136 
137  // 5 Horizontally subtract the elements in vec3 and vec4
138  vec3 = _mm256_hsub_pd(vec3, vec4);
139 
140 
142 
143  // 1 calculate the multiplications u3_1qbit_01*element_pair_vec
144  vec5 = _mm256_mul_pd(u3_1qbit_11_vec, element_pair_vec);
145 
146  // 4 Multiply elements of u3_1qbit_01*element_vec_pair_permuted
147  vec4 = _mm256_mul_pd(u3_1qbit_11_vec, element_pair_vec_permuted);
148 
149  // 5 Horizontally subtract the elements in vec5 and vec4
150  vec5 = _mm256_hsub_pd(vec5, vec4);
151 
153  vec3 = _mm256_add_pd(vec3, vec5);
154 
155  // 6 store the transformed elements in vec3
156  _mm256_storeu_pd(element_pair + col_idx, vec3);
157 
158  }
159 
160  if (input.cols % 2 == 1) {
161 
162  int col_idx = input.cols - 1;
163 
164  int index = row_offset + col_idx;
165  int index_pair = row_offset_pair + col_idx;
166 
167  QGD_Complex16 element = input[index];
168  QGD_Complex16 element_pair = input[index_pair];
169 
170  QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
171  QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
172 
173  input[index].real = tmp1.real + tmp2.real;
174  input[index].imag = tmp1.imag + tmp2.imag;
175 
176  tmp1 = mult(u3_1qbit[2], element);
177  tmp2 = mult(u3_1qbit[3], element_pair);
178 
179  input[index_pair].real = tmp1.real + tmp2.real;
180  input[index_pair].imag = tmp1.imag + tmp2.imag;
181 
182 
183  }
184 
185  }
186  else if (deriv) {
187  // when calculating derivatives, the constant element should be zeros
188  memset(input.get_data() + row_offset, 0, input.cols * sizeof(QGD_Complex16));
189  memset(input.get_data() + row_offset_pair, 0, input.cols * sizeof(QGD_Complex16));
190  }
191  else {
192  // leave the state as it is
193  continue;
194  }
195 
196 
197  //std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
198 
199 
200  //});
201  }
202 
203 
204  current_idx = current_idx + (index_step_target << 1);
205 
206 
207  }
208 
209 
210 
211 
212 }
213 
214 
215 void
216 apply_kernel_to_input_AVX_small32(Matrix_float& u3_1qbit, Matrix_float& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
217  input.ensure_aligned();
218 
219  auto cmul_ps = [](__m256 ar, __m256 ai, __m256 x) {
220  const __m256 swapped = _mm256_permute_ps(x, 0xB1);
221  return _mm256_fmaddsub_ps(ar, x, _mm256_mul_ps(ai, swapped));
222  };
223 
224  const int index_step_target = 1 << target_qbit;
225  int current_idx = 0;
226 
227  const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].real);
228  const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
229  const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
230  const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
231  const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
232  const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
233  const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
234  const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
235 
236  for (int current_idx_pair = current_idx + index_step_target; current_idx_pair < matrix_size; current_idx_pair += (index_step_target << 1)) {
237  for (int idx = 0; idx < index_step_target; idx++) {
238  const int current_idx_loc = current_idx + idx;
239  const int current_idx_pair_loc = current_idx_pair + idx;
240  const int row_offset = current_idx_loc * input.stride;
241  const int row_offset_pair = current_idx_pair_loc * input.stride;
242 
243  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
244  float* element = (float*)input.get_data() + 2 * row_offset;
245  float* element_pair = (float*)input.get_data() + 2 * row_offset_pair;
246 
247  int col_idx = 0;
248  const int limit = 2 * input.cols - 8;
249  for (; col_idx <= limit; col_idx += 8) {
250  const __m256 e = _mm256_loadu_ps(element + col_idx);
251  const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
252 
253  const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u01r, u01i, p));
254  const __m256 out1 = _mm256_add_ps(cmul_ps(u10r, u10i, e), cmul_ps(u11r, u11i, p));
255 
256  _mm256_storeu_ps(element + col_idx, out0);
257  _mm256_storeu_ps(element_pair + col_idx, out1);
258  }
259 
260  for (int c = col_idx / 2; c < input.cols; ++c) {
261  const int index = row_offset + c;
262  const int index_pair = row_offset_pair + c;
263 
264  QGD_Complex8 element_c = input[index];
265  QGD_Complex8 element_pair_c = input[index_pair];
266 
267  QGD_Complex8 tmp1 = mult(u3_1qbit[0], element_c);
268  QGD_Complex8 tmp2 = mult(u3_1qbit[1], element_pair_c);
269  input[index].real = tmp1.real + tmp2.real;
270  input[index].imag = tmp1.imag + tmp2.imag;
271 
272  tmp1 = mult(u3_1qbit[2], element_c);
273  tmp2 = mult(u3_1qbit[3], element_pair_c);
274  input[index_pair].real = tmp1.real + tmp2.real;
275  input[index_pair].imag = tmp1.imag + tmp2.imag;
276  }
277  } else if (deriv) {
278  memset(input.get_data() + row_offset, 0, input.cols * sizeof(QGD_Complex8));
279  memset(input.get_data() + row_offset_pair, 0, input.cols * sizeof(QGD_Complex8));
280  }
281  }
282  current_idx += (index_step_target << 1);
283  }
284 }
285 
286 
287 void
288 apply_kernel_from_right_AVX_small(Matrix& u3_1qbit, Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
289  input.ensure_aligned();
290 
291  const int index_step_target = 1 << target_qbit;
292 
293  const __m256d u00r = _mm256_broadcast_sd(&u3_1qbit[0].real);
294  const __m256d u00i = _mm256_broadcast_sd(&u3_1qbit[0].imag);
295  const __m256d u01r = _mm256_broadcast_sd(&u3_1qbit[1].real);
296  const __m256d u01i = _mm256_broadcast_sd(&u3_1qbit[1].imag);
297  const __m256d u10r = _mm256_broadcast_sd(&u3_1qbit[2].real);
298  const __m256d u10i = _mm256_broadcast_sd(&u3_1qbit[2].imag);
299  const __m256d u11r = _mm256_broadcast_sd(&u3_1qbit[3].real);
300  const __m256d u11i = _mm256_broadcast_sd(&u3_1qbit[3].imag);
301 
302  auto apply_pair_scalar = [&](const int index, const int index_pair) {
303  QGD_Complex16 element = input[index];
304  QGD_Complex16 element_pair = input[index_pair];
305 
306  QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
307  QGD_Complex16 tmp2 = mult(u3_1qbit[2], element_pair);
308  input[index].real = tmp1.real + tmp2.real;
309  input[index].imag = tmp1.imag + tmp2.imag;
310 
311  tmp1 = mult(u3_1qbit[1], element);
312  tmp2 = mult(u3_1qbit[3], element_pair);
313  input[index_pair].real = tmp1.real + tmp2.real;
314  input[index_pair].imag = tmp1.imag + tmp2.imag;
315  };
316 
317  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
318  const int row_offset = row_idx * input.stride;
319  double* const row_data = (double*)input.get_data() + 2 * row_offset;
320 
321  int current_idx = 0;
322  int current_idx_pair = index_step_target;
323 
324  while (current_idx_pair < input.cols) {
325  const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
326  const bool active = (control_qbit < 0)
327  || (control_qbit >= target_qbit && ((current_idx >> control_qbit) & 1));
328 
329  if (!mixed && active) {
330  double* element = row_data + 2 * current_idx;
331  double* element_pair = row_data + 2 * current_idx_pair;
332 
333  int col_idx = 0;
334  const int avx_limit = 2 * index_step_target - 8;
335 
336  for (; col_idx <= avx_limit; col_idx += 8) {
337  __m256d element_vec = _mm256_loadu_pd(element + col_idx);
338  __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
339  __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
340  element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
341  element_vec = tmp;
342 
343  __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
344  __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
345  tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
346  element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
347  element_pair_vec = tmp;
348 
349  __m256d vec3 = _mm256_mul_pd(u00r, element_vec);
350  vec3 = _mm256_fnmadd_pd(u00i, element_vec2, vec3);
351  __m256d vec4 = _mm256_mul_pd(u10r, element_pair_vec);
352  vec4 = _mm256_fnmadd_pd(u10i, element_pair_vec2, vec4);
353  vec3 = _mm256_add_pd(vec3, vec4);
354  __m256d vec5 = _mm256_mul_pd(u00r, element_vec2);
355  vec5 = _mm256_fmadd_pd(u00i, element_vec, vec5);
356  __m256d vec6 = _mm256_mul_pd(u10r, element_pair_vec2);
357  vec6 = _mm256_fmadd_pd(u10i, element_pair_vec, vec6);
358  vec5 = _mm256_add_pd(vec5, vec6);
359 
360  tmp = _mm256_shuffle_pd(vec3, vec5, 0);
361  vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
362  vec3 = tmp;
363  _mm256_storeu_pd(element + col_idx, vec3);
364  _mm256_storeu_pd(element + col_idx + 4, vec5);
365 
366  __m256d vec7 = _mm256_mul_pd(u01r, element_vec);
367  vec7 = _mm256_fnmadd_pd(u01i, element_vec2, vec7);
368  __m256d vec8 = _mm256_mul_pd(u11r, element_pair_vec);
369  vec8 = _mm256_fnmadd_pd(u11i, element_pair_vec2, vec8);
370  vec7 = _mm256_add_pd(vec7, vec8);
371  __m256d vec9 = _mm256_mul_pd(u01r, element_vec2);
372  vec9 = _mm256_fmadd_pd(u01i, element_vec, vec9);
373  __m256d vec10 = _mm256_mul_pd(u11r, element_pair_vec2);
374  vec10 = _mm256_fmadd_pd(u11i, element_pair_vec, vec10);
375  vec9 = _mm256_add_pd(vec9, vec10);
376 
377  tmp = _mm256_shuffle_pd(vec7, vec9, 0);
378  vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
379  vec7 = tmp;
380  _mm256_storeu_pd(element_pair + col_idx, vec7);
381  _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
382  }
383 
384  for (int c = col_idx / 2; c < index_step_target; ++c) {
385  const int index = row_offset + current_idx + c;
386  const int index_pair = row_offset + current_idx_pair + c;
387  apply_pair_scalar(index, index_pair);
388  }
389  }
390  else if (mixed) {
391  for (int idx = 0; idx < index_step_target; ++idx) {
392  const int col = current_idx + idx;
393  if ((col >> control_qbit) & 1) {
394  const int index = row_offset + col;
395  const int index_pair = row_offset + current_idx_pair + idx;
396  apply_pair_scalar(index, index_pair);
397  }
398  }
399  }
400 
401  current_idx += (index_step_target << 1);
402  current_idx_pair += (index_step_target << 1);
403  }
404  }
405 
406  (void)matrix_size;
407 }
408 
409 
410 void
412  input.ensure_aligned();
413 
414  auto cmul_ps = [](__m256 ar, __m256 ai, __m256 x) {
415  const __m256 swapped = _mm256_permute_ps(x, 0xB1);
416  return _mm256_fmaddsub_ps(ar, x, _mm256_mul_ps(ai, swapped));
417  };
418 
419  const int index_step_target = 1 << target_qbit;
420 
421  const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].real);
422  const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
423  const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
424  const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
425  const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
426  const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
427  const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
428  const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
429 
430  const float u00r_s = u3_1qbit[0].real;
431  const float u00i_s = u3_1qbit[0].imag;
432  const float u01r_s = u3_1qbit[1].real;
433  const float u01i_s = u3_1qbit[1].imag;
434  const float u10r_s = u3_1qbit[2].real;
435  const float u10i_s = u3_1qbit[2].imag;
436  const float u11r_s = u3_1qbit[3].real;
437  const float u11i_s = u3_1qbit[3].imag;
438 
439  auto apply_pair_scalar = [&](const int index, const int index_pair) {
440  const QGD_Complex8 element = input[index];
441  const QGD_Complex8 element_pair = input[index_pair];
442  input[index].real = u00r_s * element.real - u00i_s * element.imag + u10r_s * element_pair.real - u10i_s * element_pair.imag;
443  input[index].imag = u00r_s * element.imag + u00i_s * element.real + u10r_s * element_pair.imag + u10i_s * element_pair.real;
444  input[index_pair].real = u01r_s * element.real - u01i_s * element.imag + u11r_s * element_pair.real - u11i_s * element_pair.imag;
445  input[index_pair].imag = u01r_s * element.imag + u01i_s * element.real + u11r_s * element_pair.imag + u11i_s * element_pair.real;
446  };
447 
448  for (int row_idx = 0; row_idx < input.rows; ++row_idx) {
449  const int row_offset = row_idx * input.stride;
450  float* const row_data = (float*)input.get_data() + 2 * row_offset;
451 
452  int current_idx = 0;
453  int current_idx_pair = index_step_target;
454 
455  while (current_idx_pair < input.cols) {
456  const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
457  const bool active = (control_qbit < 0)
458  || (control_qbit >= target_qbit && ((current_idx >> control_qbit) & 1));
459 
460  if (!mixed && active) {
461  float* element = row_data + 2 * current_idx;
462  float* element_pair = row_data + 2 * current_idx_pair;
463 
464  int col_idx = 0;
465  const int avx_limit = 2 * index_step_target - 8;
466 
467  for (; col_idx <= avx_limit; col_idx += 8) {
468  const __m256 e = _mm256_loadu_ps(element + col_idx);
469  const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
470 
471  const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
472  const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
473 
474  _mm256_storeu_ps(element + col_idx, out0);
475  _mm256_storeu_ps(element_pair + col_idx, out1);
476  }
477 
478  for (int c = col_idx / 2; c < index_step_target; ++c) {
479  const int index = row_offset + current_idx + c;
480  const int index_pair = row_offset + current_idx_pair + c;
481  apply_pair_scalar(index, index_pair);
482  }
483  }
484  else if (mixed) {
485  for (int idx = 0; idx < index_step_target; ++idx) {
486  const int col = current_idx + idx;
487  if ((col >> control_qbit) & 1) {
488  const int index = row_offset + col;
489  const int index_pair = row_offset + current_idx_pair + idx;
490  apply_pair_scalar(index, index_pair);
491  }
492  }
493  }
494 
495  current_idx += (index_step_target << 1);
496  current_idx_pair += (index_step_target << 1);
497  }
498  }
499 
500  (void)matrix_size;
501 }
502 
503 
504 void
505 apply_kernel_to_input_AVX32(Matrix_float& u3_1qbit, Matrix_float& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
506  input.ensure_aligned();
507 
508  auto cmul_ps = [](__m256 ar, __m256 ai, __m256 x) {
509  const __m256 swapped = _mm256_permute_ps(x, 0xB1);
510  return _mm256_fmaddsub_ps(ar, x, _mm256_mul_ps(ai, swapped));
511  };
512 
513  const int index_step_target = 1 << target_qbit;
514  int current_idx = 0;
515 
516  const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].real);
517  const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
518  const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
519  const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
520  const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
521  const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
522  const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
523  const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
524 
525  for (int current_idx_pair = current_idx + index_step_target; current_idx_pair < matrix_size; current_idx_pair += (index_step_target << 1)) {
526  for (int idx = 0; idx < index_step_target; idx++) {
527  const int current_idx_loc = current_idx + idx;
528  const int current_idx_pair_loc = current_idx_pair + idx;
529  const int row_offset = current_idx_loc * input.stride;
530  const int row_offset_pair = current_idx_pair_loc * input.stride;
531 
532  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
533  float* element = (float*)input.get_data() + 2 * row_offset;
534  float* element_pair = (float*)input.get_data() + 2 * row_offset_pair;
535 
536  int col_idx = 0;
537  const int limit = 2 * input.cols - 8;
538  for (; col_idx <= limit; col_idx += 8) {
539  const __m256 e = _mm256_loadu_ps(element + col_idx);
540  const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
541 
542  const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u01r, u01i, p));
543  const __m256 out1 = _mm256_add_ps(cmul_ps(u10r, u10i, e), cmul_ps(u11r, u11i, p));
544 
545  _mm256_storeu_ps(element + col_idx, out0);
546  _mm256_storeu_ps(element_pair + col_idx, out1);
547  }
548 
549  for (int c = col_idx / 2; c < input.cols; ++c) {
550  const int index = row_offset + c;
551  const int index_pair = row_offset_pair + c;
552 
553  QGD_Complex8 element_c = input[index];
554  QGD_Complex8 element_pair_c = input[index_pair];
555 
556  QGD_Complex8 tmp1 = mult(u3_1qbit[0], element_c);
557  QGD_Complex8 tmp2 = mult(u3_1qbit[1], element_pair_c);
558  input[index].real = tmp1.real + tmp2.real;
559  input[index].imag = tmp1.imag + tmp2.imag;
560 
561  tmp1 = mult(u3_1qbit[2], element_c);
562  tmp2 = mult(u3_1qbit[3], element_pair_c);
563  input[index_pair].real = tmp1.real + tmp2.real;
564  input[index_pair].imag = tmp1.imag + tmp2.imag;
565  }
566  } else if (deriv) {
567  memset(input.get_data() + row_offset, 0, input.cols * sizeof(QGD_Complex8));
568  memset(input.get_data() + row_offset_pair, 0, input.cols * sizeof(QGD_Complex8));
569  }
570  }
571  current_idx += (index_step_target << 1);
572  }
573 }
574 
575 
576 void
577 apply_kernel_to_input_AVX_parallel32(Matrix_float& u3_1qbit, Matrix_float& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
578  input.ensure_aligned();
579 
580  auto cmul_ps = [](__m256 ar, __m256 ai, __m256 x) {
581  const __m256 swapped = _mm256_permute_ps(x, 0xB1);
582  return _mm256_fmaddsub_ps(ar, x, _mm256_mul_ps(ai, swapped));
583  };
584 
585  const int index_step_target = 1 << target_qbit;
586 
587  const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].real);
588  const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
589  const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
590  const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
591  const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
592  const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
593  const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
594  const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
595 
596  const int parallel_outer_cycles = matrix_size / (index_step_target << 1);
597  tbb::parallel_for(tbb::blocked_range<int>(0, parallel_outer_cycles, 8), [&](tbb::blocked_range<int> r) {
598  int current_idx = r.begin() * (index_step_target << 1);
599  int current_idx_pair = index_step_target + r.begin() * (index_step_target << 1);
600 
601  for (int rdx = r.begin(); rdx < r.end(); ++rdx) {
602  for (int idx = 0; idx < index_step_target; ++idx) {
603  const int current_idx_loc = current_idx + idx;
604  const int current_idx_pair_loc = current_idx_pair + idx;
605  const int row_offset = current_idx_loc * input.stride;
606  const int row_offset_pair = current_idx_pair_loc * input.stride;
607 
608  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
609  float* element = (float*)input.get_data() + 2 * row_offset;
610  float* element_pair = (float*)input.get_data() + 2 * row_offset_pair;
611 
612  int col_idx = 0;
613  const int limit = 2 * input.cols - 8;
614  for (; col_idx <= limit; col_idx += 8) {
615  const __m256 e = _mm256_loadu_ps(element + col_idx);
616  const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
617 
618  const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u01r, u01i, p));
619  const __m256 out1 = _mm256_add_ps(cmul_ps(u10r, u10i, e), cmul_ps(u11r, u11i, p));
620 
621  _mm256_storeu_ps(element + col_idx, out0);
622  _mm256_storeu_ps(element_pair + col_idx, out1);
623  }
624 
625  for (int c = col_idx / 2; c < input.cols; ++c) {
626  const int index = row_offset + c;
627  const int index_pair = row_offset_pair + c;
628 
629  QGD_Complex8 element_c = input[index];
630  QGD_Complex8 element_pair_c = input[index_pair];
631 
632  QGD_Complex8 tmp1 = mult(u3_1qbit[0], element_c);
633  QGD_Complex8 tmp2 = mult(u3_1qbit[1], element_pair_c);
634  input[index].real = tmp1.real + tmp2.real;
635  input[index].imag = tmp1.imag + tmp2.imag;
636 
637  tmp1 = mult(u3_1qbit[2], element_c);
638  tmp2 = mult(u3_1qbit[3], element_pair_c);
639  input[index_pair].real = tmp1.real + tmp2.real;
640  input[index_pair].imag = tmp1.imag + tmp2.imag;
641  }
642  } else if (deriv) {
643  memset(input.get_data() + row_offset, 0, input.cols * sizeof(QGD_Complex8));
644  memset(input.get_data() + row_offset_pair, 0, input.cols * sizeof(QGD_Complex8));
645  }
646  }
647 
648  current_idx += (index_step_target << 1);
649  current_idx_pair += (index_step_target << 1);
650  }
651  });
652 }
653 
654 
655 
665 void
666 apply_kernel_to_input_AVX(Matrix& u3_1qbit, Matrix& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
667 
668  input.ensure_aligned();
669 
670  int index_step_target = 1 << target_qbit;
671  int current_idx = 0;
672 
673  // load elements of the U3 unitary into 256bit registers (8 registers)
674  __m256d u3_1bit_00r_vec = _mm256_broadcast_sd(&u3_1qbit[0].real);
675  __m256d u3_1bit_00i_vec = _mm256_broadcast_sd(&u3_1qbit[0].imag);
676  __m256d u3_1bit_01r_vec = _mm256_broadcast_sd(&u3_1qbit[1].real);
677  __m256d u3_1bit_01i_vec = _mm256_broadcast_sd(&u3_1qbit[1].imag);
678  __m256d u3_1bit_10r_vec = _mm256_broadcast_sd(&u3_1qbit[2].real);
679  __m256d u3_1bit_10i_vec = _mm256_broadcast_sd(&u3_1qbit[2].imag);
680  __m256d u3_1bit_11r_vec = _mm256_broadcast_sd(&u3_1qbit[3].real);
681  __m256d u3_1bit_11i_vec = _mm256_broadcast_sd(&u3_1qbit[3].imag);
682 
683 
684  for ( int current_idx_pair=current_idx + index_step_target; current_idx_pair<matrix_size; current_idx_pair=current_idx_pair+(index_step_target << 1) ) {
685 
686 
687  for (int idx = 0; idx < index_step_target; idx++) {
688 
689 
690  int current_idx_loc = current_idx + idx;
691  int current_idx_pair_loc = current_idx_pair + idx;
692 
693  int row_offset = current_idx_loc * input.stride;
694  int row_offset_pair = current_idx_pair_loc * input.stride;
695 
696  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
697 
698 
699  double* element = (double*)input.get_data() + 2 * row_offset;
700  double* element_pair = (double*)input.get_data() + 2 * row_offset_pair;
701 
702 
703  for (int col_idx = 0; col_idx < 2 * (input.cols - 3); col_idx = col_idx + 8) {
704 
705  // extract successive elements from arrays element, element_pair
706  __m256d element_vec = _mm256_loadu_pd(element + col_idx);
707  __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
708  __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
709  element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
710  element_vec = tmp;
711 
712  __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
713  __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
714  tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
715  element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
716  element_pair_vec = tmp;
717 
718  __m256d vec3 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec);
719  vec3 = _mm256_fnmadd_pd(u3_1bit_00i_vec, element_vec2, vec3);
720  __m256d vec4 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec);
721  vec4 = _mm256_fnmadd_pd(u3_1bit_01i_vec, element_pair_vec2, vec4);
722  vec3 = _mm256_add_pd(vec3, vec4);
723  __m256d vec5 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec2);
724  vec5 = _mm256_fmadd_pd(u3_1bit_00i_vec, element_vec, vec5);
725  __m256d vec6 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec2);
726  vec6 = _mm256_fmadd_pd(u3_1bit_01i_vec, element_pair_vec, vec6);
727  vec5 = _mm256_add_pd(vec5, vec6);
728 
729  // 6 store the transformed elements in vec3
730  tmp = _mm256_shuffle_pd(vec3, vec5, 0);
731  vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
732  vec3 = tmp;
733  _mm256_storeu_pd(element + col_idx, vec3);
734  _mm256_storeu_pd(element + col_idx + 4, vec5);
735 
736  __m256d vec7 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec);
737  vec7 = _mm256_fnmadd_pd(u3_1bit_10i_vec, element_vec2, vec7);
738  __m256d vec8 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec);
739  vec8 = _mm256_fnmadd_pd(u3_1bit_11i_vec, element_pair_vec2, vec8);
740  vec7 = _mm256_add_pd(vec7, vec8);
741  __m256d vec9 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec2);
742  vec9 = _mm256_fmadd_pd(u3_1bit_10i_vec, element_vec, vec9);
743  __m256d vec10 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec2);
744  vec10 = _mm256_fmadd_pd(u3_1bit_11i_vec, element_pair_vec, vec10);
745  vec9 = _mm256_add_pd(vec9, vec10);
746 
747  // 6 store the transformed elements in vec3
748  tmp = _mm256_shuffle_pd(vec7, vec9, 0);
749  vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
750  vec7 = tmp;
751  _mm256_storeu_pd(element_pair + col_idx, vec7);
752  _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
753  }
754 
755  int remainder = input.cols % 4;
756  if (remainder != 0) {
757 
758  for (int col_idx = input.cols-remainder; col_idx < input.cols; col_idx++) {
759  int index = row_offset + col_idx;
760  int index_pair = row_offset_pair + col_idx;
761 
762  QGD_Complex16 element = input[index];
763  QGD_Complex16 element_pair = input[index_pair];
764 
765  QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
766  QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
767 
768  input[index].real = tmp1.real + tmp2.real;
769  input[index].imag = tmp1.imag + tmp2.imag;
770 
771  tmp1 = mult(u3_1qbit[2], element);
772  tmp2 = mult(u3_1qbit[3], element_pair);
773 
774  input[index_pair].real = tmp1.real + tmp2.real;
775  input[index_pair].imag = tmp1.imag + tmp2.imag;
776  }
777 
778  }
779 
780  }
781  else if (deriv) {
782  // when calculating derivatives, the constant element should be zeros
783  memset(input.get_data() + row_offset, 0, input.cols * sizeof(QGD_Complex16));
784  memset(input.get_data() + row_offset_pair, 0, input.cols * sizeof(QGD_Complex16));
785  }
786  else {
787  // leave the state as it is
788  continue;
789  }
790 
791 
792  //std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
793 
794 
795  }
796 
797 
798 
799  current_idx = current_idx + (index_step_target << 1);
800 
801  }
802 
803 
804 
805 }
806 
807 
817 void
818 apply_kernel_to_input_AVX_parallel(Matrix& u3_1qbit, Matrix& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
819 
820  input.ensure_aligned();
821 
822  int index_step_target = 1 << target_qbit;
823 
824  // load elements of the U3 unitary into 256bit registers (8 registers)
825  __m256d u3_1bit_00r_vec = _mm256_broadcast_sd(&u3_1qbit[0].real);
826  __m256d u3_1bit_00i_vec = _mm256_broadcast_sd(&u3_1qbit[0].imag);
827  __m256d u3_1bit_01r_vec = _mm256_broadcast_sd(&u3_1qbit[1].real);
828  __m256d u3_1bit_01i_vec = _mm256_broadcast_sd(&u3_1qbit[1].imag);
829  __m256d u3_1bit_10r_vec = _mm256_broadcast_sd(&u3_1qbit[2].real);
830  __m256d u3_1bit_10i_vec = _mm256_broadcast_sd(&u3_1qbit[2].imag);
831  __m256d u3_1bit_11r_vec = _mm256_broadcast_sd(&u3_1qbit[3].real);
832  __m256d u3_1bit_11i_vec = _mm256_broadcast_sd(&u3_1qbit[3].imag);
833 
834 
835 
836  int parallel_outer_cycles = matrix_size/(index_step_target << 1);
837  int outer_grain_size;
838  if ( index_step_target <= 2 ) {
839  outer_grain_size = 32;
840  }
841  else if ( index_step_target <= 4 ) {
842  outer_grain_size = 16;
843  }
844  else if ( index_step_target <= 8 ) {
845  outer_grain_size = 8;
846  }
847  else if ( index_step_target <= 16 ) {
848  outer_grain_size = 4;
849  }
850  else {
851  outer_grain_size = 2;
852  }
853 
854 
855  tbb::parallel_for( tbb::blocked_range<int>(0,parallel_outer_cycles,outer_grain_size), [&](tbb::blocked_range<int> r) {
856 
857  int current_idx = r.begin()*(index_step_target << 1);
858  int current_idx_pair = index_step_target + r.begin()*(index_step_target << 1);
859 
860  for (int rdx=r.begin(); rdx<r.end(); rdx++) {
861 
862 
863  tbb::parallel_for( tbb::blocked_range<int>(0,index_step_target,32), [&](tbb::blocked_range<int> r) {
864  for (int idx=r.begin(); idx<r.end(); ++idx) {
865 
866 
867  int current_idx_loc = current_idx + idx;
868  int current_idx_pair_loc = current_idx_pair + idx;
869 
870  int row_offset = current_idx_loc * input.stride;
871  int row_offset_pair = current_idx_pair_loc * input.stride;
872 
873  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
874 
875 
876  double* element = (double*)input.get_data() + 2 * row_offset;
877  double* element_pair = (double*)input.get_data() + 2 * row_offset_pair;
878 
879 
880  for (int col_idx = 0; col_idx < 2 * (input.cols - 3); col_idx = col_idx + 8) {
881 
882  // extract successive elements from arrays element, element_pair
883  __m256d element_vec = _mm256_loadu_pd(element + col_idx);
884  __m256d element_vec2 = _mm256_loadu_pd(element + col_idx + 4);
885  __m256d tmp = _mm256_shuffle_pd(element_vec, element_vec2, 0);
886  element_vec2 = _mm256_shuffle_pd(element_vec, element_vec2, 0xf);
887  element_vec = tmp;
888 
889  __m256d element_pair_vec = _mm256_loadu_pd(element_pair + col_idx);
890  __m256d element_pair_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
891  tmp = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0);
892  element_pair_vec2 = _mm256_shuffle_pd(element_pair_vec, element_pair_vec2, 0xf);
893  element_pair_vec = tmp;
894 
895  __m256d vec3 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec);
896  vec3 = _mm256_fnmadd_pd(u3_1bit_00i_vec, element_vec2, vec3);
897  __m256d vec4 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec);
898  vec4 = _mm256_fnmadd_pd(u3_1bit_01i_vec, element_pair_vec2, vec4);
899  vec3 = _mm256_add_pd(vec3, vec4);
900  __m256d vec5 = _mm256_mul_pd(u3_1bit_00r_vec, element_vec2);
901  vec5 = _mm256_fmadd_pd(u3_1bit_00i_vec, element_vec, vec5);
902  __m256d vec6 = _mm256_mul_pd(u3_1bit_01r_vec, element_pair_vec2);
903  vec6 = _mm256_fmadd_pd(u3_1bit_01i_vec, element_pair_vec, vec6);
904  vec5 = _mm256_add_pd(vec5, vec6);
905 
906  // 6 store the transformed elements in vec3
907  tmp = _mm256_shuffle_pd(vec3, vec5, 0);
908  vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
909  vec3 = tmp;
910  _mm256_storeu_pd(element + col_idx, vec3);
911  _mm256_storeu_pd(element + col_idx + 4, vec5);
912 
913  __m256d vec7 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec);
914  vec7 = _mm256_fnmadd_pd(u3_1bit_10i_vec, element_vec2, vec7);
915  __m256d vec8 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec);
916  vec8 = _mm256_fnmadd_pd(u3_1bit_11i_vec, element_pair_vec2, vec8);
917  vec7 = _mm256_add_pd(vec7, vec8);
918  __m256d vec9 = _mm256_mul_pd(u3_1bit_10r_vec, element_vec2);
919  vec9 = _mm256_fmadd_pd(u3_1bit_10i_vec, element_vec, vec9);
920  __m256d vec10 = _mm256_mul_pd(u3_1bit_11r_vec, element_pair_vec2);
921  vec10 = _mm256_fmadd_pd(u3_1bit_11i_vec, element_pair_vec, vec10);
922  vec9 = _mm256_add_pd(vec9, vec10);
923 
924  // 6 store the transformed elements in vec3
925  tmp = _mm256_shuffle_pd(vec7, vec9, 0);
926  vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
927  vec7 = tmp;
928  _mm256_storeu_pd(element_pair + col_idx, vec7);
929  _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
930  }
931 
932  int remainder = input.cols % 4;
933  if (remainder != 0) {
934 
935  for (int col_idx = input.cols-remainder; col_idx < input.cols; col_idx++) {
936  int index = row_offset + col_idx;
937  int index_pair = row_offset_pair + col_idx;
938 
939  QGD_Complex16 element = input[index];
940  QGD_Complex16 element_pair = input[index_pair];
941 
942  QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
943  QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
944 
945  input[index].real = tmp1.real + tmp2.real;
946  input[index].imag = tmp1.imag + tmp2.imag;
947 
948  tmp1 = mult(u3_1qbit[2], element);
949  tmp2 = mult(u3_1qbit[3], element_pair);
950 
951  input[index_pair].real = tmp1.real + tmp2.real;
952  input[index_pair].imag = tmp1.imag + tmp2.imag;
953  }
954 
955  }
956 
957  }
958  else if (deriv) {
959  // when calculating derivatives, the constant element should be zeros
960  memset(input.get_data() + row_offset, 0, input.cols * sizeof(QGD_Complex16));
961  memset(input.get_data() + row_offset_pair, 0, input.cols * sizeof(QGD_Complex16));
962  }
963  else {
964  // leave the state as it is
965  continue;
966  }
967 
968 
969  //std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
970 
971 
972  }
973  });
974 
975 
976 
977  current_idx = current_idx + (index_step_target << 1);
978  current_idx_pair = current_idx_pair + (index_step_target << 1);
979 
980  }
981  });
982 
983 
984 }
985 
986 
987 // ---------------------------------------------------------------------------
988 // apply_kernel_from_right AVX implementations
989 //
990 // For right-apply (input = input * U) the outer loop runs over rows so that
991 // all memory accesses within a row are sequential (row-major order).
992 // Within each row the column-pair blocks [current_idx .. current_idx+step-1]
993 // and [current_idx+step .. current_idx+2*step-1] are two contiguous segments,
994 // so the AVX inner loop is identical to the left-apply AVX kernel – only the
995 // kernel coefficient mapping is transposed: u[0],u[2] drive the first block
996 // and u[1],u[3] drive the pair block (vs u[0],u[1] / u[2],u[3] for left-apply).
997 //
998 // Control-bit handling:
999 // control_qbit < 0 → always active, full AVX
1000 // control_qbit >= target_qbit → uniform per block, one test per block
1001 // control_qbit < target_qbit → activity alternates within the block;
1002 // scalar fallback for those blocks
1003 // ---------------------------------------------------------------------------
1004 
1005 
1014 void
1015 apply_kernel_from_right_AVX(Matrix& u3_1qbit, Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size)
1016 {
1017  input.ensure_aligned();
1018 
1019  const int index_step_target = 1 << target_qbit;
1020 
1021  // Right-apply uses u[0],u[2] for element block and u[1],u[3] for pair block
1022  // (transpose of left-apply which uses u[0],u[1] and u[2],u[3]).
1023  const __m256d u00r = _mm256_broadcast_sd(&u3_1qbit[0].real);
1024  const __m256d u00i = _mm256_broadcast_sd(&u3_1qbit[0].imag);
1025  const __m256d u01r = _mm256_broadcast_sd(&u3_1qbit[1].real);
1026  const __m256d u01i = _mm256_broadcast_sd(&u3_1qbit[1].imag);
1027  const __m256d u10r = _mm256_broadcast_sd(&u3_1qbit[2].real);
1028  const __m256d u10i = _mm256_broadcast_sd(&u3_1qbit[2].imag);
1029  const __m256d u11r = _mm256_broadcast_sd(&u3_1qbit[3].real);
1030  const __m256d u11i = _mm256_broadcast_sd(&u3_1qbit[3].imag);
1031 
1032  for (int row_idx = 0; row_idx < input.rows; row_idx++) {
1033 
1034  const int row_offset = row_idx * input.stride;
1035  double* const row_data = (double*)input.get_data() + 2 * row_offset;
1036 
1037  int current_idx = 0;
1038  int current_idx_pair = index_step_target;
1039 
1040  while (current_idx_pair < input.cols) {
1041 
1042  // Determine control-bit activity for this block.
1043  // If control_qbit < target_qbit the bit alternates within the block
1044  // and we fall back to scalar for the affected elements.
1045  const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
1046  const bool active = (control_qbit < 0) ||
1047  (control_qbit >= target_qbit &&
1048  ((current_idx >> control_qbit) & 1));
1049 
1050  if (!mixed && !active) {
1051  // whole block inactive – skip
1052  } else if (!mixed) {
1053  // whole block active – use AVX
1054  double* element = row_data + 2 * current_idx;
1055  double* element_pair = row_data + 2 * current_idx_pair;
1056 
1057  int col_idx = 0;
1058  const int avx_limit = 2 * (index_step_target - 3);
1059 
1060  for (; col_idx < avx_limit; col_idx += 8) {
1061 
1062  __m256d e_vec = _mm256_loadu_pd(element + col_idx);
1063  __m256d e_vec2 = _mm256_loadu_pd(element + col_idx + 4);
1064  __m256d tmp = _mm256_shuffle_pd(e_vec, e_vec2, 0);
1065  e_vec2 = _mm256_shuffle_pd(e_vec, e_vec2, 0xf);
1066  e_vec = tmp;
1067 
1068  __m256d p_vec = _mm256_loadu_pd(element_pair + col_idx);
1069  __m256d p_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
1070  tmp = _mm256_shuffle_pd(p_vec, p_vec2, 0);
1071  p_vec2 = _mm256_shuffle_pd(p_vec, p_vec2, 0xf);
1072  p_vec = tmp;
1073 
1074  // new element block = u[0]*e + u[2]*p
1075  __m256d vec3 = _mm256_mul_pd(u00r, e_vec);
1076  vec3 = _mm256_fnmadd_pd(u00i, e_vec2, vec3);
1077  __m256d vec4 = _mm256_mul_pd(u10r, p_vec);
1078  vec4 = _mm256_fnmadd_pd(u10i, p_vec2, vec4);
1079  vec3 = _mm256_add_pd(vec3, vec4);
1080  __m256d vec5 = _mm256_mul_pd(u00r, e_vec2);
1081  vec5 = _mm256_fmadd_pd(u00i, e_vec, vec5);
1082  __m256d vec6 = _mm256_mul_pd(u10r, p_vec2);
1083  vec6 = _mm256_fmadd_pd(u10i, p_vec, vec6);
1084  vec5 = _mm256_add_pd(vec5, vec6);
1085 
1086  tmp = _mm256_shuffle_pd(vec3, vec5, 0);
1087  vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
1088  vec3 = tmp;
1089  _mm256_storeu_pd(element + col_idx, vec3);
1090  _mm256_storeu_pd(element + col_idx + 4, vec5);
1091 
1092  // new pair block = u[1]*e + u[3]*p
1093  __m256d vec7 = _mm256_mul_pd(u01r, e_vec);
1094  vec7 = _mm256_fnmadd_pd(u01i, e_vec2, vec7);
1095  __m256d vec8 = _mm256_mul_pd(u11r, p_vec);
1096  vec8 = _mm256_fnmadd_pd(u11i, p_vec2, vec8);
1097  vec7 = _mm256_add_pd(vec7, vec8);
1098  __m256d vec9 = _mm256_mul_pd(u01r, e_vec2);
1099  vec9 = _mm256_fmadd_pd(u01i, e_vec, vec9);
1100  __m256d vec10 = _mm256_mul_pd(u11r, p_vec2);
1101  vec10 = _mm256_fmadd_pd(u11i, p_vec, vec10);
1102  vec9 = _mm256_add_pd(vec9, vec10);
1103 
1104  tmp = _mm256_shuffle_pd(vec7, vec9, 0);
1105  vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
1106  vec7 = tmp;
1107  _mm256_storeu_pd(element_pair + col_idx, vec7);
1108  _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
1109  }
1110 
1111  // scalar remainder within block
1112  for (int c = col_idx / 2; c < index_step_target; c++) {
1113  const int index = row_offset + current_idx + c;
1114  const int index_pair = row_offset + current_idx_pair + c;
1115  QGD_Complex16 e = input[index];
1116  QGD_Complex16 p = input[index_pair];
1117  QGD_Complex16 t1 = mult(u3_1qbit[0], e);
1118  QGD_Complex16 t2 = mult(u3_1qbit[2], p);
1119  input[index].real = t1.real + t2.real;
1120  input[index].imag = t1.imag + t2.imag;
1121  t1 = mult(u3_1qbit[1], e);
1122  t2 = mult(u3_1qbit[3], p);
1123  input[index_pair].real = t1.real + t2.real;
1124  input[index_pair].imag = t1.imag + t2.imag;
1125  }
1126 
1127  } else {
1128  // mixed control: check element-by-element (cache-friendly – still row-outer)
1129  for (int idx = 0; idx < index_step_target; idx++) {
1130  const int col = current_idx + idx;
1131  const int col_pair = current_idx_pair + idx;
1132  if ((col >> control_qbit) & 1) {
1133  const int index = row_offset + col;
1134  const int index_pair = row_offset + col_pair;
1135  QGD_Complex16 e = input[index];
1136  QGD_Complex16 p = input[index_pair];
1137  QGD_Complex16 t1 = mult(u3_1qbit[0], e);
1138  QGD_Complex16 t2 = mult(u3_1qbit[2], p);
1139  input[index].real = t1.real + t2.real;
1140  input[index].imag = t1.imag + t2.imag;
1141  t1 = mult(u3_1qbit[1], e);
1142  t2 = mult(u3_1qbit[3], p);
1143  input[index_pair].real = t1.real + t2.real;
1144  input[index_pair].imag = t1.imag + t2.imag;
1145  }
1146  }
1147  }
1148 
1149  current_idx += (index_step_target << 1);
1150  current_idx_pair += (index_step_target << 1);
1151  }
1152  }
1153 
1154  (void)matrix_size;
1155 }
1156 
1157 
1162 void
1163 apply_kernel_from_right_AVX_parallel(Matrix& u3_1qbit, Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size)
1164 {
1165  input.ensure_aligned();
1166 
1167  const int index_step_target = 1 << target_qbit;
1168 
1169  const __m256d u00r = _mm256_broadcast_sd(&u3_1qbit[0].real);
1170  const __m256d u00i = _mm256_broadcast_sd(&u3_1qbit[0].imag);
1171  const __m256d u01r = _mm256_broadcast_sd(&u3_1qbit[1].real);
1172  const __m256d u01i = _mm256_broadcast_sd(&u3_1qbit[1].imag);
1173  const __m256d u10r = _mm256_broadcast_sd(&u3_1qbit[2].real);
1174  const __m256d u10i = _mm256_broadcast_sd(&u3_1qbit[2].imag);
1175  const __m256d u11r = _mm256_broadcast_sd(&u3_1qbit[3].real);
1176  const __m256d u11i = _mm256_broadcast_sd(&u3_1qbit[3].imag);
1177 
1178  const int grain = (input.rows < 64) ? 1 : 8;
1179 
1180  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, grain),
1181  [&](tbb::blocked_range<int> r) {
1182 
1183  for (int row_idx = r.begin(); row_idx < r.end(); row_idx++) {
1184 
1185  const int row_offset = row_idx * input.stride;
1186  double* const row_data = (double*)input.get_data() + 2 * row_offset;
1187 
1188  int current_idx = 0;
1189  int current_idx_pair = index_step_target;
1190 
1191  while (current_idx_pair < input.cols) {
1192 
1193  const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
1194  const bool active = (control_qbit < 0) ||
1195  (control_qbit >= target_qbit &&
1196  ((current_idx >> control_qbit) & 1));
1197 
1198  if (!mixed && !active) {
1199  // skip
1200  } else if (!mixed) {
1201  double* element = row_data + 2 * current_idx;
1202  double* element_pair = row_data + 2 * current_idx_pair;
1203 
1204  int col_idx = 0;
1205  const int avx_limit = 2 * (index_step_target - 3);
1206 
1207  for (; col_idx < avx_limit; col_idx += 8) {
1208 
1209  __m256d e_vec = _mm256_loadu_pd(element + col_idx);
1210  __m256d e_vec2 = _mm256_loadu_pd(element + col_idx + 4);
1211  __m256d tmp = _mm256_shuffle_pd(e_vec, e_vec2, 0);
1212  e_vec2 = _mm256_shuffle_pd(e_vec, e_vec2, 0xf);
1213  e_vec = tmp;
1214 
1215  __m256d p_vec = _mm256_loadu_pd(element_pair + col_idx);
1216  __m256d p_vec2 = _mm256_loadu_pd(element_pair + col_idx + 4);
1217  tmp = _mm256_shuffle_pd(p_vec, p_vec2, 0);
1218  p_vec2 = _mm256_shuffle_pd(p_vec, p_vec2, 0xf);
1219  p_vec = tmp;
1220 
1221  __m256d vec3 = _mm256_mul_pd(u00r, e_vec);
1222  vec3 = _mm256_fnmadd_pd(u00i, e_vec2, vec3);
1223  __m256d vec4 = _mm256_mul_pd(u10r, p_vec);
1224  vec4 = _mm256_fnmadd_pd(u10i, p_vec2, vec4);
1225  vec3 = _mm256_add_pd(vec3, vec4);
1226  __m256d vec5 = _mm256_mul_pd(u00r, e_vec2);
1227  vec5 = _mm256_fmadd_pd(u00i, e_vec, vec5);
1228  __m256d vec6 = _mm256_mul_pd(u10r, p_vec2);
1229  vec6 = _mm256_fmadd_pd(u10i, p_vec, vec6);
1230  vec5 = _mm256_add_pd(vec5, vec6);
1231 
1232  tmp = _mm256_shuffle_pd(vec3, vec5, 0);
1233  vec5 = _mm256_shuffle_pd(vec3, vec5, 0xf);
1234  vec3 = tmp;
1235  _mm256_storeu_pd(element + col_idx, vec3);
1236  _mm256_storeu_pd(element + col_idx + 4, vec5);
1237 
1238  __m256d vec7 = _mm256_mul_pd(u01r, e_vec);
1239  vec7 = _mm256_fnmadd_pd(u01i, e_vec2, vec7);
1240  __m256d vec8 = _mm256_mul_pd(u11r, p_vec);
1241  vec8 = _mm256_fnmadd_pd(u11i, p_vec2, vec8);
1242  vec7 = _mm256_add_pd(vec7, vec8);
1243  __m256d vec9 = _mm256_mul_pd(u01r, e_vec2);
1244  vec9 = _mm256_fmadd_pd(u01i, e_vec, vec9);
1245  __m256d vec10 = _mm256_mul_pd(u11r, p_vec2);
1246  vec10 = _mm256_fmadd_pd(u11i, p_vec, vec10);
1247  vec9 = _mm256_add_pd(vec9, vec10);
1248 
1249  tmp = _mm256_shuffle_pd(vec7, vec9, 0);
1250  vec9 = _mm256_shuffle_pd(vec7, vec9, 0xf);
1251  vec7 = tmp;
1252  _mm256_storeu_pd(element_pair + col_idx, vec7);
1253  _mm256_storeu_pd(element_pair + col_idx + 4, vec9);
1254  }
1255 
1256  for (int c = col_idx / 2; c < index_step_target; c++) {
1257  const int index = row_offset + current_idx + c;
1258  const int index_pair = row_offset + current_idx_pair + c;
1259  QGD_Complex16 e = input[index];
1260  QGD_Complex16 p = input[index_pair];
1261  QGD_Complex16 t1 = mult(u3_1qbit[0], e);
1262  QGD_Complex16 t2 = mult(u3_1qbit[2], p);
1263  input[index].real = t1.real + t2.real;
1264  input[index].imag = t1.imag + t2.imag;
1265  t1 = mult(u3_1qbit[1], e);
1266  t2 = mult(u3_1qbit[3], p);
1267  input[index_pair].real = t1.real + t2.real;
1268  input[index_pair].imag = t1.imag + t2.imag;
1269  }
1270 
1271  } else {
1272  for (int idx = 0; idx < index_step_target; idx++) {
1273  const int col = current_idx + idx;
1274  const int col_pair = current_idx_pair + idx;
1275  if ((col >> control_qbit) & 1) {
1276  const int index = row_offset + col;
1277  const int index_pair = row_offset + col_pair;
1278  QGD_Complex16 e = input[index];
1279  QGD_Complex16 p = input[index_pair];
1280  QGD_Complex16 t1 = mult(u3_1qbit[0], e);
1281  QGD_Complex16 t2 = mult(u3_1qbit[2], p);
1282  input[index].real = t1.real + t2.real;
1283  input[index].imag = t1.imag + t2.imag;
1284  t1 = mult(u3_1qbit[1], e);
1285  t2 = mult(u3_1qbit[3], p);
1286  input[index_pair].real = t1.real + t2.real;
1287  input[index_pair].imag = t1.imag + t2.imag;
1288  }
1289  }
1290  }
1291 
1292  current_idx += (index_step_target << 1);
1293  current_idx_pair += (index_step_target << 1);
1294  }
1295  }
1296  });
1297 
1298  (void)matrix_size;
1299 }
1300 
1301 
1306 void
1307 apply_kernel_from_right_AVX32(Matrix_float& u3_1qbit, Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size)
1308 {
1309  input.ensure_aligned();
1310 
1311  auto cmul_ps = [](__m256 ar, __m256 ai, __m256 x) {
1312  const __m256 swapped = _mm256_permute_ps(x, 0xB1);
1313  return _mm256_fmaddsub_ps(ar, x, _mm256_mul_ps(ai, swapped));
1314  };
1315 
1316  const int index_step_target = 1 << target_qbit;
1317 
1318  // Right-apply: u[0],u[2] for element block; u[1],u[3] for pair block.
1319  const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].real);
1320  const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
1321  const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
1322  const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
1323  const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
1324  const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
1325  const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
1326  const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
1327 
1328  const float u00r_s = u3_1qbit[0].real;
1329  const float u00i_s = u3_1qbit[0].imag;
1330  const float u01r_s = u3_1qbit[1].real;
1331  const float u01i_s = u3_1qbit[1].imag;
1332  const float u10r_s = u3_1qbit[2].real;
1333  const float u10i_s = u3_1qbit[2].imag;
1334  const float u11r_s = u3_1qbit[3].real;
1335  const float u11i_s = u3_1qbit[3].imag;
1336 
1337  auto apply_pair_scalar = [&](const int index, const int index_pair) {
1338  const QGD_Complex8 e = input[index];
1339  const QGD_Complex8 p = input[index_pair];
1340  input[index].real = u00r_s * e.real - u00i_s * e.imag + u10r_s * p.real - u10i_s * p.imag;
1341  input[index].imag = u00r_s * e.imag + u00i_s * e.real + u10r_s * p.imag + u10i_s * p.real;
1342  input[index_pair].real = u01r_s * e.real - u01i_s * e.imag + u11r_s * p.real - u11i_s * p.imag;
1343  input[index_pair].imag = u01r_s * e.imag + u01i_s * e.real + u11r_s * p.imag + u11i_s * p.real;
1344  };
1345 
1346  for (int row_idx = 0; row_idx < input.rows; row_idx++) {
1347 
1348  const int row_offset = row_idx * input.stride;
1349  float* const row_data = (float*)input.get_data() + 2 * row_offset;
1350 
1351  int current_idx = 0;
1352  int current_idx_pair = index_step_target;
1353 
1354  while (current_idx_pair < input.cols) {
1355 
1356  const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
1357  const bool active = (control_qbit < 0) ||
1358  (control_qbit >= target_qbit &&
1359  ((current_idx >> control_qbit) & 1));
1360 
1361  if (!mixed && !active) {
1362  // skip
1363  } else if (!mixed) {
1364  float* element = row_data + 2 * current_idx;
1365  float* element_pair = row_data + 2 * current_idx_pair;
1366 
1367  int col_idx = 0;
1368  const int avx_limit = 2 * index_step_target - 8;
1369 
1370  for (; col_idx <= avx_limit; col_idx += 8) {
1371  const __m256 e = _mm256_loadu_ps(element + col_idx);
1372  const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
1373  // new element = u[0]*e + u[2]*p
1374  const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
1375  // new pair = u[1]*e + u[3]*p
1376  const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
1377  _mm256_storeu_ps(element + col_idx, out0);
1378  _mm256_storeu_ps(element_pair + col_idx, out1);
1379  }
1380 
1381  for (int c = col_idx / 2; c < index_step_target; c++) {
1382  const int index = row_offset + current_idx + c;
1383  const int index_pair = row_offset + current_idx_pair + c;
1384  apply_pair_scalar(index, index_pair);
1385  }
1386 
1387  } else {
1388  for (int idx = 0; idx < index_step_target; idx++) {
1389  const int col = current_idx + idx;
1390  const int col_pair = current_idx_pair + idx;
1391  if ((col >> control_qbit) & 1) {
1392  const int index = row_offset + col;
1393  const int index_pair = row_offset + col_pair;
1394  apply_pair_scalar(index, index_pair);
1395  }
1396  }
1397  }
1398 
1399  current_idx += (index_step_target << 1);
1400  current_idx_pair += (index_step_target << 1);
1401  }
1402  }
1403 
1404  (void)matrix_size;
1405 }
1406 
1407 
1412 void
1414 {
1415  input.ensure_aligned();
1416 
1417  auto cmul_ps = [](__m256 ar, __m256 ai, __m256 x) {
1418  const __m256 swapped = _mm256_permute_ps(x, 0xB1);
1419  return _mm256_fmaddsub_ps(ar, x, _mm256_mul_ps(ai, swapped));
1420  };
1421 
1422  const int index_step_target = 1 << target_qbit;
1423 
1424  const __m256 u00r = _mm256_set1_ps(u3_1qbit[0].real);
1425  const __m256 u00i = _mm256_set1_ps(u3_1qbit[0].imag);
1426  const __m256 u01r = _mm256_set1_ps(u3_1qbit[1].real);
1427  const __m256 u01i = _mm256_set1_ps(u3_1qbit[1].imag);
1428  const __m256 u10r = _mm256_set1_ps(u3_1qbit[2].real);
1429  const __m256 u10i = _mm256_set1_ps(u3_1qbit[2].imag);
1430  const __m256 u11r = _mm256_set1_ps(u3_1qbit[3].real);
1431  const __m256 u11i = _mm256_set1_ps(u3_1qbit[3].imag);
1432 
1433  const float u00r_s = u3_1qbit[0].real;
1434  const float u00i_s = u3_1qbit[0].imag;
1435  const float u01r_s = u3_1qbit[1].real;
1436  const float u01i_s = u3_1qbit[1].imag;
1437  const float u10r_s = u3_1qbit[2].real;
1438  const float u10i_s = u3_1qbit[2].imag;
1439  const float u11r_s = u3_1qbit[3].real;
1440  const float u11i_s = u3_1qbit[3].imag;
1441 
1442  const int grain = (input.rows < 64) ? 1 : 8;
1443 
1444  tbb::parallel_for(tbb::blocked_range<int>(0, input.rows, grain),
1445  [&](tbb::blocked_range<int> r) {
1446 
1447  for (int row_idx = r.begin(); row_idx < r.end(); row_idx++) {
1448 
1449  const int row_offset = row_idx * input.stride;
1450  float* const row_data = (float*)input.get_data() + 2 * row_offset;
1451 
1452  auto apply_pair_scalar = [&](const int index, const int index_pair) {
1453  const QGD_Complex8 e = input[index];
1454  const QGD_Complex8 p = input[index_pair];
1455  input[index].real = u00r_s * e.real - u00i_s * e.imag + u10r_s * p.real - u10i_s * p.imag;
1456  input[index].imag = u00r_s * e.imag + u00i_s * e.real + u10r_s * p.imag + u10i_s * p.real;
1457  input[index_pair].real = u01r_s * e.real - u01i_s * e.imag + u11r_s * p.real - u11i_s * p.imag;
1458  input[index_pair].imag = u01r_s * e.imag + u01i_s * e.real + u11r_s * p.imag + u11i_s * p.real;
1459  };
1460 
1461  int current_idx = 0;
1462  int current_idx_pair = index_step_target;
1463 
1464  while (current_idx_pair < input.cols) {
1465 
1466  const bool mixed = (control_qbit >= 0 && control_qbit < target_qbit);
1467  const bool active = (control_qbit < 0) ||
1468  (control_qbit >= target_qbit &&
1469  ((current_idx >> control_qbit) & 1));
1470 
1471  if (!mixed && !active) {
1472  // skip
1473  } else if (!mixed) {
1474  float* element = row_data + 2 * current_idx;
1475  float* element_pair = row_data + 2 * current_idx_pair;
1476 
1477  int col_idx = 0;
1478  const int avx_limit = 2 * index_step_target - 8;
1479 
1480  for (; col_idx <= avx_limit; col_idx += 8) {
1481  const __m256 e = _mm256_loadu_ps(element + col_idx);
1482  const __m256 p = _mm256_loadu_ps(element_pair + col_idx);
1483  const __m256 out0 = _mm256_add_ps(cmul_ps(u00r, u00i, e), cmul_ps(u10r, u10i, p));
1484  const __m256 out1 = _mm256_add_ps(cmul_ps(u01r, u01i, e), cmul_ps(u11r, u11i, p));
1485  _mm256_storeu_ps(element + col_idx, out0);
1486  _mm256_storeu_ps(element_pair + col_idx, out1);
1487  }
1488 
1489  for (int c = col_idx / 2; c < index_step_target; c++) {
1490  const int index = row_offset + current_idx + c;
1491  const int index_pair = row_offset + current_idx_pair + c;
1492  apply_pair_scalar(index, index_pair);
1493  }
1494 
1495  } else {
1496  for (int idx = 0; idx < index_step_target; idx++) {
1497  const int col = current_idx + idx;
1498  const int col_pair = current_idx_pair + idx;
1499  if ((col >> control_qbit) & 1) {
1500  const int index = row_offset + col;
1501  const int index_pair = row_offset + col_pair;
1502  apply_pair_scalar(index, index_pair);
1503  }
1504  }
1505  }
1506 
1507  current_idx += (index_step_target << 1);
1508  current_idx_pair += (index_step_target << 1);
1509  }
1510  }
1511  });
1512 
1513  (void)matrix_size;
1514 }
1515 
1516 
void apply_kernel_to_input_AVX_parallel32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_kernel_to_input_AVX_small32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
void apply_kernel_from_right_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel: apply 2x2 gate from the right (input = input * U), row-major f32.
int stride
The column stride of the array. (The array elements in one row are a_0, a_1, ... a_{cols-1}, 0, 0, 0, 0. The number of zeros is stride-cols)
Definition: matrix_base.hpp:46
float real
real part
Definition: QGDTypes.h:47
Structure type representing single-precision complex numbers.
Definition: QGDTypes.h:46
void apply_kernel_to_input_AVX_small(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel to apply single qubit gate kernel on an input matrix (efficient for small inputs) ...
void apply_kernel_to_input_AVX(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel to apply single qubit gate kernel on an input matrix (single threaded) ...
void ensure_aligned()
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
Definition: common.cpp:298
float imag
imaginary part
Definition: QGDTypes.h:48
scalar * get_data() const
Call to get the pointer to the stored data.
void apply_kernel_from_right_AVX(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
AVX kernel: apply 2x2 gate from the right (input = input * U), row-major f64.
void apply_kernel_to_input_AVX_parallel(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel to apply single qubit gate kernel on an input matrix.
int rows
The number of rows.
Definition: matrix_base.hpp:42
int cols
The number of columns.
Definition: matrix_base.hpp:44
matrix_size
[load Umtx]
Definition: example.py:58
void apply_kernel_from_right_AVX_parallel(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel: apply 2x2 gate from the right (input = input * U), f64.
void apply_kernel_from_right_AVX_small(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
Double-precision complex matrix (float64).
Definition: matrix.h:38
void apply_kernel_from_right_AVX_parallel32(Matrix_float &u3_1qbit, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Parallel AVX kernel: apply 2x2 gate from the right (input = input * U), f32.
Single-precision complex matrix (float32).
Definition: matrix_float.h:41
void apply_kernel_from_right_AVX_small32(Matrix_float &u3_1qbit, Matrix_float &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
double real
the real part of a complex number
Definition: QGDTypes.h:40
int t2
Definition: noise.py:42
int t1
Definition: noise.py:41
void apply_kernel_to_input_AVX32(Matrix_float &u3_1qbit, Matrix_float &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42