The Quantum Exact Simulation Toolkit v4.0.0
Loading...
Searching...
No Matches
compare.cpp
1/** @file
2 * Testing utilities which compare scalars produced by the
3 * QuEST API to those produced by other test utilities, and
4 * Quregs modified by the API to qvector qmatrix references.
5 *
6 * @author Tyson Jones
7 */
8
9#include "qvector.hpp"
10#include "qmatrix.hpp"
11#include "convert.hpp"
12#include "linalg.hpp"
13#include "macros.hpp"
14
15#include "quest/include/quest.h"
16
17#include <catch2/catch_test_macros.hpp>
18#include <catch2/matchers/catch_matchers_floating_point.hpp>
19
20#include <complex>
21#include <vector>
22#include <algorithm>
23
24using std::abs;
25using std::vector;
26using namespace Catch::Matchers;
27
28
29
30/*
31 * Maximum tolerated difference between API and reference
32 * scalar in order to be considered equivalent.
33 */
34
35
36#if FLOAT_PRECISION == 1
37 const qreal ABSOLUTE_EPSILON = 1E-2;
38 const qreal RELATIVE_EPSILON = 1E-2;
39#elif FLOAT_PRECISION == 2
40 const qreal ABSOLUTE_EPSILON = 1E-8;
41 const qreal RELATIVE_EPSILON = 1E-8;
42#elif FLOAT_PRECISION == 4
43 const qreal ABSOLUTE_EPSILON = 1E-10;
44 const qreal RELATIVE_EPSILON = 1E-10;
45#endif
46
47
48qreal getTestAbsoluteEpsilon() {
49
50 return ABSOLUTE_EPSILON;
51}
52
53qreal getTestRelativeEpsilon() {
54
55 return RELATIVE_EPSILON;
56}
57
58
59qreal getAbsDif(qcomp a, qcomp b) {
60
61 return std::abs(a - b);
62}
63
64qreal getRelDif(qcomp a, qcomp b) {
65
66 qreal denom = std::min({std::abs(a), std::abs(b)});
67 return getAbsDif(a,b) / denom;
68}
69
70
71bool doScalarsAgree(qcomp a, qcomp b) {
72
73 // permit absolute OR relative agreement
74
75 if (getAbsDif(a, b) <= ABSOLUTE_EPSILON)
76 return true;
77
78 return (getRelDif(a, b) <= RELATIVE_EPSILON);
79}
80
81bool doMatricesAgree(qmatrix a, qmatrix b) {
82 DEMAND( a.size() == b.size() );
83
84 // assumed square and equal-size
85 size_t dim = a.size();
86
87 for (size_t i=0; i<dim; i++)
88 for (size_t j=0; j<dim; j++)
89 if (!doScalarsAgree(a[i][j], b[i][j]))
90 return false;
91
92 return true;
93}
94
95
96
97/*
98 * We compare a Qureg and its reference qvector/qmatrix
99 * in a manner which avoids polluting the Catch2 console
100 * output, and which reports a single disgreeing amplitude
101 * when the comparison fails.
102 */
103
104
105void REPORT_AMP_AND_FAIL( size_t index, qcomp amplitude, qcomp reference ) {
106 qreal absolute_difference = getAbsDif(amplitude, reference);
107 qreal relative_difference = getRelDif(amplitude, reference);
108 CAPTURE(
109 index, amplitude, reference,
110 absolute_difference, ABSOLUTE_EPSILON,
111 relative_difference, RELATIVE_EPSILON
112 );
113 FAIL( );
114}
115
116
117void REQUIRE_AGREE( Qureg q, qvector v1 ) {
118 DEMAND( !q.isDensityMatrix );
119 DEMAND( q.numAmps == (qindex) v1.size() );
120
121 qvector v2 = getVector(q);
122
123 for (size_t i=0; i<v1.size(); i++)
124 if (!doScalarsAgree(v1[i], v2[i]))
125 REPORT_AMP_AND_FAIL(i, v2[i], v1[i]);
126
127 SUCCEED( );
128}
129
130
131void REQUIRE_AGREE( Qureg q, qmatrix m1 ) {
132 DEMAND( q.isDensityMatrix );
133 DEMAND( getPow2(q.numQubits) == (qindex) m1.size() );
134
135 qmatrix m2 = getMatrix(q);
136
137 for (size_t i=0; i<m1.size(); i++)
138 for (size_t j=0; j<m1.size(); j++)
139 if (!doScalarsAgree(m1[i][j], m2[i][j]))
140 REPORT_AMP_AND_FAIL(j*m1.size()+i, m2[i][j], m1[i][j]);
141
142 SUCCEED( );
143}
144
145
146
147/*
148 * REAL AND COMPLEX SCALARS
149 */
150
151
152void REPORT_SCALAR_AND_FAIL( qcomp scalar, qcomp reference ) {
153 qreal absolute_difference = getAbsDif(scalar, reference);
154 qreal relative_difference = getRelDif(scalar, reference);
155 CAPTURE(
156 scalar, reference,
157 absolute_difference, ABSOLUTE_EPSILON,
158 relative_difference, RELATIVE_EPSILON
159 );
160 FAIL( );
161}
162
163void REPORT_SCALAR_AND_FAIL( qreal scalar, qreal reference ) {
164
165 // like above but does not display redundant imag-components
166 qreal absolute_difference = getAbsDif(qcomp(scalar,0), qcomp(reference,0));
167 qreal relative_difference = getRelDif(qcomp(scalar,0), qcomp(reference,0));
168 CAPTURE(
169 scalar, reference,
170 absolute_difference, ABSOLUTE_EPSILON,
171 relative_difference, RELATIVE_EPSILON
172 );
173 FAIL( );
174}
175
176
177void REQUIRE_AGREE( qcomp scalar, qcomp reference ) {
178
179 if (!doScalarsAgree(scalar, reference))
180 REPORT_SCALAR_AND_FAIL(scalar, reference);
181
182 SUCCEED( );
183}
184
185void REQUIRE_AGREE( qreal scalar, qreal reference ) {
186
187 if (!doScalarsAgree(qcomp(scalar,0), qcomp(reference,0)))
188 REPORT_SCALAR_AND_FAIL(scalar, reference);
189
190 SUCCEED( );
191}
192
193
194
195/*
196 * LISTS
197 */
198
199
200void REQUIRE_AGREE( vector<qreal> apiList, vector<qreal> refList ) {
201 DEMAND( apiList.size() == refList.size() );
202
203 for (size_t i=0; i<apiList.size(); i++)
204 if (!doScalarsAgree(apiList[i], refList[i]))
205 REPORT_SCALAR_AND_FAIL(apiList[i], refList[i]);
206
207 SUCCEED( );
208}
209
210
211void REQUIRE_AGREE( vector<qcomp> apiList, vector<qcomp> refList ) {
212 DEMAND( apiList.size() == refList.size() );
213
214 for (size_t i=0; i<apiList.size(); i++)
215 if (!doScalarsAgree(apiList[i], refList[i]))
216 REPORT_SCALAR_AND_FAIL(apiList[i], refList[i]);
217
218 SUCCEED( );
219}
220
221
222
223/*
224 * MATRICES
225 */
226
227
228void REPORT_ELEM_AND_FAIL( size_t row, size_t col, qcomp elem, qcomp reference ) {
229 qreal absolute_difference = getAbsDif(elem, reference);
230 qreal relative_difference = getRelDif(elem, reference);
231 CAPTURE(
232 row, col, elem, reference,
233 absolute_difference, ABSOLUTE_EPSILON,
234 relative_difference, RELATIVE_EPSILON
235 );
236 FAIL( );
237}
238
239
240void REQUIRE_AGREE( qmatrix matrix, qmatrix reference ) {
241 DEMAND( matrix.size() == reference.size() );
242
243 size_t dim = matrix.size();
244
245 for (size_t r=0; r<dim; r++)
246 for (size_t c=0; c<dim; c++)
247 if (!doScalarsAgree(matrix[r][c], reference[r][c]))
248 REPORT_ELEM_AND_FAIL(r, c, matrix[r][c], reference[r][c]);
249
250 SUCCEED( );
251}
252
253
254void REQUIRE_AGREE( CompMatr1 matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
255void REQUIRE_AGREE( CompMatr2 matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
256void REQUIRE_AGREE( CompMatr matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
257void REQUIRE_AGREE( DiagMatr1 matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
258void REQUIRE_AGREE( DiagMatr2 matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
259void REQUIRE_AGREE( DiagMatr matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
260void REQUIRE_AGREE( SuperOp matrix, qmatrix reference ) { REQUIRE_AGREE( getMatrix(matrix), reference ); }
261
262
263
264/*
265 * we lazily compare quregs by converting one into qvector
266 * or qmatrix, rather than an optimised direct comparison,
267 * so that we can compare the states of distinct deployments
268 */
269
270
271void REQUIRE_AGREE( Qureg qureg, Qureg other ) {
272 DEMAND( qureg.numQubits == other.numQubits );
273 DEMAND( qureg.isDensityMatrix == other.isDensityMatrix );
274
275 return (qureg.isDensityMatrix)?
276 REQUIRE_AGREE( qureg, getMatrix(other) ):
277 REQUIRE_AGREE( qureg, getVector(other) );
278}
Definition qureg.h:49