QCOR
qft.hpp
1 // Example code structure whereby quantum kernels are defined
2 // in separate header files which can be included into a cpp file
3 // which is compiled by QCOR.
4 #pragma once
5 
6 #include "qalloc.hpp"
7 
8 // Generic QFT and IQFT algorithms
9 // Input:
10 // (1) Qubit register (type qreg)
11 // (2) The start qubit index and the number of qubits (type integer)
12 // Note: these two parameters allow us to operate the QFT/IQFT on
13 // a contiguous subset of qubits of the register.
14 // If we want to use the entire register,
15 // just pass 0 and number of qubits in the register, respectively.
16 // (3) shouldSwap flag (as integer):
17 // If 0, no Swap gates will be added.
18 // Otherwise, Swap gates are added at the end (QFT) and beginning (IQFT).
19 __qpu__ void qft_opt_swap(qreg q, int shouldSwap) {
20  int startIdx = 0;
21  int nbQubits = q.size();
22  for (int qIdx = nbQubits - 1; qIdx >= 0; --qIdx) {
23  auto shiftedBitIdx = qIdx + startIdx;
24  H(q[shiftedBitIdx]);
25  for (int j = qIdx - 1; j >= 0; --j) {
26  const double theta = M_PI/std::pow(2.0, qIdx - j);
27  auto targetIdx = j + startIdx;
28  CPhase(q[shiftedBitIdx], q[targetIdx], theta);
29  }
30  }
31 
32  // A *hacky* way to do conditional (convert to a for loop)
33  int swapCount = (shouldSwap == 0) ? 0 : 1;
34  for (int count = 0; count < swapCount; ++count) {
35  for (int qIdx = 0; qIdx < nbQubits/2; ++qIdx) {
36  Swap(q[startIdx + qIdx], q[startIdx + nbQubits - qIdx - 1]);
37  }
38  }
39 }
40 
41 __qpu__ void qft_range_opt_swap(qreg q, int startIdx, int nbQubits, int shouldSwap) {
42  for (int qIdx = nbQubits - 1; qIdx >= 0; --qIdx) {
43  auto shiftedBitIdx = qIdx + startIdx;
44  H(q[shiftedBitIdx]);
45  for (int j = qIdx - 1; j >= 0; --j) {
46  const double theta = M_PI/std::pow(2.0, qIdx - j);
47  auto targetIdx = j + startIdx;
48  CPhase(q[shiftedBitIdx], q[targetIdx], theta);
49  }
50  }
51 
52  // A *hacky* way to do conditional (convert to a for loop)
53  int swapCount = (shouldSwap == 0) ? 0 : 1;
54  for (int count = 0; count < swapCount; ++count) {
55  for (int qIdx = 0; qIdx < nbQubits/2; ++qIdx) {
56  Swap(q[startIdx + qIdx], q[startIdx + nbQubits - qIdx - 1]);
57  }
58  }
59 }
60 
61 __qpu__ void qft(qreg q) {
62  qft_opt_swap(q, 1);
63 }
64 
65 __qpu__ void iqft_opt_swap(qreg q, int shouldSwap) {
66  int startIdx = 0;
67  int nbQubits = q.size();
68  int swapCount = (shouldSwap == 0) ? 0 : 1;
69  for (int count = 0; count < swapCount; ++count) {
70  // Swap qubits
71  for (int qIdx = 0; qIdx < nbQubits/2; ++qIdx) {
72  Swap(q[startIdx + qIdx], q[startIdx + nbQubits - qIdx - 1]);
73  }
74  }
75 
76  for (int qIdx = 0; qIdx < nbQubits - 1; ++qIdx) {
77  H(q[startIdx + qIdx]);
78  int j = qIdx + 1;
79  for (int y = qIdx; y >= 0; --y) {
80  const double theta = -M_PI/std::pow(2.0, j - y);
81  CPhase(q[startIdx + j], q[startIdx + y], theta);
82  }
83  }
84 
85  H(q[startIdx + nbQubits - 1]);
86 }
87 
88 __qpu__ void iqft_range_opt_swap(qreg q, int startIdx, int nbQubits, int shouldSwap) {
89  int swapCount = (shouldSwap == 0) ? 0 : 1;
90  for (int count = 0; count < swapCount; ++count) {
91  // Swap qubits
92  for (int qIdx = 0; qIdx < nbQubits/2; ++qIdx) {
93  Swap(q[startIdx + qIdx], q[startIdx + nbQubits - qIdx - 1]);
94  }
95  }
96 
97  for (int qIdx = 0; qIdx < nbQubits - 1; ++qIdx) {
98  H(q[startIdx + qIdx]);
99  int j = qIdx + 1;
100  for (int y = qIdx; y >= 0; --y) {
101  const double theta = -M_PI/std::pow(2.0, j - y);
102  CPhase(q[startIdx + j], q[startIdx + y], theta);
103  }
104  }
105 
106  H(q[startIdx + nbQubits - 1]);
107 }
108 
109 __qpu__ void iqft(qreg q) {
110  iqft_opt_swap(q, 1);
111 }