-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathutilities.hpp
1254 lines (1145 loc) · 45.9 KB
/
utilities.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/** @file
* Unoptimised, analytic implementations of matrix operations used by QuEST's unit tests
*
* @defgroup unittest Unit tests
* Unit tests of the QuEST API, using Catch2 in C++14.
* @defgroup testutilities Unit test utilities
* Functions used in the unit testing. These are mostly unoptimised, analytic implementations
* of the complex linear algebra that QuEST ultimately effects on quantum states.
* These are not part of the QuEST API, and require C++14.
*
* @author Tyson Jones
*/
#ifndef QUEST_TEST_UTILS_H
#define QUEST_TEST_UTILS_H
#include "QuEST.h"
#include "QuEST_complex.h"
#include "catch.hpp"
#include <vector>
/** The single QuESTEnv environment created before the Catch tests begin,
* and destroyed thereafter.
*/
extern QuESTEnv QUEST_ENV;
/** The default number of qubits in the registers created for unit testing
* (both statevectors and density matrices). Creation of non-NUM_QUBITS sized
* Quregs should be justified in a comment.
* Note that the smaller this number is, the fewer nodes can be employed in
* distribution testing, since each node must contain at least one amplitude.
* Furthermore, the larger this number is, the greater the deviation of correct
* results from their expected value, due to numerical error; this is especially
* apparent for density matrices.
*/
#define NUM_QUBITS 5
#ifndef M_PI
#define M_PI 3.141592653589793238
#endif
/** This is absolute war against MSVC C++14 which does not permit variable-length
* arrays. We hunted down the previous VLAs with regex:
* ([a-zA-Z0-9]+?) ([a-zA-Z0-9]+?)\[([a-zA-Z0-9]+?)\]
* replacing results with:
* VLA($1, $2, $3)
* We perform this replacement even for non-MSVC compilers, since some dislike
* VLAs of non-POD elemets (like of QMatrix, compiling with NVCC + Clang).
* Eat it, Bill!
*/
#define VLA(type, name, len) \
std::vector<type> name##_vla_hack_vec(len); \
type* name = name##_vla_hack_vec.data();
/** A complex square matrix.
* Should be initialised with getZeroMatrix().
* These have all the natural linear-algebra operator overloads, including
* left-multiplication onto a vector.
*
* This data-structure is not partitioned between nodes in distributed mode.
* That is, every node has a complete copy, allowing for safe comparisons.
*
* @ingroup testutilities
* @author Tyson Jones
*/
typedef std::vector<std::vector<qcomp>> QMatrix;
/** A complex vector, which can be zero-initialised with QVector(numAmps).
* These have all the natural linear-algebra operator overloads.
*
* This data-structure is not partitioned between nodes in distributed mode.
* That is, every node has a complete copy, allowing for safe comparisons.
*
* @ingroup testutilities
* @author Tyson Jones
*/
typedef std::vector<qcomp> QVector;
/** Asserts the given statevector qureg and reference agree, and are properly initialised in the debug state.
* Assertion uses the DEMAND() macro, calling Catch2's FAIL() if unsatisfied, so does not contribute
* toward unit test statistics. This should be called within every PREPARE_TEST macro, to ensure that
* the test states themselves are initially correct, and do not accidentally agree by (e.g.) being all-zero.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void assertQuregAndRefInDebugState(Qureg qureg, QVector ref);
/** Asserts the given density qureg and reference agree, and are properly initialised in the debug state.
* Assertion uses the DEMAND() macro, calling Catch2's FAIL() if unsatisfied, so does not contribute
* toward unit test statistics. This should be called within every PREPARE_TEST macro, to ensure that
* the test states themselves are initially correct, and do not accidentally agree by (e.g.) being all-zero.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void assertQuregAndRefInDebugState(Qureg qureg, QMatrix ref);
/* (Excluded from Doxygen doc)
*
* Define QVector and QMatrix operator overloads.
* Note that QMatrix overloads don't simply use QVector
* overloads, since the complex vector dot product involves
* conjugation, which doesn't occur in complex matrix multiplication.
* Note too we also avoid defining operators in terms of other operators
* (e.g. minus is plus(negative times)) since compiler optimisations
* may change the order of operations and confuse the overloads invoked.
* Definition of division using multiplication can furthermore
* heighten numerical errors.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector operator + (const QVector& v1, const QVector& v2);
QVector operator - (const QVector& v1, const QVector& v2);
QVector operator * (const qcomp& a, const QVector& v);
QVector operator * (const QVector& v, const qcomp& a);
QVector operator / (const QVector& v, const qcomp& a);
qcomp operator * (const QVector &v1, const QVector& v2);
void operator += (QVector& v1, const QVector& v2);
void operator -= (QVector& v1, const QVector& v2);
void operator *= (QVector& v1, const qcomp& a);
void operator /= (QVector& v1, const qcomp& a);
QMatrix operator + (const QMatrix& m1, const QMatrix& m2);
QMatrix operator - (const QMatrix& m1, const QMatrix& m2);
QMatrix operator * (const qcomp& a, const QMatrix& m);
QMatrix operator * (const QMatrix& m, const qcomp& a);
QMatrix operator / (const QMatrix& m, const qcomp& a);
QMatrix operator * (const QMatrix& m1, const QMatrix& m2);
void operator += (QMatrix& m1, const QMatrix& m2);
void operator -= (QMatrix& m1, const QMatrix& m2);
void operator *= (QMatrix& m1, const qreal& a);
void operator /= (QMatrix& m1, const qreal& a);
void operator *= (QMatrix& m1, const QMatrix& m2);
QVector operator * (const QMatrix& m, const QVector& v);
/** Returns an equal-size copy of the given state-vector \p qureg.
* In GPU mode, this function involves a copy of \p qureg from GPU memory to RAM.
* In distributed mode, this involves an all-to-all broadcast of \p qureg.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector toQVector(Qureg qureg);
/** Returns a vector with the same of the full diagonal operator,
* populated with \p op's elements.
* In distributed mode, this involves an all-to-all broadcast of \p op.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector toQVector(DiagonalOp op);
/** Returns an equal-size copy of the given density matrix \p qureg.
* In GPU mode, this function involves a copy of \p qureg from GPU memory to RAM.
* In distributed mode, this involves an all-to-all broadcast of \p qureg.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(Qureg qureg);
/** Returns the matrix (where a=\p alpha, b=\p beta)
* {{a, -conj(b)}, {b, conj(a)}} using the \p qcomp complex type.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(Complex alpha, Complex beta);
/** Returns a copy of the given 2-by-2 matrix.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(ComplexMatrix2 src);
/** Returns a copy of the given 4-by-4 matrix.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(ComplexMatrix4 src);
/** Returns a copy of the given 2^\p N-by-2^\p N matrix
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(ComplexMatrixN src);
/** Returns a 2^\p N-by-2^\p N Hermitian matrix form of the specified
* weighted sum of Pauli products
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(qreal* coeffs, pauliOpType* paulis, int numQubits, int numTerms);
/** Returns a 2^\p N-by-2^\p N Hermitian matrix form of the PauliHamil
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(PauliHamil hamil);
/** Returns a 2^\p N-by-2^\p N complex diagonal matrix form of the DiagonalOp
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(DiagonalOp op);
/** Returns a 2^\p n-by-2^\p n complex diagonal matrix form of the SubDiagonalOp,
* where n = op.numQubits
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toQMatrix(SubDiagonalOp op);
/** Returns a diagonal complex matrix formed by the given vector
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix toDiagonalQMatrix(QVector vec);
/** Returns a \p ComplexMatrix2 copy of QMatix \p qm.
* Demands that \p qm is a 2-by-2 matrix.
*
* @ingroup testutilities
* @author Tyson Jones
*/
ComplexMatrix2 toComplexMatrix2(QMatrix qm);
/** Returns a \p ComplexMatrix4 copy of QMatix \p qm.
* Demands that \p qm is a 4-by-4 matrix.
*
* @ingroup testutilities
* @author Tyson Jones
*/
ComplexMatrix4 toComplexMatrix4(QMatrix qm);
/** Initialises \p cm with the values of \p qm.
* Demands that \p cm is a previously created ComplexMatrixN instance, with
* the same dimensions as \p qm.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm);
/** Initialises the state-vector \p qureg to have the same amplitudes as \p vec.
* Demands \p qureg is a state-vector of an equal size to \p vec.
* In GPU mode, this function involves a copy from RAM to GPU memory.
* This function has no communication cost in distributed mode.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void toQureg(Qureg qureg, QVector vec);
/** Initialises the density matrix \p qureg to have the same amplitudes as \p mat.
* Demands \p qureg is a density matrix of equal dimensions to \p mat.
* In GPU mode, this function involves a copy from RAM to GPU memory.
* This function has no communication cost in distributed mode.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void toQureg(Qureg qureg, QMatrix mat);
/** Returns b (otimes) a. If b and a are state-vectors, the resulting kronecker
* product is the seperable state formed by joining the qubits in the state-vectors,
* producing |b>|a> (a is least significant)
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getKroneckerProduct(QVector b, QVector a);
/** Returns a dim-by-dim square complex matrix, initialised to all zeroes.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getZeroMatrix(size_t dim);
/** Returns a dim-by-dim identity matrix
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getIdentityMatrix(size_t dim);
/** Returns the matrix exponential of a diagonal, square, complex matrix.
* This method explicitly checks that the passed matrix \p a is diagonal.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getExponentialOfDiagonalMatrix(QMatrix a);
/** Returns the matrix exponential of a kronecker product of pauli matrices
* (or of any involutory matrices), with exponent factor (-i \p angle / 2).
* This method will not explicitly check that the passed matrix \p a is
* kronecker product of involutory matrices, but will otherwise return an
* incorrect exponential.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getExponentialOfPauliMatrix(qreal angle, QMatrix a);
/** Returns the kronecker product of \p a and \p b, where \p a and \p b are
* square but possibly differently-sized complex matrices.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getKroneckerProduct(QMatrix a, QMatrix b);
/** Returns the 2^\p numQb-by-2^\p numQb unitary matrix which swaps qubits
* \p qb1 and \p qb2; the SWAP gate of not-necessarily-adjacent qubits.
* If \p qb1 == \p qb2, returns the identity matrix.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getSwapMatrix(int qb1, int qb2, int numQb);
/** Takes a 2^\p numTargs-by-2^\p numTargs matrix \p op and a returns a
* 2^\p numQubits-by-2^\p numQubits matrix where \p op is controlled on the given
* \p ctrls qubits. The union of {\p ctrls} and {\p targs} must be unique (though
* this is not explicitly checked), and every element must be >= 0 (not checked).
* The passed {\p ctrls} and {\p targs} arrays are unmodified.
*
* This funciton works by first swapping {\p targs} and {\p ctrls} (via swap unitaries)
* to be strictly increasing {0,1,...}, building controlled(\p op), tensoring it to
* the full Hilbert space, and then 'unswapping'. The returned matrix has form:
* swap1 ... swapN . controlled(\p op) . swapN ... swap1
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getFullOperatorMatrix(int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits);
/** Returns the matrix |\p ket><\p bra|, with ith-jth element \p ket(i) conj(\p bra(j)), since
* |\p ket><\p bra| = sum_i a_i|i> sum_j b_j* <j| = sum_{ij} a_i b_j* |i><j|.
* The dimensions of bra and ket must agree, and the returned square complex matrix
* has dimensions size(bra) x size(bra).
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getKetBra(QVector ket, QVector bra);
/** Returns the conjugate transpose of the complex square matrix \p a
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getConjugateTranspose(QMatrix a);
/** Returns a random integer between \p min (inclusive) and \p max (exclusive),
* from the uniform distribution.
* Demands that \p max > \p min.
*
* @ingroup testutilities
* @author Tyson Jones
*/
int getRandomInt(int min, int max);
/** Returns a random real between \p min (inclusive) and \p max (exclusive),
* from the uniform distribution.
* Demands that \p max > \p min.
*
* @ingroup testutilities
* @author Tyson Jones
*/
qreal getRandomReal(qreal min, qreal max);
/** Returns a random complex number within the square closing (-1-i) and (1+i),
* from a distribution uniformly randomising the individual real and imaginary
* components in their domains.
*
* @ingroup testutilities
* @author Tyson Jones
*/
qcomp getRandomComplex();
/** Returns a \p dim-length vector with random complex amplitudes in the
* square joining {-1-i, 1+i}, of an undisclosed distribution. The resulting
* vector is NOT L2-normalised.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getRandomQVector(int dim);
/** Returns a \p dim-by-\p dim complex matrix, where the real and imaginary value of
* each element are independently random, under the standard normal distribution
* (mean 0, standard deviation 1).
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getRandomQMatrix(int dim);
/** Returns a uniformly random (under Haar) 2^\p numQb-by-2^\p numQb unitary matrix.
* This function works by first generating a complex matrix where
* each element is independently random; the real and imaginary component thereof are
* independent standard normally-distributed (mean 0, standard-dev 1).
* Then, the matrix is orthonormalised via the Gram Schmidt algorithm.
* The resulting unitary matrix MAY be uniformly distributed under the Haar
* measure, but we make no assurance.
* This routine may return an identity matrix if it was unable to sufficiently
* precisely produce a unitary of the given size.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getRandomUnitary(int numQb);
/** Returns a random \p numQb-length L2-normalised state-vector from an
* undisclosed distribution. This function works by randomly generating each
* complex amplitude, then L2-normalising.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getRandomStateVector(int numQb);
/** Returns a random \p numQb-by-\p numQb density matrix, from an undisclosed
* distribution, in a very mixed state. This function works by generating
* 2^\p numQb random pure states, and mixing them with random probabilities.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getRandomDensityMatrix(int numQb);
/** Returns a random \p numQb-by-\p numQb density matrix, from an undisclosed
* distribution, which is pure (corresponds to a random state-vector)
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getRandomPureDensityMatrix(int numQb);
/** Returns a density matrix initialised into the given pure state
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getPureDensityMatrix(QVector state);
/** Returns the diagonal vector of the given matrix
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getMatrixDiagonal(QMatrix matr);
/** Returns a random Kraus map of #`numOps` 2^\p numQb-by-2^\p numQb operators,
* from an undisclosed distribution.
* Note this method is very simple and cannot generate all possible Kraus maps.
* It works by generating \p numOps random unitary matrices, and randomly
* re-normalising them, such that the sum of ops[j]^dagger ops[j] = 1
*
* @ingroup testutilities
* @author Tyson Jones
*/
std::vector<QMatrix> getRandomKrausMap(int numQb, int numOps);
/** Returns a list of random real scalars, each in [0, 1], which sum to unity.
*
* @ingroup testutilities
* @author Tyson Jones
*/
std::vector<qreal> getRandomProbabilities(int numProbs);
/** Returns a list of random orthonormal complex vectors, from an undisclosed
* distribution.
*
* @ingroup testutilities
* @author Tyson Jones
*/
std::vector<QVector> getRandomOrthonormalVectors(int numQb, int numStates);
/** Returns a mixed density matrix formed from mixing the given pure states,
* which are assumed normalised, but not necessarily orthogonal.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QMatrix getMixedDensityMatrix(std::vector<qreal> probs, std::vector<QVector> states);
/** Returns an L2-normalised copy of \p vec, using Kahan summation for improved accuracy.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getNormalised(QVector vec);
/** Returns the discrete fourier transform of vector in
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getDFT(QVector in);
/** Returns the discrete fourier transform of a sub-partition of the vector in.
*
* @ingroup testutilities
* @author Tyson Jones
*/
QVector getDFT(QVector in, int* targs, int numTargs);
/** Returns the integer value of the targeted sub-register for the given
* full state index \p ind.
*
* @ingroup testutilities
* @author Tyson Jones
*/
long long int getValueOfTargets(long long int ind, int* targs, int numTargs);
/** Modifies \p dest by overwriting its submatrix (from top-left corner
* (\p r, \p c) to bottom-right corner (\p r + \p dest.size(), \p c + \p dest.size())
* with the complete elements of sub.
* This demands that dest.size() >= sub.size() + max(r,c).
*
* @ingroup testutilities
* @author Tyson Jones
*/
void setSubMatrix(QMatrix &dest, QMatrix sub, size_t r, size_t c);
/** Modifies the density matrix \p state to be the result of applying the multi-target operator
* matrix \p op, with the specified control and target qubits (in \p ctrls and \p targs
* respectively). This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 2^\p numTargs-by-2^\p numTargs matrix. Furthermore, every element of \p targs
* must not appear in \p ctrls (and vice-versa), though this is not explicitly checked.
* Elements of \p targs and \p ctrls should be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the two-target operator
* matrix \p op, with the specified control qubits (in \p ctrls).
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 4-by-4 matrix. Both \p targ1 and \p targ2 must not appear in \p ctrls,
* though this is not explicitly checked. Elements of \p ctrls, and \p targ1 and \p targ2,
* should be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the single-target
* operator matrix \p op, with the specified control qubits (in \p ctrls).
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 2-by-2 matrix. \p target must not appear in \p ctrls,
* though this is not explicitly checked.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int* ctrls, int numCtrls, int target, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the multi-target
* operator matrix \p op, with no control qubits.
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 2^\p numTargs-by-2^\p numTargs matrix.
* Every element in \p targs should be unique, though this is not explicitly checked.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int *targs, int numTargs, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the single-control
* single-target operator matrix \p op.
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 2-by-2 matrix, and \p ctrl and \p targ should be different.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int ctrl, int targ, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the multi-target
* operator matrix \p op, with a single control qubit \p ctrl.
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 2^\p numTargs-by-2^\p numTargs matrix, and \p ctrl must not
* appear in \p targs (though this is not explicitly checked).
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int ctrl, int* targs, int numTargs, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the two-target
* operator matrix \p op, with a single control qubit \p ctrl.
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 4-by-4 matrix, and \p ctrl, \p targ1 and \p targ2 must be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int ctrl, int targ1, int targ2, QMatrix op);
/** Modifies the density matrix \p state to be the result of applying the single-target
* operator matrix \p op, with no control qubit.
* This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state} \, \text{op}^\dagger
* \f]
* even if \p op is not unitary (which is useful for applying Kraus operators).
*
* \p op must be a 2-by-2 matrix.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multipling it to \p state, then right-multiplying its
* conjugate transpose onto the result.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QMatrix &state, int targ, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the multi-target operator
* matrix \p op, with the specified control and target qubits (in \p ctrls and \p targs
* respectively). This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 2^\p numTargs-by-2^\p numTargs matrix. Furthermore, every element of \p targs
* must not appear in \p ctrls (and vice-versa), though this is not explicitly checked.
* Elements of \p targs and \p ctrls should be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the two-target operator
* matrix \p op, with the specified control qubits (in \p ctrls). This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 4-by-4 matrix. Furthermore, \p ctrls, \p targ1 and \p targ2 should
* all be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the single-target operator
* matrix \p op, with the specified control qubits (in \p ctrls). This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 2-by-2 matrix. Furthermore, elements in \p ctrls and \p target should
* all be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int* ctrls, int numCtrls, int target, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the multi-target operator
* matrix \p op, with no contorl qubits. This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 2^\p numTargs-by-2^\p numTargs matrix. Furthermore, elements in \p targs should be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int *targs, int numTargs, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the single-target operator
* matrix \p op, with a single control qubit (\p ctrl). This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 2-by-2 matrix. Furthermore, \p ctrl and \p targ must be different.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int ctrl, int targ, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the multi-target operator
* matrix \p op, with a single control qubit (\p ctrl) This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 2^\p numTargs-by-2^\p numTargs matrix. Furthermore, elements
* in \p targs and \p ctrl should be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int ctrl, int* targs, int numTargs, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the two-target operator
* matrix \p op, with a single control qubit (\p ctrl). This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 4-by-4 matrix. Furthermore, \p ctrl, \p targ1 and \p targ2 should
* all be unique.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int ctrl, int targ1, int targ2, QMatrix op);
/** Modifies the state-vector \p state to be the result of applying the single-target operator
* matrix \p op, with no contorl qubits. This updates \p state under
* \f[
\text{state} \to \text{op} \, \text{state}
* \f]
* even if \p op is not unitary.
*
* \p op must be a 2-by-2 matrix.
*
* This function works by computing getFullOperatorMatrix() from the given
* arguments, and left-multiplying it onto \p state.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceOp(QVector &state, int targ, QMatrix op);
/** Modifies the state-vector \p state to be the result of left-multiplying the multi-target operator
* matrix \p op, with the specified control and target qubits (in \p ctrls and \p targs
* respectively). This is an alias of applyReferenceOp(), since operators are always
* left-multiplied as matrices onto state-vectors.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceMatrix(QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op);
/** Modifies the state-vector \p state to be the result of left-multiplying the multi-target operator
* matrix \p op, with the specified target qubits (assuming no control qubits). T
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceMatrix(QVector &state, int *targs, int numTargs, QMatrix op);
/** Modifies the density matrix \p state to be the result of left-multiplying the multi-target operator
* matrix \p op, with the specified control and target qubits (in \p ctrls and \p targs
* respectively). Here, \p op is treated like a simple matrix and is hence left-multiplied
* onto the state once.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceMatrix(QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op);
/** Modifies the density matrix \p state to be the result of left-multiplying the multi-target operator
* matrix \p op, with the target qubits (assuming no control qubits).
* Here, \p op is treated like a simple matrix and is hence left-multiplied onto the state once.
*
* @ingroup testutilities
* @author Tyson Jones
*/
void applyReferenceMatrix(QMatrix &state, int *targs, int numTargs, QMatrix op);
/** Performs a hardware-agnostic comparison of the given quregs, checking
* whether the difference between the real and imaginary components of every amplitude
* is smaller than the QuEST_PREC-specific REAL_EPS (defined in QuEST_precision) precision.
* This function demands that \p qureg1 and \p qureg2 are of the same type
* (i.e. both state-vectors or both density matrices), and of an equal number
* of qubits.
*
* In GPU mode, this function involves a GPU to CPU memory copy overhead.
* In distributed mode, it involves a all-to-all single-int broadcast.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(Qureg qureg1, Qureg qureg2);
/** Performs a hardware-agnostic comparison of state-vector \p qureg to \p vec, checking
* whether the difference between the real and imaginary components of every amplitude
* is smaller than the QuEST_PREC-specific REAL_EPS (defined in QuEST_precision) precision.
* This function demands \p qureg is a state-vector, and that \p qureg and
* \p vec have the same number of amplitudes.
*
* In GPU mode, this function involves a GPU to CPU memory copy overhead.
* In distributed mode, it involves a all-to-all single-int broadcast.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(Qureg qureg, QVector vec);
/** Performs a hardware-agnostic comparison of density-matrix \p qureg to \p matr, checking
* whether the difference between the real and imaginary components of every amplitude
* is smaller than the QuEST_PREC-specific REAL_EPS (defined in QuEST_precision) precision.
* This function demands \p qureg is a density matrix, and that \p qureg and \p matr have
* equal dimensions.
*
* In GPU mode, this function involves a GPU to CPU memory copy overhead.
* In distributed mode, it involves a all-to-all single-int broadcast.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(Qureg qureg, QMatrix matr);
/** Performs a hardware-agnostic comparison of the given quregs, checking
* whether the difference between the real and imaginary components of every amplitude
* is smaller than \p precision.
* This function demands that \p qureg1 and \p qureg2 are of the same type
* (i.e. both state-vectors or both density matrices), and of an equal number
* of qubits.
*
* In GPU mode, this function involves a GPU to CPU memory copy overhead.
* In distributed mode, it involves a all-to-all single-int broadcast.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(Qureg qureg1, Qureg qureg2, qreal precision);
/** Performs a hardware-agnostic comparison of state-vector \p qureg to \p vec, checking
* whether the difference between the real and imaginary components of every amplitude
* is smaller than \p precision.
* This function demands \p qureg is a state-vector, and that \p qureg and
* \p vec have the same number of amplitudes.
*
* In GPU mode, this function involves a GPU to CPU memory copy overhead.
* In distributed mode, it involves a all-to-all single-int broadcast.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(Qureg qureg, QVector vec, qreal precision);
/** Performs a hardware-agnostic comparison of density-matrix \p qureg to \p matr, checking
* whether the difference between the real and imaginary components of every amplitude
* is smaller than \p precision.
* This function demands \p qureg is a density matrix, and that \p qureg and \p matr have
* equal dimensions.
*
* In GPU mode, this function involves a GPU to CPU memory copy overhead.
* In distributed mode, it involves a all-to-all single-int broadcast.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(Qureg qureg, QMatrix matr, qreal precision);
/** Returns true if the absolute value of the difference between every amplitude in
* vectors \p a and \p b is less than \p REAL_EPS.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(QVector a, QVector b);
/** Returns true if the absolute value of the difference between every amplitude in
* matrices \p a and \p b is less than \p REAL_EPS.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(QMatrix a, QMatrix b);
/** Returns true if the absolute value of the difference between every element in
* \p vec and those implied by \p reals and \p imags, is less than \p REAL_EPS.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(QVector vec, qreal* reals, qreal* imags);
/** Returns true if the absolute value of the difference between every element in
* \p vec (which must be strictly real) and those implied by \p reals, is less
* than \p REAL_EPS.
*
* @ingroup testutilities
* @author Tyson Jones
*/
bool areEqual(QVector vec, qreal* reals);
/** Returns the unit-norm complex number exp(i*\p phase). This function uses the
* Euler formula, and avoids problems with calling exp(__complex__) in a platform
* agnostic way