Skip to content

Commit 26071f2

Browse files
committed
UPDATE:
1. added new function `no_of_qubits()` 2. removed redundant template usage all over the code
1 parent 21c1bcd commit 26071f2

File tree

1 file changed

+44
-38
lines changed

1 file changed

+44
-38
lines changed

qubitverse/simulator/gates/gates.hh

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ namespace simulator
4444
complex matrix[2][2]; // 2x2 matrix that stores various types of gates
4545
};
4646

47-
static inline const constexpr qgate_2x2 pre_defined_qgates[7] = {
47+
static constexpr qgate_2x2 pre_defined_qgates[7] = {
4848
{IDENTITY, {{1, 0}, {0, 1}}},
4949
{PAULI_X, {{0, 1}, {1, 0}}},
5050
{PAULI_Y, {{0, {0, -1}}, {{0, 1}, 0}}},
@@ -55,13 +55,13 @@ namespace simulator
5555
};
5656

5757
static void apply_predefined_gate(complex (&__s)[1 << n_qubits], const gate_type &__g_type, const std::size_t &qubit_target);
58-
static qgate_2x2 &get_theta_gate(qgate_2x2 &__g, const enum gate_type &__g_type, const double &__theta);
58+
static qgate_2x2 &get_theta_gate(qgate_2x2 &__g, const gate_type &__g_type, const double &__theta);
5959
static void apply_theta_gate(complex (&__s)[1 << n_qubits], const gate_type &__g_type, const double &__theta, const std::size_t &qubit_target);
6060
static void apply_2qubit_gate(complex (&__s)[1 << n_qubits], const gate_type &__g_type, const std::size_t &q_control, const std::size_t &q_target);
6161

6262
private:
6363
// a vector-space (hilbert-space) defined over complex numbers C
64-
// 1 << n_qubit translates to 2^N, where N is the the number of qubit the hilbert-space(quantum-system) supports
64+
// 1 << n_qubit translates to 2^N, where N is the number of qubit the hilbert-space(quantum-system) supports
6565
// memory consumption on x86_64 architecture for N-qubit system is: f(N) = 16 * 2^abs(N) bytes, that is exponential growth
6666
// Initially, the hilbert-space is defined as 1 + 0i, 0 + 0i, 0 + 0i, 0 + 0i, 0 + 0i, ..., 0 + 0i
6767
complex M_qubits[1 << n_qubits] = {};
@@ -85,16 +85,17 @@ namespace simulator
8585
const complex (&get_qubits() const)[1 << n_qubits];
8686
const constexpr std::size_t get_size() const;
8787
const constexpr std::size_t memory_consumption() const;
88+
const constexpr std::size_t no_of_qubits() const;
8889
std::size_t measure();
8990
~qubit() = default;
9091
};
9192

9293
template <std::size_t n_qubits>
9394
void qubit<n_qubits>::apply_predefined_gate(complex (&__s)[1 << n_qubits], const gate_type &__g_type, const std::size_t &qubit_target)
9495
{
95-
std::size_t stride = 1 << qubit_target; // Distance between paired indices
96+
const std::size_t stride = 1 << qubit_target; // Distance between paired indices
9697

97-
for (std::size_t i = 0; i < (1 << n_qubits); i += 2 * stride)
98+
for (std::size_t i = 0; i < 1 << n_qubits; i += 2 * stride)
9899
{
99100
for (std::size_t j = 0; j < stride; ++j)
100101
{
@@ -105,40 +106,40 @@ namespace simulator
105106
complex a = __s[idx0];
106107
complex b = __s[idx1];
107108

108-
__s[idx0] = pre_defined_qgates[(std::size_t)__g_type].matrix[0][0] * a + pre_defined_qgates[(std::size_t)__g_type].matrix[0][1] * b;
109-
__s[idx1] = pre_defined_qgates[(std::size_t)__g_type].matrix[1][0] * a + pre_defined_qgates[(std::size_t)__g_type].matrix[1][1] * b;
109+
__s[idx0] = pre_defined_qgates[static_cast<std::size_t>(__g_type)].matrix[0][0] * a + pre_defined_qgates[static_cast<std::size_t>(__g_type)].matrix[0][1] * b;
110+
__s[idx1] = pre_defined_qgates[static_cast<std::size_t>(__g_type)].matrix[1][0] * a + pre_defined_qgates[static_cast<std::size_t>(__g_type)].matrix[1][1] * b;
110111
}
111112
}
112113
}
113114

114115
template <std::size_t n_qubits>
115-
qubit<n_qubits>::qgate_2x2 &qubit<n_qubits>::get_theta_gate(qubit<n_qubits>::qgate_2x2 &__g, const enum gate_type &__g_type, const double &__theta)
116+
qubit<n_qubits>::qgate_2x2 &qubit<n_qubits>::get_theta_gate(qgate_2x2 &__g, const gate_type &__g_type, const double &__theta)
116117
{
117118
__g.type = __g_type;
118119
switch (__g_type)
119120
{
120-
case qubit<n_qubits>::gate_type::PHASE_GENERAL_SHIFT:
121+
case gate_type::PHASE_GENERAL_SHIFT:
121122
__g.matrix[0][0] = 1.0;
122123
__g.matrix[0][1] = 0.0;
123124
__g.matrix[1][0] = 0.0;
124125
__g.matrix[1][1] = std::polar(1.0, __theta);
125126
break;
126127

127-
case qubit<n_qubits>::gate_type::ROTATION_X:
128+
case gate_type::ROTATION_X:
128129
__g.matrix[0][0] = std::__complex_cos<double>(__theta / 2.0);
129130
__g.matrix[0][1] = (complex){0.0, -1.0} * std::__complex_sin<double>(__theta / 2.0);
130131
__g.matrix[1][0] = (complex){0.0, -1.0} * std::__complex_sin<double>(__theta / 2.0);
131132
__g.matrix[1][1] = std::__complex_cos<double>(__theta / 2.0);
132133
break;
133134

134-
case qubit<n_qubits>::gate_type::ROTATION_Y:
135+
case gate_type::ROTATION_Y:
135136
__g.matrix[0][0] = std::__complex_cos<double>(__theta / 2.0);
136137
__g.matrix[0][1] = -std::__complex_sin<double>(__theta / 2.0);
137138
__g.matrix[1][0] = std::__complex_sin<double>(__theta / 2.0);
138139
__g.matrix[1][1] = std::__complex_cos<double>(__theta / 2.0);
139140
break;
140141

141-
case qubit<n_qubits>::gate_type::ROTATION_Z:
142+
case gate_type::ROTATION_Z:
142143
__g.matrix[0][0] = std::polar(1.0, -__theta / 2.0);
143144
__g.matrix[0][1] = 0.0;
144145
__g.matrix[1][0] = 0.0;
@@ -148,19 +149,18 @@ namespace simulator
148149
default:
149150
std::fprintf(stderr, "error: invalid gate selected '%u'\n", (unsigned)__g_type);
150151
std::exit(EXIT_FAILURE);
151-
break;
152152
}
153153
return __g;
154154
}
155155

156156
template <std::size_t n_qubits>
157157
void qubit<n_qubits>::apply_theta_gate(complex (&__s)[1 << n_qubits], const gate_type &__g_type, const double &__theta, const std::size_t &qubit_target)
158158
{
159-
std::size_t stride = 1 << qubit_target; // Distance between paired indices
160-
qubit<n_qubits>::qgate_2x2 __g;
161-
__g = qubit<n_qubits>::get_theta_gate(__g, __g_type, __theta);
159+
const std::size_t stride = 1 << qubit_target; // Distance between paired indices
160+
qgate_2x2 __g;
161+
__g = qubit::get_theta_gate(__g, __g_type, __theta);
162162

163-
for (std::size_t i = 0; i < (1 << n_qubits); i += 2 * stride)
163+
for (std::size_t i = 0; i < 1 << n_qubits; i += 2 * stride)
164164
{
165165
for (std::size_t j = 0; j < stride; ++j)
166166
{
@@ -186,7 +186,7 @@ namespace simulator
186186
std::exit(EXIT_FAILURE);
187187
}
188188

189-
if (__g_type == qubit<n_qubits>::gate_type::CONTROLLED_NOT)
189+
if (__g_type == gate_type::CONTROLLED_NOT)
190190
{
191191
for (std::size_t i = 0; i < (1 << n_qubits); i++)
192192
{
@@ -201,7 +201,7 @@ namespace simulator
201201
}
202202
}
203203
}
204-
else if (__g_type == qubit<n_qubits>::gate_type::CONTROLLED_Z)
204+
else if (__g_type == gate_type::CONTROLLED_Z)
205205
{
206206
for (std::size_t i = 0; i < (1 << n_qubits); i++)
207207
{
@@ -211,13 +211,13 @@ namespace simulator
211211
}
212212
}
213213
}
214-
else if (__g_type == qubit<n_qubits>::gate_type::SWAP_GATE)
214+
else if (__g_type == gate_type::SWAP_GATE)
215215
{
216216
for (std::size_t i = 0; i < (1 << n_qubits); ++i)
217217
{
218218
// Extract the bits at positions q_control and q_target.
219-
std::size_t bit_q1 = (i >> q_control) & 1;
220-
std::size_t bit_q2 = (i >> q_target) & 1;
219+
const std::size_t bit_q1 = (i >> q_control) & 1;
220+
const std::size_t bit_q2 = (i >> q_target) & 1;
221221

222222
// Only need to swap if the bits differ.
223223
if (bit_q1 != bit_q2)
@@ -243,98 +243,98 @@ namespace simulator
243243
template <std::size_t n_qubits>
244244
qubit<n_qubits> &qubit<n_qubits>::apply_identity(const std::size_t &q_target)
245245
{
246-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::IDENTITY, q_target);
246+
qubit::apply_predefined_gate(this->M_qubits, gate_type::IDENTITY, q_target);
247247
return *this;
248248
}
249249

250250
template <std::size_t n_qubits>
251251
qubit<n_qubits> &qubit<n_qubits>::apply_pauli_x(const std::size_t &q_target)
252252
{
253-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::PAULI_X, q_target);
253+
qubit::apply_predefined_gate(this->M_qubits, gate_type::PAULI_X, q_target);
254254
return *this;
255255
}
256256

257257
template <std::size_t n_qubits>
258258
qubit<n_qubits> &qubit<n_qubits>::apply_pauli_y(const std::size_t &q_target)
259259
{
260-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::PAULI_Y, q_target);
260+
qubit::apply_predefined_gate(this->M_qubits, gate_type::PAULI_Y, q_target);
261261
return *this;
262262
}
263263

264264
template <std::size_t n_qubits>
265265
qubit<n_qubits> &qubit<n_qubits>::apply_pauli_z(const std::size_t &q_target)
266266
{
267-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::PAULI_Z, q_target);
267+
qubit::apply_predefined_gate(this->M_qubits, gate_type::PAULI_Z, q_target);
268268
return *this;
269269
}
270270

271271
template <std::size_t n_qubits>
272272
qubit<n_qubits> &qubit<n_qubits>::apply_hadamard(const std::size_t &q_target)
273273
{
274-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::HADAMARD, q_target);
274+
qubit::apply_predefined_gate(this->M_qubits, gate_type::HADAMARD, q_target);
275275
return *this;
276276
}
277277

278278
template <std::size_t n_qubits>
279279
qubit<n_qubits> &qubit<n_qubits>::apply_phase_pi_2_shift(const std::size_t &q_target)
280280
{
281-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::PHASE_PI_2_SHIFT, q_target);
281+
qubit::apply_predefined_gate(this->M_qubits, gate_type::PHASE_PI_2_SHIFT, q_target);
282282
return *this;
283283
}
284284

285285
template <std::size_t n_qubits>
286286
qubit<n_qubits> &qubit<n_qubits>::apply_phase_pi_4_shift(const std::size_t &q_target)
287287
{
288-
qubit<n_qubits>::apply_predefined_gate(this->M_qubits, qubit<n_qubits>::gate_type::PHASE_PI_4_SHIFT, q_target);
288+
qubit::apply_predefined_gate(this->M_qubits, gate_type::PHASE_PI_4_SHIFT, q_target);
289289
return *this;
290290
}
291291

292292
template <std::size_t n_qubits>
293293
qubit<n_qubits> &qubit<n_qubits>::apply_phase_general_shift(const double &_theta, const std::size_t &q_target)
294294
{
295-
qubit<n_qubits>::apply_theta_gate(this->M_qubits, qubit<n_qubits>::gate_type::PHASE_GENERAL_SHIFT, _theta, q_target);
295+
qubit::apply_theta_gate(this->M_qubits, gate_type::PHASE_GENERAL_SHIFT, _theta, q_target);
296296
return *this;
297297
}
298298

299299
template <std::size_t n_qubits>
300300
qubit<n_qubits> &qubit<n_qubits>::apply_rotation_x(const double &_theta, const std::size_t &q_target)
301301
{
302-
qubit<n_qubits>::apply_theta_gate(this->M_qubits, qubit<n_qubits>::gate_type::ROTATION_X, _theta, q_target);
302+
qubit::apply_theta_gate(this->M_qubits, gate_type::ROTATION_X, _theta, q_target);
303303
return *this;
304304
}
305305

306306
template <std::size_t n_qubits>
307307
qubit<n_qubits> &qubit<n_qubits>::apply_rotation_y(const double &_theta, const std::size_t &q_target)
308308
{
309-
qubit<n_qubits>::apply_theta_gate(this->M_qubits, qubit<n_qubits>::gate_type::ROTATION_Y, _theta, q_target);
309+
qubit::apply_theta_gate(this->M_qubits, gate_type::ROTATION_Y, _theta, q_target);
310310
return *this;
311311
}
312312

313313
template <std::size_t n_qubits>
314314
qubit<n_qubits> &qubit<n_qubits>::apply_rotation_z(const double &_theta, const std::size_t &q_target)
315315
{
316-
qubit<n_qubits>::apply_theta_gate(this->M_qubits, qubit<n_qubits>::gate_type::ROTATION_Z, _theta, q_target);
316+
qubit::apply_theta_gate(this->M_qubits, gate_type::ROTATION_Z, _theta, q_target);
317317
return *this;
318318
}
319319

320320
template <std::size_t n_qubits>
321321
qubit<n_qubits> &qubit<n_qubits>::apply_cnot(const std::size_t &q_control, const std::size_t &q_target)
322322
{
323-
qubit<n_qubits>::apply_2qubit_gate(this->M_qubits, qubit<n_qubits>::gate_type::CONTROLLED_NOT, q_control, q_target);
323+
qubit::apply_2qubit_gate(this->M_qubits, gate_type::CONTROLLED_NOT, q_control, q_target);
324324
return *this;
325325
}
326326

327327
template <std::size_t n_qubits>
328328
qubit<n_qubits> &qubit<n_qubits>::apply_cz(const std::size_t &q_control, const std::size_t &q_target)
329329
{
330-
qubit<n_qubits>::apply_2qubit_gate(this->M_qubits, qubit<n_qubits>::gate_type::CONTROLLED_Z, q_control, q_target);
330+
qubit::apply_2qubit_gate(this->M_qubits, gate_type::CONTROLLED_Z, q_control, q_target);
331331
return *this;
332332
}
333333

334334
template <std::size_t n_qubits>
335335
qubit<n_qubits> &qubit<n_qubits>::apply_swap(const std::size_t &q_control, const std::size_t &q_target)
336336
{
337-
qubit<n_qubits>::apply_2qubit_gate(this->M_qubits, qubit<n_qubits>::gate_type::SWAP_GATE, q_control, q_target);
337+
qubit::apply_2qubit_gate(this->M_qubits, gate_type::SWAP_GATE, q_control, q_target);
338338
return *this;
339339
}
340340

@@ -353,7 +353,13 @@ namespace simulator
353353
template <std::size_t n_qubits>
354354
const constexpr std::size_t qubit<n_qubits>::memory_consumption() const
355355
{
356-
return sizeof(qubit<n_qubits>::complex) * (1 << n_qubits);
356+
return sizeof(complex) * (1 << n_qubits);
357+
}
358+
359+
template <std::size_t n_qubits>
360+
const constexpr std::size_t qubit<n_qubits>::no_of_qubits() const
361+
{
362+
return n_qubits;
357363
}
358364

359365
template <std::size_t n_qubits>
@@ -367,7 +373,7 @@ namespace simulator
367373

368374
std::random_device rd;
369375
std::mt19937 gen(rd());
370-
std::uniform_real_distribution<double> dist(0.0, tot_prob);
376+
std::uniform_real_distribution dist(0.0, tot_prob);
371377
double r = dist(gen);
372378

373379
double accum = 0.0;

0 commit comments

Comments
 (0)