29 #include <type_traits> 32 template<
typename MatrixT>
33 using KernelLargeComplexT =
typename std::remove_reference<decltype(std::declval<MatrixT&>()[0])>::type;
35 template<
typename MatrixT>
38 template<
typename MatrixT>
41 template<
typename MatrixT>
44 template<
typename MatrixT>
47 template<
typename MatrixT>
50 template<
typename MatrixT>
53 template<
typename MatrixT>
56 template<
typename MatrixT>
62 for (
int step=1; step<7; step++){
63 if (index_step <= 1<<step){
64 grain_size = 256/(1<<step);
70 template<
typename MatrixT>
72 MatrixT transposed = unitary.copy();
73 for (
int row = 0; row < unitary.rows; ++row) {
74 for (
int col = 0; col < unitary.cols; ++col) {
75 transposed[row * unitary.cols + col] = unitary[col * unitary.cols + row];
81 template<
typename MatrixT>
85 switch(involved_qbits.size()){
90 default:
throw std::invalid_argument(
"Unsupported number of qubits for state vector.");
95 if (involved_qbits.size() == 2) {
104 template<
typename MatrixT>
107 if (input.cols == 1) {
108 throw std::invalid_argument(
"Right-apply is not supported for column state vectors.");
111 if (involved_qbits.size() < 2 || involved_qbits.size() > 5) {
112 throw std::invalid_argument(
"Unsupported number of qubits for right-applied matrix input.");
115 if (involved_qbits.size() == 2) {
123 template<
typename MatrixT>
128 const int n =
static_cast<int>(involved_qbits.size());
129 const int block_size = 1 <<
n;
131 if (input.cols == 1) {
132 throw std::invalid_argument(
"Left-apply large matrix-input dispatch received a column state vector.");
135 if (n < 2 || n > 5) {
136 throw std::invalid_argument(
"Unsupported number of qubits for left-applied matrix input.");
139 if (unitary.rows != block_size || unitary.cols != block_size) {
140 throw std::invalid_argument(
"Left-apply large-kernel dispatch received a mismatched local kernel size.");
143 std::sort(involved_qbits.begin(), involved_qbits.end());
147 while (dim < matrix_size) {
152 std::vector<int> non_targets;
153 non_targets.reserve(qubit_num - n);
154 for (
int q = 0; q < qubit_num; ++q) {
155 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
156 non_targets.push_back(q);
160 std::vector<int> block_pattern(block_size);
161 for (
int k = 0;
k < block_size; ++
k) {
163 for (
int bit = 0; bit <
n; ++bit) {
164 if (
k & (1 << bit)) {
165 idx |= (1 << involved_qbits[bit]);
168 block_pattern[
k] = idx;
171 const int num_blocks = matrix_size >>
n;
172 const ComplexT* unitary_data = unitary.get_data();
173 ComplexT* input_data = input.get_data();
174 const int unitary_stride = unitary.stride;
175 const int input_stride = input.stride;
176 std::array<int, 32> indices;
177 std::array<ComplexT, 32> source;
178 std::array<ComplexT, 32> out;
180 for (
int col = 0; col < input.cols; ++col) {
181 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
183 for (
size_t i = 0; i < non_targets.size(); ++i) {
184 if (iter_idx & (1ULL << i)) {
185 base |= (1 << non_targets[i]);
189 for (
int k = 0;
k < block_size; ++
k) {
190 indices[
k] = base | block_pattern[
k];
191 source[
k] = input_data[indices[
k] * input_stride + col];
194 for (
int out_idx = 0; out_idx < block_size; ++out_idx) {
199 for (
int in_idx = 0; in_idx < block_size; ++in_idx) {
200 const ComplexT kernel_element = unitary_data[out_idx * unitary_stride + in_idx];
201 const ComplexT source_element = source[in_idx];
202 accum.real += kernel_element.real * source_element.real - kernel_element.imag * source_element.imag;
203 accum.imag += kernel_element.real * source_element.imag + kernel_element.imag * source_element.real;
206 out[out_idx] = accum;
209 for (
int out_idx = 0; out_idx < block_size; ++out_idx) {
210 input_data[indices[out_idx] * input_stride + col] = out[out_idx];
216 template<
typename MatrixT>
221 const int n =
static_cast<int>(involved_qbits.size());
222 const int block_size = 1 <<
n;
224 if (unitary.rows != block_size || unitary.cols != block_size) {
225 throw std::invalid_argument(
"Right-apply large-kernel dispatch received a mismatched local kernel size.");
228 std::sort(involved_qbits.begin(), involved_qbits.end());
232 while (dim < matrix_size) {
237 std::vector<int> non_targets;
238 non_targets.reserve(qubit_num - n);
239 for (
int q = 0; q < qubit_num; ++q) {
240 if (!std::binary_search(involved_qbits.begin(), involved_qbits.end(), q)) {
241 non_targets.push_back(q);
245 std::vector<int> block_pattern(block_size);
246 for (
int k = 0;
k < block_size; ++
k) {
248 for (
int bit = 0; bit <
n; ++bit) {
249 if (
k & (1 << bit)) {
250 idx |= (1 << involved_qbits[bit]);
253 block_pattern[
k] = idx;
256 const int num_blocks = matrix_size >>
n;
257 const ComplexT* unitary_data = unitary.get_data();
258 ComplexT* input_data = input.get_data();
259 const int unitary_stride = unitary.stride;
260 const int input_stride = input.stride;
261 std::array<int, 32> indices;
262 std::array<ComplexT, 32> source;
263 std::array<ComplexT, 32> out;
265 for (
int row = 0; row < input.rows; ++row) {
266 const int row_offset = row * input_stride;
268 for (
int iter_idx = 0; iter_idx < num_blocks; ++iter_idx) {
270 for (
size_t i = 0; i < non_targets.size(); ++i) {
271 if (iter_idx & (1ULL << i)) {
272 base |= (1 << non_targets[i]);
276 for (
int k = 0;
k < block_size; ++
k) {
277 indices[
k] = base | block_pattern[
k];
278 source[
k] = input_data[row_offset + indices[
k]];
281 for (
int out_idx = 0; out_idx < block_size; ++out_idx) {
286 for (
int in_idx = 0; in_idx < block_size; ++in_idx) {
287 const ComplexT kernel_element = unitary_data[in_idx * unitary_stride + out_idx];
288 const ComplexT source_element = source[in_idx];
289 accum.real += kernel_element.real * source_element.real - kernel_element.imag * source_element.imag;
290 accum.imag += kernel_element.real * source_element.imag + kernel_element.imag * source_element.real;
293 out[out_idx] = accum;
296 for (
int out_idx = 0; out_idx < block_size; ++out_idx) {
297 input_data[row_offset + indices[out_idx]] = out[out_idx];
311 template<
typename MatrixT>
314 int index_step_outer = 1 << outer_qbit;
315 int index_step_inner = 1 << inner_qbit;
318 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)){
320 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner=current_idx_inner+(index_step_inner<<1)){
322 for (
int idx=0; idx<index_step_inner; idx++){
324 int current_idx_outer_loc = current_idx + current_idx_inner + idx;
325 int current_idx_inner_loc = current_idx + current_idx_inner + idx + index_step_inner;
326 int current_idx_outer_pair_loc = current_idx_pair_outer + idx + current_idx_inner;
327 int current_idx_inner_pair_loc = current_idx_pair_outer + idx + current_idx_inner + index_step_inner;
329 int row_offset_outer = current_idx_outer_loc*input.stride;
330 int row_offset_outer_pair = current_idx_outer_pair_loc*input.stride;
331 int row_offset_inner = current_idx_inner_loc*input.stride;
332 int row_offset_inner_pair = current_idx_inner_pair_loc*input.stride;
334 for (
int col_idx=0; col_idx<input.cols; col_idx++) {
335 int index_outer = row_offset_outer+col_idx;
336 int index_outer_pair = row_offset_outer_pair+col_idx;
337 int index_inner = row_offset_inner+col_idx;
338 int index_inner_pair = row_offset_inner_pair + col_idx;
339 int indexes[4] = {index_outer,index_inner,index_outer_pair,index_inner_pair};
349 for (
int mult_idx=0; mult_idx<4; mult_idx++){
351 tmp1 =
mult(two_qbit_unitary[mult_idx*4], element_outer);
352 tmp2 =
mult(two_qbit_unitary[mult_idx*4 + 1], element_inner);
353 tmp3 =
mult(two_qbit_unitary[mult_idx*4 + 2], element_outer_pair);
354 tmp4 =
mult(two_qbit_unitary[mult_idx*4 + 3], element_inner_pair);
355 input[indexes[mult_idx]].real = tmp1.real + tmp2.real + tmp3.real + tmp4.real;
356 input[indexes[mult_idx]].imag = tmp1.imag + tmp2.imag + tmp3.imag + tmp4.imag;
361 current_idx = current_idx + (index_step_outer << 1);
366 template<
typename MatrixT>
369 int index_step_outer = 1 << outer_qbit;
370 int index_step_inner = 1 << inner_qbit;
372 for (
int row_idx = 0; row_idx < input.rows; row_idx++) {
374 int row_offset = row_idx * input.stride;
377 for (
int current_idx_pair_outer = current_idx + index_step_outer; current_idx_pair_outer < input.cols; current_idx_pair_outer = current_idx_pair_outer + (index_step_outer << 1)) {
379 for (
int current_idx_inner = 0; current_idx_inner < index_step_outer; current_idx_inner = current_idx_inner + (index_step_inner << 1)) {
381 for (
int idx = 0; idx < index_step_inner; idx++) {
383 int current_idx_outer_loc = current_idx + current_idx_inner + idx;
384 int current_idx_inner_loc = current_idx + current_idx_inner + idx + index_step_inner;
385 int current_idx_outer_pair_loc = current_idx_pair_outer + idx + current_idx_inner;
386 int current_idx_inner_pair_loc = current_idx_pair_outer + idx + current_idx_inner + index_step_inner;
389 row_offset + current_idx_outer_loc,
390 row_offset + current_idx_inner_loc,
391 row_offset + current_idx_outer_pair_loc,
392 row_offset + current_idx_inner_pair_loc,
405 for (
int out_idx = 0; out_idx < 4; out_idx++) {
407 tmp1 =
mult(two_qbit_unitary[out_idx], element_outer);
408 tmp2 =
mult(two_qbit_unitary[4 + out_idx], element_inner);
409 tmp3 =
mult(two_qbit_unitary[8 + out_idx], element_outer_pair);
410 tmp4 =
mult(two_qbit_unitary[12 + out_idx], element_inner_pair);
412 input[indexes[out_idx]].real = tmp1.real + tmp2.real + tmp3.real + tmp4.real;
413 input[indexes[out_idx]].imag = tmp1.imag + tmp2.imag + tmp3.imag + tmp4.imag;
418 current_idx = current_idx + (index_step_outer << 1);
434 template<
typename MatrixT>
442 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) ) {
444 for(
int idx=0; idx<index_step_target; idx++) {
447 int current_idx_loc = current_idx + idx;
448 int current_idx_pair_loc = current_idx_pair + idx;
450 int row_offset = current_idx_loc*input.stride;
451 int row_offset_pair = current_idx_pair_loc*input.stride;
452 for (
int col_idx=0; col_idx<input.cols; col_idx++) {
454 int index = row_offset+col_idx;
455 int index_pair = row_offset_pair+col_idx;
456 if ( (current_idx_loc >> control_qbit) & 1 ) {
466 input[index].real = tmp1.real + tmp2.real;
467 input[index].imag = tmp1.imag + tmp2.imag;
469 tmp1 =
mult(u3_1qbit1[2], element);
470 tmp2 =
mult(u3_1qbit1[3], element_pair);
472 input[index_pair].real = tmp1.real + tmp2.real;
473 input[index_pair].imag = tmp1.imag + tmp2.imag;
484 input[index].real = tmp1.real + tmp2.real;
485 input[index].imag = tmp1.imag + tmp2.imag;
487 tmp1 =
mult(u3_1qbit2[2], element);
488 tmp2 =
mult(u3_1qbit2[3], element_pair);
490 input[index_pair].real = tmp1.real + tmp2.real;
491 input[index_pair].imag = tmp1.imag + tmp2.imag;
500 current_idx = current_idx + (index_step_target << 1);
509 template<
typename MatrixT>
515 for (
int row_idx = 0; row_idx < input.rows; ++row_idx) {
517 int row_offset = row_idx * input.stride;
519 int current_idx_pair = index_step_target;
521 while (current_idx_pair < input.cols) {
523 for (
int idx = 0; idx < index_step_target; ++idx) {
525 int current_idx_loc = current_idx + idx;
526 int current_idx_pair_loc = current_idx_pair + idx;
527 int index = row_offset + current_idx_loc;
528 int index_pair = row_offset + current_idx_pair_loc;
530 const MatrixT& gate = ((current_idx_loc >>
control_qbit) & 1) ? u3_1qbit1 : u3_1qbit2;
537 input[index].real = tmp1.real + tmp2.real;
538 input[index].imag = tmp1.imag + tmp2.imag;
540 tmp1 =
mult(gate[1], element);
541 tmp2 =
mult(gate[3], element_pair);
542 input[index_pair].real = tmp1.real + tmp2.real;
543 input[index_pair].imag = tmp1.imag + tmp2.imag;
546 current_idx += (index_step_target << 1);
547 current_idx_pair += (index_step_target << 1);
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
Double-precision complex matrix (float64).
Single-precision complex matrix (float32).