Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
apply_kernel_to_input.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright 2020 Peter Rakyta, Ph.D.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 
17 @author: Peter Rakyta, Ph.D.
18 */
24 #include "apply_kernel_to_input.h"
25 //#include <immintrin.h>
26 #include "tbb/tbb.h"
27 #include <type_traits>
28 #include <utility>
29 
30 template<typename MatrixT>
31 using KernelComplexT = typename std::remove_reference<decltype(std::declval<MatrixT&>()[0])>::type;
32 
33 template<typename MatrixT>
34 void
35 apply_kernel_to_input_impl(MatrixT& u3_1qbit, MatrixT& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
36 
37 
38 
39 
40 
50  using ComplexT = KernelComplexT<MatrixT>;
51 
52  int index_step_target = 1 << target_qbit;
53  int current_idx = 0;
54 
55 
56  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) ) {
57 
58  for(int idx=0; idx<index_step_target; idx++) {
59  //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {
60 
61  int current_idx_loc = current_idx + idx;
62  int current_idx_pair_loc = current_idx_pair + idx;
63 
64  int row_offset = current_idx_loc*input.stride;
65  int row_offset_pair = current_idx_pair_loc*input.stride;
66 
67  if ( control_qbit<0 || ((current_idx_loc >> control_qbit) & 1) ) {
68 
69  for ( int col_idx=0; col_idx<input.cols; col_idx++) {
70 
71  int index = row_offset+col_idx;
72  int index_pair = row_offset_pair+col_idx;
73 
74  ComplexT element = input[index];
75  ComplexT element_pair = input[index_pair];
76 
77  ComplexT tmp1 = mult(u3_1qbit[0], element);
78  ComplexT tmp2 = mult(u3_1qbit[1], element_pair);
79 
80  input[index].real = tmp1.real + tmp2.real;
81  input[index].imag = tmp1.imag + tmp2.imag;
82 
83  tmp1 = mult(u3_1qbit[2], element);
84  tmp2 = mult(u3_1qbit[3], element_pair);
85 
86  input[index_pair].real = tmp1.real + tmp2.real;
87  input[index_pair].imag = tmp1.imag + tmp2.imag;
88 
89  }
90 
91 
92  }
93  else if (deriv) {
94  // when calculating derivatives, the constant element should be zeros
95  memset( input.get_data()+row_offset, 0, input.cols*sizeof(ComplexT));
96  memset( input.get_data()+row_offset_pair, 0, input.cols*sizeof(ComplexT));
97  }
98  else {
99  // leave the state as it is
100  continue;
101  }
102 
103 
104 
105  //});
106  }
107 
108 
109  current_idx = current_idx + (index_step_target << 1);
110 
111 
112  }
113 
114 
115 }
116 
117 void
118 apply_kernel_to_input(Matrix& u3_1qbit, Matrix& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
119  apply_kernel_to_input_impl(u3_1qbit, input, deriv, target_qbit, control_qbit, matrix_size);
120 }
121 
122 
123 void
124 apply_kernel_to_input(Matrix_float& u3_1qbit, Matrix_float& input, const bool& deriv, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
125  apply_kernel_to_input_impl(u3_1qbit, input, deriv, target_qbit, control_qbit, matrix_size);
126 }
127 
128 
129 template<typename MatrixT>
130 void
131 apply_kernel_from_right_impl(MatrixT& u3_1qbit, MatrixT& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
132 
133  using ComplexT = KernelComplexT<MatrixT>;
134 
135  int index_step_target = 1 << target_qbit;
136 
137  // Outer loop over rows so all accesses within a row are sequential (row-major).
138  // The original column-pair-outer / row-inner order caused strided cache misses.
139  for (int row_idx = 0; row_idx < input.rows; row_idx++) {
140 
141  int row_offset = row_idx * input.stride;
142  int current_idx = 0;
143  int current_idx_pair = index_step_target;
144 
145  while (current_idx_pair < input.cols) {
146 
147  for (int idx = 0; idx < index_step_target; idx++) {
148 
149  int current_idx_loc = current_idx + idx;
150  int current_idx_pair_loc = current_idx_pair + idx;
151 
152  if (control_qbit < 0 || ((current_idx_loc >> control_qbit) & 1)) {
153 
154  int index = row_offset + current_idx_loc;
155  int index_pair = row_offset + current_idx_pair_loc;
156 
157  ComplexT element = input[index];
158  ComplexT element_pair = input[index_pair];
159 
160  ComplexT tmp1 = mult(u3_1qbit[0], element);
161  ComplexT tmp2 = mult(u3_1qbit[2], element_pair);
162  input[index].real = tmp1.real + tmp2.real;
163  input[index].imag = tmp1.imag + tmp2.imag;
164 
165  tmp1 = mult(u3_1qbit[1], element);
166  tmp2 = mult(u3_1qbit[3], element_pair);
167  input[index_pair].real = tmp1.real + tmp2.real;
168  input[index_pair].imag = tmp1.imag + tmp2.imag;
169  }
170  }
171 
172  current_idx += (index_step_target << 1);
173  current_idx_pair += (index_step_target << 1);
174  }
175  }
176 
177  (void)matrix_size;
178 }
179 
180 
181 void
182 apply_kernel_from_right(Matrix& u3_1qbit, Matrix& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
183  apply_kernel_from_right_impl(u3_1qbit, input, target_qbit, control_qbit, matrix_size);
184 }
185 
186 
187 void
188 apply_kernel_from_right(Matrix_float& u3_1qbit, Matrix_float& input, const int& target_qbit, const int& control_qbit, const int& matrix_size) {
189  apply_kernel_from_right_impl(u3_1qbit, input, target_qbit, control_qbit, matrix_size);
190 }
void apply_kernel_from_right(Matrix &u3_1qbit, Matrix &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Apply a 2x2 kernel from the right: input = input * U.
void apply_kernel_from_right_impl(MatrixT &u3_1qbit, MatrixT &input, const int &target_qbit, const int &control_qbit, const int &matrix_size)
QGD_Complex16 mult(QGD_Complex16 &a, QGD_Complex16 &b)
Call to calculate the product of two complex scalars.
Definition: common.cpp:298
void apply_kernel_to_input_impl(MatrixT &u3_1qbit, MatrixT &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
typename std::remove_reference< decltype(std::declval< MatrixT & >()[0])>::type KernelComplexT
matrix_size
[load Umtx]
Definition: example.py:58
void apply_kernel_to_input(Matrix &u3_1qbit, Matrix &input, const bool &deriv, const int &target_qbit, const int &control_qbit, const int &matrix_size)
Call to apply kernel to apply single qubit gate kernel on an input matrix.
Double-precision complex matrix (float64).
Definition: matrix.h:38
Single-precision complex matrix (float32).
Definition: matrix_float.h:41