31 #include <tbb/enumerable_thread_specific.h> 46 struct GateKernelScratch {
52 struct GateKernelScratchFloat {
58 inline bool has_inplace_small_kernel(
gate_type type) {
88 template <
typename ScratchT>
89 class ReentrantScratchLease {
91 ReentrantScratchLease(tbb::enumerable_thread_specific<std::vector<ScratchT>>& scratch_tls_in,
92 tbb::enumerable_thread_specific<int>& depth_tls_in)
93 : depth_tls(depth_tls_in) {
94 int& depth = depth_tls.local();
95 std::vector<ScratchT>& scratch = scratch_tls_in.local();
96 if (scratch.size() <=
static_cast<size_t>(depth)) {
97 scratch.resize(static_cast<size_t>(depth) + 1);
99 ptr = &scratch[
static_cast<size_t>(depth++)];
102 ~ReentrantScratchLease() {
111 tbb::enumerable_thread_specific<int>& depth_tls;
136 target_qbits.clear();
137 control_qbits.clear();
141 parameter_start_idx = 0;
142 matrix_alloc_float_valid =
false;
169 target_qbits.clear();
170 control_qbits.clear();
174 parameter_start_idx = 0;
175 matrix_alloc_float_valid =
false;
186 Gate::Gate(
int qbit_num_in,
const std::vector<int>& target_qbits_in,
const std::vector<int>& control_qbits_in) {
201 parameter_start_idx = 0;
202 matrix_alloc_float_valid =
false;
205 for (
int tq : target_qbits_in) {
206 if (tq >= qbit_num_in) {
207 std::stringstream sstream;
208 sstream <<
"Gate: Target qubit index " << tq <<
" is larger than or equal to the number of qubits " << qbit_num_in << std::endl;
215 for (
int cq : control_qbits_in) {
216 if (cq >= qbit_num_in) {
217 std::stringstream sstream;
218 sstream <<
"Gate: Control qubit index " << cq <<
" is larger than or equal to the number of qubits " << qbit_num_in << std::endl;
224 target_qbits = target_qbits_in;
225 control_qbits = control_qbits_in;
226 std::sort(target_qbits.begin(), target_qbits.end());
227 std::sort(control_qbits.begin(), control_qbits.end());
229 target_qbit = target_qbits.empty() ? -1 : target_qbits[0];
230 control_qbit = control_qbits.empty() ? -1 : control_qbits[0];
249 std::string err(
"Gate::set_qbit_num: The number of qbits is too small, conflicting with either target_qbit os control_qbit");
272 apply_to(parameters, gate_matrix, parallel);
279 return get_matrix(parameters, 0);
309 apply_to(parameters, gate_matrix, parallel);
323 for (
Matrix& input : inputs) {
330 tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
331 for (
int idx=r.begin(); idx<r.end(); ++idx) {
333 Matrix* input = &inputs[idx];
358 for (
Matrix& input : inputs) {
359 apply_to(parameters_mtx, input, parallel);
365 tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
366 for (
int idx=r.begin(); idx<r.end(); ++idx) {
367 apply_to( parameters_mtx, inputs[idx], parallel );
390 tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
391 for (
int idx=r.begin(); idx<r.end(); ++idx) {
411 apply_to(parameters_mtx, input, parallel);
417 tbb::parallel_for( tbb::blocked_range<int>(0,static_cast<int>(inputs.size()),work_batch), [&](tbb::blocked_range<int> r) {
418 for (
int idx=r.begin(); idx<r.end(); ++idx) {
419 apply_to( parameters_mtx, inputs[idx], parallel );
436 std::stringstream sstream;
437 sstream <<
"Gate::apply_to: Wrong matrix size in Gate gate apply." 442 <<
" input.rows=" << input.
rows 443 <<
" input.cols=" << input.
cols << std::endl;
449 if (has_inplace_small_kernel(type)) {
450 static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
451 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
452 ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
453 GateKernelScratch& scratch = scratch_lease.get();
454 gate_kernel_to(empty_params, scratch.kernel);
455 apply_kernel_to(scratch.kernel, input,
false, parallel);
461 kernel = gate_kernel(empty_params);
463 apply_kernel_to(kernel, input,
false, parallel);
471 std::string err(
"Gate::apply_to(Matrix_float&): Wrong matrix size in gate apply.");
476 if (has_inplace_small_kernel(type)) {
477 static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
478 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
479 ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
480 GateKernelScratchFloat& scratch = scratch_lease.get();
481 gate_kernel_to(empty_params, scratch.kernel);
482 apply_kernel_to(scratch.kernel, input,
false, parallel);
488 kernel = gate_kernel(empty_params);
490 apply_kernel_to(kernel, input,
false, parallel);
504 std::stringstream sstream;
505 sstream <<
"Gate::apply_to(Matrix_real&, Matrix&): Wrong matrix size in gate apply." 510 <<
" input.rows=" << input.
rows 511 <<
" input.cols=" << input.
cols 513 <<
" provided_params=" << parameter_mtx.
size() << std::endl;
524 Matrix_real precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
525 apply_to_inner(parameter_mtx, precomputed_sincos, input, parallel);
533 std::string err(
"Gate::apply_to(Matrix_real_float&, Matrix_float&): Wrong matrix size in gate apply.");
543 Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
544 apply_to_inner(parameter_mtx, precomputed_sincos, input, parallel);
556 if (has_inplace_small_kernel(type)) {
557 static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
558 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
559 ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
560 GateKernelScratch& scratch = scratch_lease.get();
561 gate_kernel_to(precomputed_sincos, scratch.kernel);
562 inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
563 apply_kernel_to(scratch.kernel, input,
false, parallel, &scratch.inverse_kernel);
567 Matrix kernel = gate_kernel(precomputed_sincos);
568 Matrix inverse_kernel = inverse_gate_kernel(precomputed_sincos);
569 apply_kernel_to(kernel, input,
false, parallel, &inverse_kernel);
581 if (has_inplace_small_kernel(type)) {
582 static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
583 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
584 ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
585 GateKernelScratchFloat& scratch = scratch_lease.get();
586 gate_kernel_to(precomputed_sincos, scratch.kernel);
587 inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
588 apply_kernel_to(scratch.kernel, input,
false, parallel, &scratch.inverse_kernel);
593 Matrix_float inverse_kernel = inverse_gate_kernel(precomputed_sincos);
594 apply_kernel_to(kernel, input,
false, parallel, &inverse_kernel);
623 std::string err(
"Gate::apply_to(Matrix_real_any&, Matrix_any&): precision mismatch between parameters and input");
638 std::vector<Matrix> ret;
639 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, ret);
647 const int parameter_count = get_parameter_num();
648 if (parameter_count <= 0) {
655 const size_t required_size = offset +
static_cast<size_t>(parameter_count);
657 output.resize(required_size);
659 else if (output.size() < required_size) {
660 std::string err(
"Gate::apply_derivative_to_precomputed: output vector is too small.");
664 auto calculate_derivative = [&](
int param_idx,
Matrix&
result) {
665 if (has_inplace_small_kernel(type)) {
666 static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
667 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
668 ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
669 GateKernelScratch& scratch = scratch_lease.get();
670 derivative_kernel_to(precomputed_sincos, param_idx, scratch.kernel);
671 derivative_aux_kernel_to(precomputed_sincos, param_idx, scratch.aux_kernel);
673 if (scratch.aux_kernel.size() > 0) {
674 apply_kernel_to(scratch.kernel,
result,
true, parallel, &scratch.aux_kernel);
677 apply_kernel_to(scratch.kernel,
result,
true, parallel);
682 Matrix u3 = derivative_kernel(precomputed_sincos, param_idx);
683 Matrix u3_aux = derivative_aux_kernel(precomputed_sincos, param_idx);
686 if (u3_aux.
size() > 0) {
687 apply_kernel_to(u3,
result,
true, parallel, &u3_aux);
690 apply_kernel_to(u3,
result,
true, parallel);
694 if (parallel == 0 || parameter_count == 1) {
695 for (
int param_idx = 0; param_idx < parameter_count; ++param_idx) {
696 calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
700 tbb::parallel_for(tbb::blocked_range<int>(0, parameter_count, 1), [&](tbb::blocked_range<int> r) {
701 for (
int param_idx = r.begin(); param_idx < r.end(); ++param_idx) {
702 calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
709 std::vector<Matrix_float>
712 std::vector<Matrix_float> ret;
713 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, ret);
721 const int parameter_count = get_parameter_num();
722 if (parameter_count <= 0) {
729 const size_t required_size = offset +
static_cast<size_t>(parameter_count);
731 output.resize(required_size);
733 else if (output.size() < required_size) {
734 std::string err(
"Gate::apply_derivative_to_precomputed(Matrix_real_float): output vector is too small.");
739 if (has_inplace_small_kernel(type)) {
740 static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
741 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
742 ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
743 GateKernelScratchFloat& scratch = scratch_lease.get();
744 derivative_kernel_to(precomputed_sincos, param_idx, scratch.kernel);
745 derivative_aux_kernel_to(precomputed_sincos, param_idx, scratch.aux_kernel);
747 if (scratch.aux_kernel.size() > 0) {
748 apply_kernel_to(scratch.kernel,
result,
true, parallel, &scratch.aux_kernel);
751 apply_kernel_to(scratch.kernel,
result,
true, parallel);
756 Matrix_float u3 = derivative_kernel(precomputed_sincos, param_idx);
757 Matrix_float u3_aux = derivative_aux_kernel(precomputed_sincos, param_idx);
760 if (u3_aux.
size() > 0) {
761 apply_kernel_to(u3,
result,
true, parallel, &u3_aux);
764 apply_kernel_to(u3,
result,
true, parallel);
768 if (parallel == 0 || parameter_count == 1) {
769 for (
int param_idx = 0; param_idx < parameter_count; ++param_idx) {
770 calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
774 tbb::parallel_for(tbb::blocked_range<int>(0, parameter_count, 1), [&](tbb::blocked_range<int> r) {
775 for (
int param_idx = r.begin(); param_idx < r.end(); ++param_idx) {
776 calculate_derivative(param_idx, output[offset + static_cast<size_t>(param_idx)]);
786 Matrix_real precomputed_sincos = precompute_sincos(parameters_mtx_in);
787 return apply_derivative_to_precomputed(precomputed_sincos, input, parallel);
794 Matrix_real precomputed_sincos = precompute_sincos(parameters_mtx_in);
795 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output);
799 std::vector<Matrix_float>
803 return apply_derivative_to_precomputed(precomputed_sincos, input, parallel);
811 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output);
818 Matrix_real precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
819 return apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
826 Matrix_real precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
827 apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, output);
834 std::vector<Matrix> ret;
835 apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, ret);
843 const size_t output_size =
static_cast<size_t>(get_parameter_num()) + 1;
844 output.resize(output_size);
847 apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
848 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1,
false);
851 tbb::parallel_invoke(
854 apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
857 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1,
false);
864 std::vector<Matrix_float>
867 Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
868 return apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel);
875 Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameters_mtx_in);
876 apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, output);
880 std::vector<Matrix_float>
883 std::vector<Matrix_float> ret;
884 apply_to_combined_inner(parameters_mtx_in, precomputed_sincos, input, parallel, ret);
892 const size_t output_size =
static_cast<size_t>(get_parameter_num()) + 1;
893 output.resize(output_size);
896 apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
897 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1,
false);
900 tbb::parallel_invoke(
903 apply_to_inner(parameters_mtx_in, precomputed_sincos, output[0], parallel);
906 apply_derivative_to_precomputed(precomputed_sincos, input, parallel, output, 1,
false);
922 std::string err(
"Gate::apply_from_right(Matrix&): Wrong matrix size in gate apply.");
936 std::string err(
"Gate::apply_from_right(Matrix_float&): Wrong matrix size in gate apply.");
953 std::string err(
"Gate::apply_from_right(Matrix_real&, Matrix&): Wrong matrix size in gate apply.");
957 Matrix_real precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
958 apply_from_right_inner(parameter_mtx, precomputed_sincos, input);
967 std::string err(
"Gate::apply_from_right(Matrix_real_float&, Matrix_float&): Wrong matrix size in gate apply.");
971 Matrix_real_float precomputed_sincos = compute_precomputed_sincos(parameter_mtx);
972 apply_from_right_inner(parameter_mtx, precomputed_sincos, input);
979 if (has_inplace_small_kernel(type)) {
980 static tbb::enumerable_thread_specific<std::vector<GateKernelScratch>> scratch_tls;
981 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
982 ReentrantScratchLease<GateKernelScratch> scratch_lease(scratch_tls, depth_tls);
983 GateKernelScratch& scratch = scratch_lease.get();
984 gate_kernel_to(precomputed_sincos, scratch.kernel);
985 inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
990 Matrix kernel = gate_kernel(precomputed_sincos);
991 Matrix inv_kernel = inverse_gate_kernel(precomputed_sincos);
999 if (has_inplace_small_kernel(type)) {
1000 static tbb::enumerable_thread_specific<std::vector<GateKernelScratchFloat>> scratch_tls;
1001 static tbb::enumerable_thread_specific<int> depth_tls([](){
return 0; });
1002 ReentrantScratchLease<GateKernelScratchFloat> scratch_lease(scratch_tls, depth_tls);
1003 GateKernelScratchFloat& scratch = scratch_lease.get();
1004 gate_kernel_to(precomputed_sincos, scratch.kernel);
1005 inverse_gate_kernel_to(precomputed_sincos, scratch.inverse_kernel);
1011 Matrix_float inv_kernel = inverse_gate_kernel(precomputed_sincos);
1019 return precompute_sincos(parameters);
1026 return precompute_sincos(parameters);
1037 matrix_alloc = input;
1039 matrix_alloc_float_valid =
false;
1044 matrix_alloc_float = input;
1045 matrix_alloc_float_valid =
true;
1056 if ( control_qbit_in >=
qbit_num ) {
1057 std::string err(
"Gate::set_target_qbit: Wrong value of the control qbit: of out of the range given by qbit_num");
1064 if (control_qbits.empty()) {
1065 control_qbits.push_back(control_qbit_in);
1067 control_qbits[0] = control_qbit_in;
1078 if ( target_qbit_in >=
qbit_num ) {
1079 std::string err(
"Gate::set_target_qbit: Wrong value of the target qbit: out of the range given by qbit_num");
1086 if (target_qbits.empty()) {
1087 target_qbits.push_back(target_qbit_in);
1089 target_qbits[0] = target_qbit_in;
1099 for (
int cq : control_qbits_in) {
1101 std::stringstream sstream;
1102 sstream <<
"Gate::set_control_qbits: Control qubit index " << cq <<
" is out of range" << std::endl;
1104 throw sstream.str();
1108 control_qbits = control_qbits_in;
1109 control_qbit = control_qbits.empty() ? -1 : control_qbits[0];
1118 for (
int tq : target_qbits_in) {
1120 std::stringstream sstream;
1121 sstream <<
"Gate::set_target_qbits: Target qubit index " << tq <<
" is out of range" << std::endl;
1123 throw sstream.str();
1127 target_qbits = target_qbits_in;
1128 target_qbit = target_qbits.empty() ? -1 : target_qbits[0];
1136 return control_qbits;
1144 return target_qbits;
1154 if ((
int)qbit_list.size() !=
qbit_num ) {
1155 std::string err(
"Gate::reorder_qubits: Wrong number of qubits.");
1163 for (
int idx=0; idx<
qbit_num; idx++) {
1165 target_qbit_new = idx;
1168 control_qbit_new = idx;
1176 for (
size_t i = 0; i < control_qbits.size(); i++) {
1177 int old_qbit = control_qbits[i];
1178 for (
int idx = 0; idx <
qbit_num; idx++) {
1179 if (old_qbit == qbit_list[idx]) {
1180 control_qbits[i] = idx;
1187 for (
size_t i = 0; i < target_qbits.size(); i++) {
1188 int old_qbit = target_qbits[i];
1189 for (
int idx = 0; idx <
qbit_num; idx++) {
1190 if (old_qbit == qbit_list[idx]) {
1191 target_qbits[i] = idx;
1221 std::vector<int> involved_qbits;
1224 if (!target_qbits.empty()) {
1225 involved_qbits.insert(involved_qbits.end(), target_qbits.begin(), target_qbits.end());
1231 if (!control_qbits.empty()) {
1232 involved_qbits.insert(involved_qbits.end(), control_qbits.begin(), control_qbits.end());
1238 return involved_qbits;
1250 if ( std::count(parents.begin(), parents.end(), parent) > 0 ) {
1254 parents.push_back( parent );
1267 if ( std::count(children.begin(), children.end(), child) > 0 ) {
1271 children.push_back( child );
1354 if (matrix_alloc_float_valid) {
1373 if (param_idx < 0 || param_idx >= get_parameter_num() || precomputed_sincos.
cols < 2) {
1377 static tbb::enumerable_thread_specific<Matrix_real> shifted_tls;
1379 precomputed_sincos.
copy_to(shifted);
1380 const int offset = param_idx * shifted.
stride;
1381 const double sin_theta = shifted[offset + 0];
1382 const double cos_theta = shifted[offset + 1];
1385 shifted[offset + 0] = cos_theta;
1386 shifted[offset + 1] = -sin_theta;
1388 return gate_kernel(shifted);
1395 if (param_idx < 0 || param_idx >= get_parameter_num() || precomputed_sincos.
cols < 2) {
1399 static tbb::enumerable_thread_specific<Matrix_real_float> shifted_tls;
1401 precomputed_sincos.
copy_to(shifted);
1402 const int offset = param_idx * shifted.
stride;
1403 const float sin_theta = shifted[offset + 0];
1404 const float cos_theta = shifted[offset + 1];
1407 shifted[offset + 0] = cos_theta;
1408 shifted[offset + 1] = -sin_theta;
1410 return gate_kernel(shifted);
1416 (void)precomputed_sincos;
1424 (void)precomputed_sincos;
1438 std::string err(name +
"::precompute_sincos(double): not enough parameters supplied.");
1457 std::string err(name +
"::precompute_sincos(float): not enough parameters supplied.");
1584 if (matrix_alloc_float_valid && matrix_alloc.rows == 0 && matrix_alloc_float.rows > 0) {
1585 matrix_alloc = matrix_alloc_float.to_float64();
1587 const std::vector<int> involved_qbits = get_involved_qubits();
1588 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
1589 const bool is_state_vector = (input.
cols == 1);
1590 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
1592 const bool has_local_matrix = involved_qbit_num > 0
1593 && matrix_alloc.rows ==
Power_of_2(involved_qbit_num)
1594 && matrix_alloc.cols ==
Power_of_2(involved_qbit_num);
1595 const bool can_use_large_kernel = !has_any_control
1596 && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1597 || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1599 if (has_full_matrix) {
1600 if (can_use_large_kernel) {
1624 Matrix transformed =
dot(matrix_alloc, input);
1630 if (has_local_matrix && matrix_alloc.rows == 2 && matrix_alloc.cols == 2 && involved_qbit_num == 1) {
1631 u3_1qbit = matrix_alloc;
1633 else if (has_local_matrix && can_use_large_kernel) {
1658 std::string err(
"Gate::apply_kernel_to(Matrix&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
1663 if (u3_1qbit.
rows != 2 || u3_1qbit.
cols != 2) {
1664 const std::vector<int> involved_qbits = get_involved_qubits();
1665 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
1666 const bool is_state_vector = (input.
cols == 1);
1667 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
1668 const bool can_use_large_kernel = !has_any_control
1669 && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1670 || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1672 if (can_use_large_kernel) {
1698 Matrix transformed =
dot(u3_1qbit, input);
1703 std::string err(
"Gate::apply_kernel_to(Matrix&): unsupported non-2x2 kernel dispatch for this configuration.");
1714 else if ( input.
cols == 1 ) {
1715 if ( parallel == 1 ) {
1718 else if ( parallel == 2 ) {
1722 std::string err(
"Gate::apply_kernel_to: the argument parallel should be either 0,1 or 2. Set 0 for sequential execution (default), 1 for parallel execution with OpenMP and 2 for parallel with TBB");
1735 else if (
qbit_num < 10 || !parallel) {
1752 else if ( input.
cols == 1 ) {
1865 if (input.
cols != 1) {
1866 for (
int col_idx = 0; col_idx < input.
cols; ++col_idx) {
1868 for (
int row_idx = 0; row_idx < input.
rows; ++row_idx) {
1869 col_mtx[row_idx * col_mtx.
stride] = input[row_idx * input.
stride + col_idx];
1884 for (
int row_idx = 0; row_idx < input.
rows; ++row_idx) {
1885 input[row_idx * input.
stride + col_idx] = col_mtx[row_idx * col_mtx.
stride];
1903 const std::vector<int> involved_qbits = get_involved_qubits();
1904 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
1905 const bool is_state_vector = (input.
cols == 1);
1906 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
1908 if (matrix_alloc_float_valid) {
1909 matrix_alloc32 = matrix_alloc_float;
1912 matrix_alloc32 = matrix_alloc.to_float32();
1915 const bool has_local_matrix = involved_qbit_num > 0
1918 const bool can_use_large_kernel = !has_any_control
1919 && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1920 || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1922 if (has_full_matrix) {
1923 if (can_use_large_kernel) {
1949 if (has_local_matrix && matrix_alloc32.
rows == 2 && matrix_alloc32.
cols == 2 && involved_qbit_num == 1) {
1950 u3_1qbit = matrix_alloc32;
1952 else if (has_local_matrix && can_use_large_kernel) {
1973 std::string err(
"Gate::apply_kernel_to(Matrix_float&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
1978 if (u3_1qbit.
rows != 2 || u3_1qbit.
cols != 2) {
1979 const std::vector<int> involved_qbits = get_involved_qubits();
1980 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
1981 const bool is_state_vector = (input.
cols == 1);
1982 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
1983 const bool can_use_large_kernel = !has_any_control
1984 && ((is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5)
1985 || (!is_state_vector && involved_qbit_num >= 2 && involved_qbit_num <= 5));
1987 if (can_use_large_kernel) {
2014 std::string err(
"Gate::apply_kernel_to(Matrix_float&): unsupported non-2x2 kernel dispatch for this configuration.");
2025 else if ( input.
cols == 1 ) {
2026 if ( parallel == 1 ) {
2029 else if ( parallel == 2 ) {
2033 std::string err(
"Gate::apply_kernel_to(Matrix_float&): the argument parallel should be either 0,1 or 2.");
2044 else if (
qbit_num < 10 || !parallel) {
2056 if ( input.
cols == 1 && (
qbit_num < 10 || !parallel) ) {
2060 else if ( input.
cols == 1 ) {
2152 if (matrix_alloc_float_valid && matrix_alloc.rows == 0 && matrix_alloc_float.rows > 0) {
2153 matrix_alloc = matrix_alloc_float.to_float64();
2156 const std::vector<int> involved_qbits = get_involved_qubits();
2157 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
2158 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
2160 const bool has_local_matrix = involved_qbit_num > 0
2161 && matrix_alloc.rows ==
Power_of_2(involved_qbit_num)
2162 && matrix_alloc.cols ==
Power_of_2(involved_qbit_num);
2163 const bool can_use_large_kernel_from_right = !has_any_control
2165 && involved_qbit_num >= 2
2166 && involved_qbit_num <= 5;
2168 if (has_full_matrix) {
2169 if (can_use_large_kernel_from_right) {
2171 if (involved_qbit_num == 2 &&
qbit_num < 10) {
2174 else if (involved_qbit_num == 2) {
2185 Matrix transformed =
dot(input, matrix_alloc);
2191 if (has_local_matrix && matrix_alloc.rows == 2 && matrix_alloc.cols == 2 && involved_qbit_num == 1) {
2192 u3_1qbit = matrix_alloc;
2194 else if (has_local_matrix && can_use_large_kernel_from_right) {
2196 if (involved_qbit_num == 2 &&
qbit_num < 10) {
2199 else if (involved_qbit_num == 2) {
2211 std::string err(
"Gate::apply_kernel_from_right(Matrix&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
2216 if (u3_1qbit.
rows == 0 || u3_1qbit.
cols == 0) {
2225 const std::vector<int> involved_qbits = get_involved_qubits();
2226 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
2227 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
2228 const bool can_use_large_kernel_from_right = !has_any_control
2230 && involved_qbit_num >= 2
2231 && involved_qbit_num <= 5;
2233 if (u3_1qbit.
rows != 2 || u3_1qbit.
cols != 2) {
2234 if (can_use_large_kernel_from_right) {
2236 if (involved_qbit_num == 2 &&
qbit_num < 10) {
2239 else if (involved_qbit_num == 2) {
2252 Matrix transformed =
dot(input, u3_1qbit);
2260 if (alt_kernel !=
nullptr) {
2262 apply_kernel_to(kernel_copy, gate_matrix,
false, 0, &alt_copy);
2264 apply_kernel_to(kernel_copy, gate_matrix,
false, 0);
2359 const std::vector<int> involved_qbits = get_involved_qubits();
2360 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
2361 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
2363 if (matrix_alloc_float_valid) {
2364 matrix_alloc32 = matrix_alloc_float;
2367 matrix_alloc32 = matrix_alloc.to_float32();
2370 const bool has_local_matrix = involved_qbit_num > 0
2373 const bool can_use_large_kernel_from_right = !has_any_control
2375 && involved_qbit_num >= 2
2376 && involved_qbit_num <= 5;
2378 if (has_full_matrix) {
2379 if (can_use_large_kernel_from_right) {
2381 if (involved_qbit_num == 2 &&
qbit_num < 10) {
2384 else if (involved_qbit_num == 2) {
2401 if (has_local_matrix && matrix_alloc32.
rows == 2 && matrix_alloc32.
cols == 2 && involved_qbit_num == 1) {
2402 u3_1qbit = matrix_alloc32;
2404 else if (has_local_matrix && can_use_large_kernel_from_right) {
2406 if (involved_qbit_num == 2 &&
qbit_num < 10) {
2409 else if (involved_qbit_num == 2) {
2421 std::string err(
"Gate::apply_kernel_from_right(Matrix_float&): unsupported GENERAL_OPERATION dispatch for stored matrix size.");
2426 if (u3_1qbit.
rows == 0 || u3_1qbit.
cols == 0) {
2435 const std::vector<int> involved_qbits = get_involved_qubits();
2436 const int involved_qbit_num =
static_cast<int>(involved_qbits.size());
2437 const bool has_any_control = (
control_qbit >= 0) || !control_qbits.empty();
2438 const bool can_use_large_kernel_from_right = !has_any_control
2440 && involved_qbit_num >= 2
2441 && involved_qbit_num <= 5;
2443 if (u3_1qbit.
rows != 2 || u3_1qbit.
cols != 2) {
2444 if (can_use_large_kernel_from_right) {
2446 if (involved_qbit_num == 2 &&
qbit_num < 10) {
2449 else if (involved_qbit_num == 2) {
2470 if (alt_kernel !=
nullptr) {
2472 apply_kernel_to(kernel_copy, gate_matrix,
false, 0, &alt_copy);
2474 apply_kernel_to(kernel_copy, gate_matrix,
false, 0);
2502 parameter_start_idx = start_idx;
2525 children = children_;
2537 return parameter_start_idx;
2547 std::string err(name +
"::gate_kernel(double) not implemented");
2554 std::string err(name +
"::gate_kernel(float) not implemented");
2561 Matrix fwd = gate_kernel(precomputed_sincos);
2563 for (
int row_idx = 0; row_idx < fwd.
rows; ++row_idx) {
2564 const int row_offset = row_idx * fwd.
stride;
2565 for (
int col_idx = 0; col_idx < fwd.
cols; ++col_idx) {
2579 for (
int row_idx = 0; row_idx < fwd.
rows; ++row_idx) {
2580 const int row_offset = row_idx * fwd.
stride;
2581 for (
int col_idx = 0; col_idx < fwd.
cols; ++col_idx) {
2594 output = gate_kernel(precomputed_sincos);
2600 output = gate_kernel(precomputed_sincos);
2606 output = inverse_gate_kernel(precomputed_sincos);
2612 output = inverse_gate_kernel(precomputed_sincos);
2618 output = derivative_kernel(precomputed_sincos, param_idx);
2624 output = derivative_kernel(precomputed_sincos, param_idx);
2630 output = derivative_aux_kernel(precomputed_sincos, param_idx);
2636 output = derivative_aux_kernel(precomputed_sincos, param_idx);
2651 double sin_theta, cos_theta;
2652 double sin_phi, cos_phi;
2653 double sin_lambda, cos_lambda;
2657 return calc_one_qubit_u3_from_trig<Matrix, double>(sin_theta, cos_theta, sin_phi, cos_phi, sin_lambda, cos_lambda);
2663 float sin_theta, cos_theta;
2664 float sin_phi, cos_phi;
2665 float sin_lambda, cos_lambda;
2669 return calc_one_qubit_u3_from_trig<Matrix_float, float>(sin_theta, cos_theta, sin_phi, cos_phi, sin_lambda, cos_lambda);
2681 const std::vector<double> mults = get_parameter_multipliers();
2683 if (mults.empty()) {
2687 const int n =
static_cast<int>(mults.size());
2688 if ( get_parameter_start_idx() + n > (
int)parameters.
size() ) {
2689 std::string err(name +
"::extract_parameters: Can't extract parameters, input array has not enough elements.");
2694 const int start = get_parameter_start_idx();
2695 for (
int i = 0; i <
n; ++i) {
2696 const double m = mults[i];
2697 extracted_parameters[i] = std::fmod(m * parameters[start + i], m * 2.0 *
M_PI);
2700 return extracted_parameters;
2712 const std::vector<double> mults = get_parameter_multipliers();
2714 if (mults.empty()) {
2718 const int n =
static_cast<int>(mults.size());
2719 if ( get_parameter_start_idx() + n > (
int)parameters.
size() ) {
2720 std::string err(name +
"::extract_parameters: Can't extract parameters, input array has not enough elements.");
2725 const int start = get_parameter_start_idx();
2726 for (
int i = 0; i <
n; ++i) {
2727 const float m =
static_cast<float>(mults[i]);
2728 extracted_parameters[i] = std::fmod(m * parameters[start + i], m * static_cast<float>(2.0 *
M_PI));
2731 return extracted_parameters;
void qgd_sincos< float >(float x, float *s, float *c)
Matrix_real compute_precomputed_sincos(const Matrix_real ¶meters) const
Public wrapper to precompute gate-local sin/cos pairs.
Matrix dot(Matrix &A, Matrix &B)
Call to calculate the product of two complex matrices by calling method zgemm3m from the CBLAS librar...
Gate()
Default constructor of the class.
virtual Matrix inverse_gate_kernel(const Matrix_real &precomputed_sincos)
parameter_num
[set adaptive gate structure]
Header file for a class for the representation of general gate operations.
void set_control_qbits(const std::vector< int > &control_qbits_in)
Call to set the control qubits for the gate operation.
Class to store single-precision real arrays and properties.
void qgd_sincos< double >(double x, double *s, double *c)
virtual void gate_kernel_to(const Matrix_real &precomputed_sincos, Matrix &output)
virtual Matrix derivative_aux_kernel(const Matrix_real &precomputed_sincos, int param_idx)
void add_child(Gate *child)
Call to add a child gate to the current gate.
virtual Matrix get_matrix()
Retrieve the gate matrix for zero-parameter gates (no parameters).
void clear_children()
Call to erase data on children.
def apply_to(self, parameters_mtx, state_to_be_transformed)
virtual std::vector< Matrix > apply_derivate_to(Matrix_real ¶meters_mtx_in, Matrix &input, int parallel)
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...
virtual Gate * clone()
Call to create a clone of the present class.
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)
void set_children(std::vector< Gate *> &children_)
Call to set the children of the current gate.
void apply_kernel_from_right(Matrix &u3_1qbit, Matrix &input, const Matrix *alt_kernel=nullptr)
Call to apply the gate kernel on the input state or unitary from right (no AVX support) ...
virtual void set_qbit_num(int qbit_num_in)
Set the number of qubits spanning the matrix of the operation.
Structure type representing single-precision complex numbers.
virtual ~Gate()
Destructor of the class.
virtual std::vector< Matrix > apply_to_combined_inner(Matrix_real ¶meters_mtx_in, const Matrix_real &precomputed_sincos, Matrix &input, int parallel)
Internal combined forward+derivative entry with precomputed sin/cos.
scalar * get_data() const
Call to get the pointer to the stored data.
static Matrix calc_one_qubit_u3(double ThetaOver2=0.0, double Phi=0.0, double Lambda=0.0)
Build a 2x2 U3 kernel from angles (theta/2, phi, lambda).
Non-owning carrier that references either Matrix (complex128) or Matrix_float (complex64) without dat...
Non-owning carrier that can reference either Matrix_real or Matrix_real_float.
virtual void apply_from_right_inner(Matrix_real ¶meter_mtx, const Matrix_real &precomputed_sincos, Matrix &input)
Internal right-apply entry that consumes already precomputed sin/cos values.
virtual void apply_to_inner(Matrix_real ¶meter_mtx, const Matrix_real &precomputed_sincos, Matrix &input, int parallel)
Internal apply entry that consumes already precomputed sin/cos values.
virtual void apply_from_right(Matrix &input)
Call to apply the gate on the input array/matrix by input*Gate.
std::vector< int > get_control_qbits() const
Call to get the vector of control qubits.
int rows
The number of rows.
int cols
The number of columns.
virtual void derivative_kernel_to(const Matrix_real &precomputed_sincos, int param_idx, Matrix &output)
gate_type get_type()
Call to get the type of the operation.
Matrix_float & as_float32()
Matrix_real & as_float64()
virtual void inverse_gate_kernel_to(const Matrix_real &precomputed_sincos, Matrix &output)
void set_control_qbit(int control_qbit_in)
Call to set the control qubit for the gate operation.
void set_parameter_start_idx(int start_idx)
Call to set the starting index of the parameters in the parameter array corresponding to the circuit ...
std::vector< Gate * > get_parents()
Call to get the parents of the current gate.
int get_parameter_start_idx()
Call to get the starting index of the parameters in the parameter array corresponding to the circuit ...
void add_parent(Gate *parent)
Call to add a parent gate to the current gate.
void qgd_sincos_batch< double >(const double *input, double *output, std::size_t count, int stride)
void set_target_qbit(int target_qbit_in)
Call to set the target qubit for the gate operation.
Structure type representing complex numbers in the SQUANDER package.
virtual Matrix_real extract_parameters(Matrix_real ¶meters)
Call to extract parameters from the parameter array corresponding to the circuit, in which the gate i...
Matrix copy() const
Call to create a copy of the matrix.
void clear_parents()
Call to erase data on parents.
void copy_to(Matrix_real &target) const
Copy the matrix to a reusable double-precision target matrix.
void set_matrix(Matrix input)
Call to set the stored matrix in the operation.
std::vector< Gate * > get_children()
Call to get the children of the current gate.
int Power_of_2(int n)
Calculates the n-th power of 2.
std::vector< int > get_target_qbits() const
Call to get the vector of target qubits.
Double-precision complex matrix (float64).
void copy_to(matrix_base< scalar > &target) const
Copy the current matrix storage into a reusable target matrix.
int size() const
Call to get the number of the allocated elements.
virtual int get_parameter_num()
Call to get the number of free parameters.
virtual Matrix gate_kernel(const Matrix_real &precomputed_sincos)
Compute the gate kernel matrix from precomputed trigonometric values.
Single-precision complex matrix (float32).
virtual std::vector< Matrix > apply_to_combined(Matrix_real ¶meters_mtx_in, Matrix &input, int parallel)
Combined forward + derivative application with shared precomputed trig cache.
int get_target_qbit()
Call to get the index of the target qubit.
Base class for the representation of general gate operations.
void set_target_qbits(const std::vector< int > &target_qbits_in)
Call to set the target qubits for the gate operation.
std::string get_name()
Call to get the name label of the gate.
void apply_kernel_to(Matrix &u3_1qbit, Matrix &input, bool deriv=false, int parallel=0, const Matrix *alt_kernel=nullptr)
Call to apply the gate kernel on the input state or unitary with optional AVX support.
Matrix create_identity(int matrix_size)
Call to create an identity matrix.
virtual Matrix derivative_kernel(const Matrix_real &precomputed_sincos, int param_idx)
std::vector< Matrix > apply_derivative_to_precomputed(const Matrix_real &precomputed_sincos, Matrix &input, int parallel)
Call to evaluate the derivate of the circuit on an inout with respect to all of the free parameters...
Header file for commonly used functions and wrappers to CBLAS functions.
virtual void apply_to_list(std::vector< Matrix > &inputs, int parallel)
Call to apply the gate on a list of inputs.
Matrix_real_float & as_float32()
void set_parents(std::vector< Gate *> &parents_)
Call to set the parents of the current gate.
double real
the real part of a complex number
virtual void derivative_aux_kernel_to(const Matrix_real &precomputed_sincos, int param_idx, Matrix &output)
Matrix_real precompute_sincos(const Matrix_real ¶meters) const
Precompute sin/cos pairs for each gate-local parameter.
int get_qbit_num()
Call to get the number of qubits composing the unitary.
gate_type
Type definition of operation types (also generalized for decomposition classes derived from the class...
virtual void apply_to(Matrix &input, int parallel)
Call to apply the gate on the input array/matrix.
virtual std::vector< double > get_parameter_multipliers() const
Returns the per-parameter multipliers relative to 2Ï used by extract_parameters. ...
void qgd_sincos_batch< float >(const float *input, float *output, std::size_t count, int stride)
virtual std::vector< int > get_involved_qubits(bool only_target=false)
Call to get the qubits involved in the gate operation.
virtual void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operation.
Matrix_float copy() const
Call to create a copy of the matrix.
int get_control_qbit()
Call to get the index of the control qubit.
Class to store data of complex arrays and its properties.
double imag
the imaginary part of a complex number
Matrix_float create_identity_float(int matrix_size)
Call to create a single-precision complex identity matrix.