The Quantum Exact Simulation Toolkit v4.0.0
Loading...
Searching...
No Matches
random.cpp
1/** @file
2 * Testing utilities which generate random objects
3 * independently of QuEST's internal generators.
4 * It is important that this file uses only its
5 * internal RNG, and not C-library functions like
6 * rand(), so that it cannot be interfered with by
7 * external files calling e.g. srand().
8 *
9 * @author Tyson Jones
10 */
11
12#include "qvector.hpp"
13#include "qmatrix.hpp"
14#include "macros.hpp"
15#include "linalg.hpp"
16#include "lists.hpp"
17#include "random.hpp"
18#include "quest/include/quest.h"
19
20#include <cmath>
21#include <vector>
22#include <tuple>
23#include <random>
24#include <algorithm>
25
26using std::vector;
27using std::tuple;
28
29
30
31/*
32 * RNG
33 */
34
35
36static std::mt19937 RNG;
37
38
40 DEMAND( isQuESTEnvInit() );
41
42 // generate a random seed from hardware rng
43 std::random_device cspnrg;
44 unsigned seed = cspnrg();
45
46 // seed QuEST, which uses only the root node's seed
47 setSeeds(&seed, 1);
48
49 // broadcast root node seed to all nodes
50 getSeeds(&seed);
51
52 // seed RNG
53 RNG.seed(seed);
54}
55
56
57
58/*
59 * SCALAR
60 */
61
62
63qreal getRandomReal(qreal min, qreal maxExcl) {
64 DEMAND( min < maxExcl );
65
66 // advance RNG on every node, identically
67 std::uniform_real_distribution<qreal> dist(min,maxExcl);
68 qreal out = dist(RNG);
69
70 // note despite the doc asserting maxExcl is exclusive,
71 // uniform_real_distribution() can indeed return it! In that
72 // case, we substract machine-eps for caller integrity
73 if (out >= maxExcl)
74 out = std::nextafter(maxExcl, min);
75
76 DEMAND( out >= min );
77 DEMAND( out < maxExcl );
78 return out;
79}
80
81
82qreal getRandomPhase() {
83
84 // accuracy of PI does not matter here
85 qreal pi = 3.14159265358979323846;
86 return getRandomReal(-2*pi, 2*pi);
87}
88
89
90int getRandomInt(int min, int maxExcl) {
91 DEMAND( maxExcl >= min );
92
93 // permit this out of convenience
94 // for some test generators
95 if (min == maxExcl)
96 return min;
97
98 qreal r = std::floor(getRandomReal(min, maxExcl));
99 int out = static_cast<int>(r);
100
101 DEMAND( out >= min );
102 DEMAND( out < maxExcl );
103 return out;
104}
105
106
108 qreal re = getRandomReal(-1,1);
109 qreal im = getRandomReal(-1,1);
110 return qcomp(re, im);
111}
112
113
114qcomp getRandomUnitComplex() {
115 return std::exp(1_i * getRandomPhase());
116}
117
118
119
120/*
121 * LIST
122 */
123
124
125vector<int> getRandomInts(int min, int maxExcl, int len) {
126 DEMAND( len >= 0 ); // permit empty
127 DEMAND( min < maxExcl );
128
129 vector<int> out(len);
130
131 for (auto& x : out)
132 x = getRandomInt(min, maxExcl);
133
134 return out;
135}
136
137
138vector<int> getRandomOutcomes(int len) {
139
140 int min = 0;
141 int max = 1;
142 return getRandomInts(min, max+1, len);
143}
144
145
146vector<int> getRandomSubRange(int start, int endExcl, int numElems) {
147 DEMAND( endExcl >= start );
148 DEMAND( numElems >= 1 );
149 DEMAND( numElems <= endExcl - start );
150
151 // shuffle entire range (advances RNG on every node, identically)
152 vector<int> range = getRange(start, endExcl);
153 std::shuffle(range.begin(), range.end(), RNG);
154
155 // return first subrange
156 return vector<int>(range.begin(), range.begin() + numElems);
157}
158
159
160vector<qreal> getRandomProbabilities(int numProbs) {
161
162 vector<qreal> probs(numProbs, 0);
163
164 // generate random unnormalised scalars
165 for (auto& p : probs)
166 p = getRandomReal(0, 1);
167
168 // normalise
169 qreal total = 0;
170 for (auto& p : probs)
171 total += p;
172
173 for (auto& p : probs)
174 p /= total;
175
176 return probs;
177}
178
179
180listpair getRandomFixedNumCtrlsTargs(int numQubits, int numCtrls, int numTargs) {
181
182 vector<int> targsCtrls = getRandomSubRange(0, numQubits, numTargs + numCtrls);
183 vector<int> targs = getSublist(targsCtrls, 0, numTargs);
184 vector<int> ctrls = getSublist(targsCtrls, numTargs, numCtrls);
185
186 return tuple{ctrls,targs};
187}
188
189
190listtrio getRandomVariNumCtrlsStatesTargs(int numQubits, int minNumTargs, int maxNumTargsIncl) {
191 DEMAND( minNumTargs <= maxNumTargsIncl );
192 DEMAND( maxNumTargsIncl <= numQubits );
193
194 int numTargs = getRandomInt(minNumTargs, maxNumTargsIncl+1);
195
196 // numCtrls in [0, remainingNumQb]
197 int minNumCtrls = 0;
198 int maxNumCtrls = numQubits - numTargs;
199 int numCtrls = getRandomInt(minNumCtrls, maxNumCtrls+1);
200
201 // distribute qubits randomly
202 auto [ctrls,targs] = getRandomFixedNumCtrlsTargs(numQubits, numCtrls, numTargs);
203 vector<int> states = getRandomInts(0, 2, numCtrls);
204
205 return tuple{ctrls,states,targs};
206}
207
208
209
210/*
211 * VECTOR
212 */
213
214
215qvector getRandomVector(size_t dim) {
216
217 qvector vec = getZeroVector(dim);
218
219 for (auto& elem : vec)
220 elem = getRandomComplex();
221
222 return vec;
223}
224
225
226vector<qvector> getRandomOrthonormalVectors(size_t dim, int numVecs) {
227 DEMAND( dim >= 1 );
228 DEMAND( numVecs >= 1);
229
230 vector<qvector> vecs(numVecs);
231
232 // produce each vector in-turn
233 for (int n=0; n<numVecs; n++) {
234
235 // from a random vector
236 vecs[n] = getRandomVector(dim);
237
238 // orthogonalise by substracting projections of existing vectors
239 for (int m=0; m<n; m++)
240 vecs[n] -= vecs[m] * getInnerProduct(vecs[m], vecs[n]);
241
242 // then re-normalise
243 vecs[n] = getNormalised(vecs[n]);
244 }
245
246 return vecs;
247}
248
249
250
251/*
252 * MATRIX
253 */
254
255
256qmatrix getRandomNonSquareMatrix(size_t numRows, size_t numCols) {
257
258 // this function is DANGEROUS; it produces a
259 // non-square matrix, whereas most test utilities
260 // assume qmatrix is square. It should ergo be
261 // used very cautiously!
262
263 qmatrix out = qmatrix(numRows, qvector(numCols));
264
265 for (auto& row : out)
266 for (auto& elem : row)
267 elem = getRandomComplex();
268
269 return out;
270}
271
272
273qmatrix getRandomMatrix(size_t dim) {
274
275 return getRandomNonSquareMatrix(dim, dim);
276}
277
278
279qmatrix getRandomDiagonalMatrix(size_t dim) {
280
281 qmatrix out = getZeroMatrix(dim);
282
283 for (size_t i=0; i<dim; i++)
284 out[i][i] = getRandomComplex();
285
286 return out;
287}
288
289
290
291/*
292 * STATES
293 */
294
295
296qvector getRandomStateVector(int numQb) {
297
298 return getNormalised(getRandomVector(getPow2(numQb)));
299}
300
301
302vector<qvector> getRandomOrthonormalStateVectors(int numQb, int numStates) {
303
304 return getRandomOrthonormalVectors(getPow2(numQb), numStates);
305}
306
307
308qmatrix getRandomDensityMatrix(int numQb) {
309 DEMAND( numQb > 0 );
310
311 // generate random probabilities to weight random pure states
312 int dim = getPow2(numQb);
313 vector<qreal> probs = getRandomProbabilities(dim);
314
315 // add random pure states
316 qmatrix dens = getZeroMatrix(dim);
317 for (int i=0; i<dim; i++) {
318 qvector pure = getRandomStateVector(numQb);
319 dens += probs[i] * getOuterProduct(pure, pure);
320 }
321
322 return dens;
323}
324
325
326qmatrix getRandomPureDensityMatrix(int numQb) {
327
328 qvector vec = getRandomStateVector(numQb);
329 qmatrix mat = getOuterProduct(vec, vec);
330 return mat;
331}
332
333
334void setToRandomState(qvector& state) {
335 state = getRandomStateVector(getLog2(state.size()));
336}
337void setToRandomState(qmatrix& state) {
338 state = getRandomDensityMatrix(getLog2(state.size()));
339}
340
341
342
343/*
344 * OPERATORS
345 */
346
347
348qmatrix getRandomUnitary(int numQb) {
349 DEMAND( numQb >= 1 );
350
351 // create Z ~ random complex matrix (distribution not too important)
352 size_t dim = getPow2(numQb);
353 qmatrix matrZ = getRandomMatrix(dim);
354 qmatrix matrZT = getTranspose(matrZ);
355
356 // create Z = Q R (via QR decomposition) ...
357 qmatrix matrQT = getOrthonormalisedRows(matrZ);
358 qmatrix matrQ = getTranspose(matrQT);
359 qmatrix matrR = getZeroMatrix(dim);
360
361 // ... where R_rc = (columm c of Z) . (column r of Q) = (row c of ZT) . (row r of QT) = <r|c>
362 for (size_t r=0; r<dim; r++)
363 for (size_t c=r; c<dim; c++)
364 matrR[r][c] = getInnerProduct(matrQT[r], matrZT[c]);
365
366 // create D = normalised diagonal of R
367 qmatrix matrD = getZeroMatrix(dim);
368 for (size_t i=0; i<dim; i++)
369 matrD[i][i] = matrR[i][i] / std::abs(matrR[i][i]);
370
371 // create U = Q D
372 qmatrix matrU = matrQ * matrD;
373
374 DEMAND( isApproxUnitary(matrU) );
375 return matrU;
376}
377
378
379qmatrix getRandomDiagonalUnitary(int numQb) {
380 DEMAND( numQb >= 1 );
381
382 qmatrix matr = getZeroMatrix(getPow2(numQb));
383
384 // unitary diagonals have unit complex scalars
385 for (size_t i=0; i<matr.size(); i++)
386 matr[i][i] = getRandomUnitComplex();
387
388 return matr;
389}
390
391
392qmatrix getRandomDiagonalHermitian(int numQb) {
393 DEMAND( numQb >= 1 );
394
395 qmatrix out = getZeroMatrix(getPow2(numQb));
396
397 // Hermitian diagonals are real
398 for (size_t i=0; i<out.size(); i++)
399 out[i][i] = getRandomReal(-10, 10);
400
401 return out;
402}
403
404
405vector<qmatrix> getRandomKrausMap(int numQb, int numOps) {
406 DEMAND( numOps >= 1 );
407
408 // generate random unitaries
409 vector<qmatrix> ops(numOps);
410 for (auto& u : ops)
411 u = getRandomUnitary(numQb);
412
413 // generate random weights
414 vector<qreal> weights(numOps);
415 for (auto& w : weights)
416 w = getRandomReal(0, 1);
417
418 // normalise random weights
419 qreal sum = 0;
420 for (auto& w : weights)
421 sum += w;
422 for (auto& w : weights)
423 w = std::sqrt(w/sum);
424
425 // normalise unitaries according to weights
426 for (int i=0; i<numOps; i++)
427 ops[i] *= weights[i];
428
429 DEMAND( isApproxCPTP(ops) );
430 return ops;
431}
432
433
434
435/*
436 * PAULIS
437 */
438
439
440PauliStr getRandomPauliStr(int numQubits) {
441
442 std::string paulis = "";
443 for (int i=0; i<numQubits; i++)
444 paulis += "IXYZ"[getRandomInt(0,4)];
445
446 return getPauliStr(paulis);
447}
448
449
450PauliStr getRandomPauliStr(vector<int> targs) {
451
452 std::string paulis = "";
453 for (size_t i=0; i<targs.size(); i++)
454 paulis += "IXYZ"[getRandomInt(0,4)];
455
456 return getPauliStr(paulis, targs);
457}
458
459
460PauliStr getRandomDiagPauliStr(int numQubits) {
461
462 std::string paulis = "";
463 for (int i=0; i<numQubits; i++)
464 paulis += "IZ"[getRandomInt(0,2)];
465
466 return getPauliStr(paulis);
467}
468
469
470PauliStrSum createRandomNonHermitianPauliStrSum(int numQubits, int numTerms) {
471
472 vector<PauliStr> strings(numTerms);
473 for (auto& str : strings)
474 str = getRandomPauliStr(numQubits);
475
476 vector<qcomp> coeffs(numTerms);
477 for (auto& c : coeffs)
478 c = getRandomComplex();
479
480 return createPauliStrSum(strings, coeffs);
481}
482
483
484PauliStrSum createRandomPauliStrSum(int numQubits, int numTerms) {
485
486 PauliStrSum out = createRandomNonHermitianPauliStrSum(numQubits, numTerms);
487
488 for (qindex i=0; i<numTerms; i++)
489 out.coeffs[i] = std::real(out.coeffs[i]);
490
491 return out;
492}
std::vector< unsigned > getSeeds()
Definition debug.cpp:197
void setSeeds(unsigned *seeds, int numSeeds)
Definition debug.cpp:37
int isQuESTEnvInit()
PauliStrSum createPauliStrSum(PauliStr *strings, qcomp *coeffs, qindex numTerms)
Definition paulis.cpp:385
PauliStr getPauliStr(const char *paulis, int *indices, int numPaulis)
Definition paulis.cpp:296
qmatrix getZeroMatrix(size_t dim)
Definition qmatrix.cpp:18
qvector getRandomStateVector(int numQb)
Definition random.cpp:296
qcomp getRandomComplex()
Definition random.cpp:107
qmatrix getRandomPureDensityMatrix(int numQb)
Definition random.cpp:326
qmatrix getRandomUnitary(int numQb)
Definition random.cpp:348
void setRandomTestStateSeeds()
Definition random.cpp:39
qreal getRandomReal(qreal min, qreal maxExcl)
Definition random.cpp:63
qmatrix getRandomDensityMatrix(int numQb)
Definition random.cpp:308
vector< qreal > getRandomProbabilities(int numProbs)
Definition random.cpp:160
vector< qmatrix > getRandomKrausMap(int numQb, int numOps)
Definition random.cpp:405
int getRandomInt(int min, int maxExcl)
Definition random.cpp:90