QCOR
integer_arithmetic.hpp
1 #pragma once
2 #include <qcor_qft>
3 
4 namespace qcor {
5 namespace internal {
6 // Generates a list of angles to perform addition by a in the Fourier space.
7 void genAngles(std::vector<double> &io_angles, int a, int nbQubits) {
8  // Makes sure the vector appropriately sized
9  io_angles.resize(nbQubits);
10  std::fill(io_angles.begin(), io_angles.end(), 0.0);
11 
12  for (int i = 0; i < nbQubits; ++i) {
13  int bitIdx = nbQubits - i - 1;
14  auto &angle = io_angles[bitIdx];
15  for (int j = i; j < nbQubits; ++j) {
16  // Check bit
17  int bitShift = nbQubits - j - 1;
18  if (((1 << bitShift) & a) != 0) {
19  angle += std::pow(2.0, -(j - i));
20  }
21  }
22  angle *= M_PI;
23  }
24 }
25 
26 inline void calcPowMultMod(int &result, int i, int a, int N) {
27  result = (1 << i) * a % N;
28 }
29 
30 // Compute extended greatest common divisor of two integers
31 inline std::tuple<int, int, int> egcd(int a, int b) {
32  int m, n, c, q, r;
33  int m1 = 0, m2 = 1, n1 = 1, n2 = 0;
34 
35  while (b) {
36  q = a / b, r = a - q * b;
37  m = m2 - q * m1, n = n2 - q * n1;
38  a = b, b = r;
39  m2 = m1, m1 = m, n2 = n1, n1 = n;
40  }
41 
42  c = a, m = m2, n = n2;
43 
44  // correct the signs
45  if (c < 0) {
46  m = -m;
47  n = -n;
48  c = -c;
49  }
50 
51  return std::make_tuple(m, n, c);
52 }
53 
54 // Modular inverse of a mod p
55 inline void modinv(int &result, int a, int p) {
56  int x, y;
57  int gcd_ap;
58  std::tie(x, y, gcd_ap) = egcd(p, a);
59  result = (y > 0) ? y : y + p;
60 }
61 } // namespace internal
62 } // namespace qcor
63 
64 // Majority gate
65 __qpu__ void majority(qubit a, qubit b, qubit c) {
66  X::ctrl(c, b);
67  X::ctrl(c, a);
68  X::ctrl({b, a}, c);
69 }
70 
71 // Note: this is not the adjoint of majority....
72 // Could be a good use case for per-qubit uncompute...
73 __qpu__ void unmajority(qubit a, qubit b, qubit c) {
74  X::ctrl({b, a}, c);
75  X::ctrl(c, a);
76  X::ctrl(a, b);
77 }
78 
79 // Add a to b and save to b
80 // c_in and c_out are carry bits (in and out)
81 __qpu__ void ripple_add(qreg a, qreg b, qubit c_in, qubit c_out) {
82  majority(c_in, b[0], a[0]);
83  for (auto j : range(a.size() - 1)) {
84  majority(a[j], b[j + 1], a[j + 1]);
85  }
86 
87  X::ctrl(a.tail(), c_out);
88 
89  for (int j = a.size() - 2; j >= 0; --j) {
90  unmajority(a[j], b[j + 1], a[j + 1]);
91  }
92  unmajority(c_in, b[0], a[0]);
93 }
94 
95 // Init a qubit register in an integer value
96 __qpu__ void integer_init(qreg a, int val) {
97  Reset(a);
98  for (auto i : range(a.size())) {
99  if (val & (1 << i)) {
100  X(a[i]);
101  }
102  }
103 }
104 
105 // Add an integer to the phase (Fourier basis)
106 __qpu__ void phase_add_integer(qreg q, int a) {
107  std::vector<double> angles;
108  qcor::internal::genAngles(angles, a, q.size());
109  for (int i = 0; i < q.size(); ++i) {
110  U1(q[i], angles[i]);
111  }
112 }
113 
114 // Add an integer to the a qubit register:
115 // |a> --> |a + b>
116 __qpu__ void add_integer(qreg q, int a) {
117  // Bring it to Fourier basis
118  // (IQFT automatically)
119  compute { qft_opt_swap(q, 0); }
120  action { phase_add_integer(q, a); }
121 }
122 
123 // + a mod N in phase space
124 // need an additional ancilla qubit since we don't do allocation
125 // inside a kernel.
126 // See Fig. 5 of https://arxiv.org/pdf/quant-ph/0205095.pdf
127 __qpu__ void phase_add_integer_mod(qreg q, qubit anc, int a, int N) {
128  Reset(anc);
129  phase_add_integer(q, a);
130  phase_add_integer::adjoint(q, N);
131  qft_opt_swap::adjoint(q, 0);
132  X::ctrl(q.tail(), anc);
133  qft_opt_swap(q, 0);
134  phase_add_integer::ctrl(anc, q, N);
135  phase_add_integer::adjoint(q, a);
136  qft_opt_swap::adjoint(q, 0);
137  X(q.tail());
138  X::ctrl(q.tail(), anc);
139  X(q.tail());
140  qft_opt_swap(q, 0);
141  phase_add_integer(q, a);
142 }
143 
144 // Add an integer a mod N to the a qubit register:
145 // |q> --> |q + a mod N>
146 // TODO: ability to create scratch qubits
147 __qpu__ void add_integer_mod_impl(qreg q, qubit anc, int a, int N) {
148  // Bring it to Fourier basis
149  // (IQFT automatically)
150  compute { qft_opt_swap(q, 0); }
151  action { phase_add_integer_mod(q, anc, a, N); }
152 }
153 
154 __qpu__ void add_integer_mod(qreg q, int a, int N) {
155  auto anc = qalloc(1);
156  add_integer_mod_impl(q, anc[0], a, N);
157 }
158 
159 // Modular multiply in phase basis:
160 // See Fig. 6 of https://arxiv.org/pdf/quant-ph/0205095.pdf
161 // |x>|b> ==> |x> |b + ax mod N>
162 // i.e. if b == 0 ==> the result |ax mod N> is stored in b
163 __qpu__ void phase_mul_integer_mod(qreg x, qreg b, qubit anc, int a, int N) {
164  for (int i = 0; i < x.size(); ++i) {
165  // add operand = 2^i * a
166  int operand;
167  qcor::internal::calcPowMultMod(operand, i, a, N);
168  phase_add_integer_mod::ctrl(x[i], b, anc, operand, N);
169  }
170 }
171 
172 __qpu__ void mul_integer_mod_impl(qreg x, qreg b, qubit anc, int a, int N) {
173  // Bring it to Fourier basis
174  // (IQFT automatically)
175  compute { qft_opt_swap(b, 0); }
176  action { phase_mul_integer_mod(x, b, anc, a, N); }
177 }
178 
179 __qpu__ void mul_integer_mod(qreg x, qreg b, int a, int N) {
180  auto anc = qalloc(1);
181  mul_integer_mod_impl(x, b, anc[0], a, N);
182 }
183 
184 // Modular multiply in-place:
185 // See Fig. 7 (https://arxiv.org/pdf/quant-ph/0205095.pdf)
186 // |x>|0> ==> |ax mod N>|0>
187 // x and aux_reg must have the same size.
188 __qpu__ void mul_integer_mod_in_place_impl(qreg x, qreg aux_reg, qubit anc, int a, int N) {
189  Reset(aux_reg);
190  mul_integer_mod_impl(x, aux_reg, anc, a, N);
191  // Swap the result to x register
192  for (int i = 0; i < x.size(); ++i) {
193  Swap(x[i], aux_reg[i]);
194  }
195 
196  int aInv = 0;
197  qcor::internal::modinv(aInv, a, N);
198  // Apply modular multiply of 1/a
199  mul_integer_mod_impl::adjoint(x, aux_reg, anc, aInv, N);
200 }
201 
202 // |x> ==> |ax mod N> in-place
203 __qpu__ void mul_integer_mod_in_place(qreg x, int a, int N) {
204  auto anc_reg = qalloc(1);
205  mul_integer_mod_in_place_impl(x, qalloc(x.size() + 1), anc_reg[0], a, N);
206 }
qcor
Definition: qcor_syntax_handler.cpp:15