Loading [MathJax]/extensions/tex2jax.js
The Quantum Exact Simulation Toolkit v4.0.0
 v4.0.0
All Classes Functions Variables Typedefs Modules Pages
calculations.cpp
1/** @file
2 * API definitions for calculating properties of quantum states,
3 * such as probabilities, expectation values and partial traces.
4 *
5 * @author Tyson Jones
6 * @author Balint Koczor (prototyped v3 calcHilbertSchmidtDistance and calcDensityInnerProduct)
7 */
8
9#include "quest/include/types.h"
10#include "quest/include/qureg.h"
11#include "quest/include/paulis.h"
12#include "quest/include/calculations.h"
13
14#include "quest/src/core/validation.hpp"
15#include "quest/src/core/utilities.hpp"
16#include "quest/src/core/localiser.hpp"
17#include "quest/src/core/bitwise.hpp"
18#include "quest/src/core/errors.hpp"
19
20#include <vector>
21using std::vector;
22
23
24
25/*
26 * INTERNAL FUNCTIONS
27 */
28
29
30extern Qureg validateAndCreateCustomQureg(
31 int numQubits, int isDensMatr, int useDistrib,
32 int useGpuAccel, int useMultithread, const char* caller);
33
34
35
36/*
37 * C++ ONLY FUNCTIONS
38 *
39 * which are not directly C-compatible because of limited
40 * interoperability of the qcomp type. See calculations.h
41 * for more info. We here define a C++-only signature (with
42 * name-mangling), and a C-friendly wrapper which passes by
43 * pointer; the C-friendly interface in wrappers.h which itself
44 * wrap this. This excludes additional convenience C++-only
45 * overloads defined at the bottom of this file.
46 */
47
48
49qcomp calcInnerProduct(Qureg quregA, Qureg quregB) {
50 validate_quregFields(quregA, __func__);
51 validate_quregFields(quregB, __func__);
52 validate_quregsCanBeProducted(quregA, quregB, __func__);
53
54 // <A|B> or Tr(A^dagger B) = <<A|B>>
55 if (quregA.isDensityMatrix == quregB.isDensityMatrix)
56 return localiser_statevec_calcInnerProduct(quregA, quregB);
57
58 return (quregA.isDensityMatrix)?
59 localiser_densmatr_calcFidelityWithPureState(quregA, quregB, true): // <B|A^dagger|B>
60 localiser_densmatr_calcFidelityWithPureState(quregB, quregA, false); // <A|B|A>
61}
62extern "C" void _wrap_calcInnerProduct(Qureg bra, Qureg ket, qcomp* out) {
63
64 *out = calcInnerProduct(bra, ket);
65}
66
67
69 validate_quregFields(qureg, __func__);
70 validate_pauliStrSumFields(sum, __func__);
71 validate_pauliStrSumTargets(sum, qureg, __func__);
72
73 qcomp value = (qureg.isDensityMatrix)?
74 localiser_densmatr_calcExpecPauliStrSum(qureg, sum):
75 localiser_statevec_calcExpecPauliStrSum(qureg, sum);
76
77 return value;
78}
79extern "C" void _wrap_calcExpecNonHermitianPauliStrSum(qcomp* out, Qureg qureg, PauliStrSum sum) {
80
81 *out = calcExpecNonHermitianPauliStrSum(qureg, sum);
82}
83
84
86 validate_quregFields(qureg, __func__);
87 validate_matrixFields(matrix, __func__);
88 validate_matrixAndQuregAreCompatible(matrix, qureg, true, __func__);
89
90 return calcExpecNonHermitianFullStateDiagMatrPower(qureg, matrix, 1); // harmlessly re-validates
91}
92extern "C" void _wrap_calcExpecNonHermitianFullStateDiagMatr(qcomp* out, Qureg qureg, FullStateDiagMatr matr) {
93
95}
96
97
99 validate_quregFields(qureg, __func__);
100 validate_matrixFields(matrix, __func__);
101 validate_matrixAndQuregAreCompatible(matrix, qureg, true, __func__);
102
103 // this function never uses the qreal-pow overload (because we make
104 // no assumption matrix is Hermitian i.e. real), and instead always
105 // uses the relatively numerically inaccurate qcomp overload
106 const bool useRealPow = false;
107
108 return (qureg.isDensityMatrix)?
109 localiser_densmatr_calcExpecFullStateDiagMatr(qureg, matrix, exponent, useRealPow):
110 localiser_statevec_calcExpecFullStateDiagMatr(qureg, matrix, exponent, useRealPow);
111}
112extern "C" void _wrap_calcExpecNonHermitianFullStateDiagMatrPower(qcomp* out, Qureg qureg, FullStateDiagMatr matr, qcomp exponent) {
113
114 *out = calcExpecNonHermitianFullStateDiagMatrPower(qureg, matr, exponent);
115}
116
117
118
119/*
120 * C AND C++ AGNOSTIC FUNCTIONS
121 */
122
123// enable invocation by both C and C++ binaries
124extern "C" {
125
126
127
128/*
129 * EXPECTED VALUES
130 */
131
132
134 validate_quregFields(qureg, __func__);
135 validate_pauliStrTargets(qureg, str, __func__);
136
137 qcomp value = (qureg.isDensityMatrix)?
138 localiser_densmatr_calcExpecPauliStr(qureg, str):
139 localiser_statevec_calcExpecPauliStr(qureg, str);
140
141 validate_expecPauliStrValueIsReal(value, qureg.isDensityMatrix, __func__);
142 return std::real(value);
143}
144
145
147 validate_quregFields(qureg, __func__);
148 validate_pauliStrSumFields(sum, __func__);
149 validate_pauliStrSumTargets(sum, qureg, __func__);
150 validate_pauliStrSumIsHermitian(sum, __func__);
151
152 qcomp value = (qureg.isDensityMatrix)?
153 localiser_densmatr_calcExpecPauliStrSum(qureg, sum):
154 localiser_statevec_calcExpecPauliStrSum(qureg, sum);
155
156 validate_expecPauliStrSumValueIsReal(value, qureg.isDensityMatrix, __func__);
157 return std::real(value);
158}
159
160
162 validate_quregFields(qureg, __func__);
163 validate_matrixFields(matrix, __func__);
164 validate_matrixAndQuregAreCompatible(matrix, qureg, true, __func__);
165 validate_matrixIsHermitian(matrix, __func__);
166
167 // unused parameters; see calcExpecFullStateDiagMatrPower() for explanation
168 qcomp exponent = qcomp(1,0);
169 bool useRealPow = false;
170
171 qcomp value = (qureg.isDensityMatrix)?
172 localiser_densmatr_calcExpecFullStateDiagMatr(qureg, matrix, exponent, useRealPow):
173 localiser_statevec_calcExpecFullStateDiagMatr(qureg, matrix, exponent, useRealPow);
174
175 // the sub-epsilon imaginary components in matrix never damage the real
176 // component of the statevector expectation value, so we do not validate
177 // imag(value)~0; we can always safely discard it, and only validate for densmatr:
178 if (qureg.isDensityMatrix)
179 validate_densMatrExpecDiagMatrValueIsReal(value, exponent, __func__);
180
181 return std::real(value);
182}
183
184
185qreal calcExpecFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr matrix, qreal exponent) {
186 validate_quregFields(qureg, __func__);
187 validate_matrixFields(matrix, __func__);
188 validate_matrixAndQuregAreCompatible(matrix, qureg, true, __func__);
189 validate_matrixExpIsHermitian(matrix, exponent, __func__);
190
191 // the backend can use either the pow(qcomp,qcomp) or pow(qreal,qreal) overload;
192 // the former is significantly less accurate when the base is real & negative and
193 // the exponent is real, because complex pow(a,b) = exp(i b Arg(a)) = exp(i b 2 pi),
194 // and the numerical error in pi is compounded by the exponent and the exp(). Because
195 // this function assumes approx/intended Hermiticity, it will always use the real-pow
196 // overload, discarding the imaginary components of 'matrix' during computation - this
197 // is a behaviour unique to this function (other functions collect the erroneous
198 // imaginary components before a final validation and discarding).
199 const bool useRealPow = true;
200
201 qcomp value = (qureg.isDensityMatrix)?
202 localiser_densmatr_calcExpecFullStateDiagMatr(qureg, matrix, exponent, useRealPow):
203 localiser_statevec_calcExpecFullStateDiagMatr(qureg, matrix, exponent, useRealPow);
204
205 // is it impossible for the statevector routine to produce non-zero
206 // imaginary components because of our use of real-pow, the result of
207 // which is multiplied by abs(amp). Alas, density matrices multiply the
208 // result with a complex scalar and can accrue erroneous imaginary
209 // components when the density matrix is non-Hermitian, or due to rounding
210 // errors. As such, we only post-validate density matrix values.
211 if (qureg.isDensityMatrix)
212 validate_densMatrExpecDiagMatrValueIsReal(value, exponent, __func__);
213
214 return std::real(value);
215}
216
217
218
219/*
220 * PROBABILITIES
221 */
222
223
224qreal calcProbOfBasisState(Qureg qureg, qindex index) {
225 validate_quregFields(qureg, __func__);
226 validate_basisStateIndex(qureg, index, __func__);
227
228 // |i><i| = ||(1+2^N)i>>
229 if (qureg.isDensityMatrix)
230 index *= 1 + powerOf2(qureg.numQubits);
231
232 qcomp amp = localiser_statevec_getAmp(qureg, index);
233 qreal prob = (qureg.isDensityMatrix)?
234 std::real(amp):
235 std::norm(amp);
236
237 return prob;
238}
239
240
241qreal calcProbOfQubitOutcome(Qureg qureg, int qubit, int outcome) {
242 validate_quregFields(qureg, __func__);
243 validate_target(qureg, qubit, __func__);
244 validate_measurementOutcomeIsValid(outcome, __func__);
245
246 int numQubits = 1;
247 return calcProbOfMultiQubitOutcome(qureg, &qubit, &outcome, numQubits);
248}
249
250
251qreal calcProbOfMultiQubitOutcome(Qureg qureg, int* qubits, int* outcomes, int numQubits) {
252 validate_quregFields(qureg, __func__);
253 validate_targets(qureg, qubits, numQubits, __func__);
254 validate_measurementOutcomesAreValid(outcomes, numQubits, __func__);
255
256 auto qubitVec = util_getVector(qubits, numQubits);
257 auto outcomeVec = util_getVector(outcomes, numQubits);
258
259 return (qureg.isDensityMatrix)?
260 localiser_densmatr_calcProbOfMultiQubitOutcome(qureg, qubitVec, outcomeVec):
261 localiser_statevec_calcProbOfMultiQubitOutcome(qureg, qubitVec, outcomeVec);
262}
263
264
265void calcProbsOfAllMultiQubitOutcomes(qreal* outcomeProbs, Qureg qureg, int* qubits, int numQubits) {
266 validate_quregFields(qureg, __func__);
267 validate_targets(qureg, qubits, numQubits, __func__);
268 validate_measurementOutcomesFitInGpuMem(qureg, numQubits, __func__);
269
270 auto qubitVec = util_getVector(qubits, numQubits);
271
272 (qureg.isDensityMatrix)?
273 localiser_densmatr_calcProbsOfAllMultiQubitOutcomes(outcomeProbs, qureg, qubitVec):
274 localiser_statevec_calcProbsOfAllMultiQubitOutcomes(outcomeProbs, qureg, qubitVec);
275}
276
277
278qreal calcTotalProb(Qureg qureg) {
279 validate_quregFields(qureg, __func__);
280
281 return (qureg.isDensityMatrix)?
282 localiser_densmatr_calcTotalProb(qureg):
283 localiser_statevec_calcTotalProb(qureg);
284}
285
286
287
288/*
289 * MEASURES
290 */
291
292
293qreal calcPurity(Qureg qureg) {
294 validate_quregFields(qureg, __func__);
295
296 // assuming Hermiticity, Tr(rho^2) = sum_ij |rho_ij|^2,
297 // and Tr(|x><x|x><x|) = (sum_i |x_i|^2)^2
298 qreal prob = localiser_statevec_calcTotalProb(qureg);
299 return (qureg.isDensityMatrix)? prob : prob * prob;
300}
301
302
303qreal calcFidelity(Qureg quregA, Qureg quregB) {
304 validate_quregFields(quregA, __func__);
305 validate_quregFields(quregB, __func__);
306 validate_quregsCanBeProducted(quregA, quregB, __func__);
307
308 bool isDensA = quregA.isDensityMatrix;
309 bool isDensB = quregB.isDensityMatrix;
310
311 // F(rho,sigma) not yet supported
312 if (isDensA && isDensB)
313 validate_throwErrorBecauseCalcFidOfDensMatrNotYetImplemented(__func__);
314
315 // |<A|B>|^2
316 if (!isDensA && !isDensB) {
317 qcomp prod = localiser_statevec_calcInnerProduct(quregA, quregB);
318 return std::norm(prod);
319 }
320
321 // Re[<B|A|B>] or Re[<A|B|A>]
322 qcomp fid = (quregA.isDensityMatrix)?
323 localiser_densmatr_calcFidelityWithPureState(quregA, quregB, false): // no conj
324 localiser_densmatr_calcFidelityWithPureState(quregB, quregA, false); // no conj
325
326 validate_fidelityIsReal(fid, __func__);
327 return std::real(fid);
328}
329
330
331qreal calcDistance(Qureg quregA, Qureg quregB) {
332 validate_quregFields(quregA, __func__);
333 validate_quregFields(quregB, __func__);
334 validate_quregsCanBeProducted(quregA, quregB, __func__);
335
336 bool isDensA = quregA.isDensityMatrix;
337 bool isDensB = quregB.isDensityMatrix;
338
339 // Hilbert-Schmidt = sqrt( Tr((A-B)(A-B)^dagger) = sqrt(sum_ij |A_ij - B_ij|^2)
340 if (isDensA && isDensB) {
341 qreal dif = localiser_densmatr_calcHilbertSchmidtDistance(quregA, quregB);
342 return std::sqrt(dif);
343 }
344
345 // Bures = sqrt(2 - 2 |<A|B>|) (even when unnormalised)
346 if (!isDensA && !isDensB) {
347 qcomp prod = localiser_statevec_calcInnerProduct(quregA, quregB);
348 qreal mag = std::abs(prod);
349
350 validate_buresDistanceInnerProdIsNormalised(mag, __func__);
351 mag = (mag > 1)? 1 : mag; // forgive eps error to avoid complex
352 return std::sqrt(2 - 2 * mag);
353 }
354
355 // purified distance = sqrt(1 - <psi|rho|psi>)
356 qcomp fid = (quregA.isDensityMatrix)?
357 localiser_densmatr_calcFidelityWithPureState(quregA, quregB, false): // no conj
358 localiser_densmatr_calcFidelityWithPureState(quregB, quregA, false); // no conj
359
360 validate_purifiedDistanceIsNormalised(fid, __func__);
361 qreal re = std::real(fid);
362 re = (re > 1)? 1 : re; // forgive eps error to avoid complex
363 return std::sqrt(1 - re);
364}
365
366
367
368/*
369 * PARTIAL TRACE
370 */
371
372
373Qureg calcPartialTrace(Qureg qureg, int* traceOutQubits, int numTraceQubits) {
374 validate_quregFields(qureg, __func__);
375 validate_quregIsDensityMatrix(qureg, __func__);
376 validate_targets(qureg, traceOutQubits, numTraceQubits, __func__);
377 validate_quregCanBeReduced(qureg, numTraceQubits, __func__);
378
379 // attempt to create reduced Qureg with same deployments as input Qureg
380 Qureg out = validateAndCreateCustomQureg(
381 qureg.numQubits - numTraceQubits,
382 qureg.isDensityMatrix, qureg.isDistributed,
383 qureg.isGpuAccelerated, qureg.isMultithreaded, __func__);
384
385 // set it to reduced density matrix
386 auto targets = util_getVector(traceOutQubits, numTraceQubits);
387 localiser_densmatr_partialTrace(qureg, out, targets);
388
389 return out;
390}
391
392
393Qureg calcReducedDensityMatrix(Qureg qureg, int* retainQubits, int numRetainQubits) {
394 validate_quregFields(qureg, __func__);
395 validate_quregIsDensityMatrix(qureg, __func__);
396 validate_targets(qureg, retainQubits, numRetainQubits, __func__);
397 validate_quregCanBeReduced(qureg, qureg.numQubits - numRetainQubits, __func__);
398
399 auto traceQubits = util_getNonTargetedQubits(retainQubits, numRetainQubits, qureg.numQubits);
400
401 // harmlessly re-validates
402 return calcPartialTrace(qureg, traceQubits.data(), traceQubits.size());
403}
404
405
406} // end de-name mangler
407
408
409
410/*
411 * C++ OVERLOADS
412 *
413 * which are only ever accessible to C++ binaries, and
414 * accept arguments more natural to C++ (e.g. std::vector).
415 */
416
417
418qreal calcProbOfMultiQubitOutcome(Qureg qureg, vector<int> qubits, vector<int> outcomes) {
419
420 // C interface discards individual sizes, so we validate
421 validate_measurementOutcomesMatchTargets(qubits.size(), outcomes.size(), __func__);
422
423 // but C function performs all other validation
424 return calcProbOfMultiQubitOutcome(qureg, qubits.data(), outcomes.data(), qubits.size());
425}
426
427
428vector<qreal> calcProbsOfAllMultiQubitOutcomes(Qureg qureg, vector<int> qubits) {
429
430 // allocate temp vector, and validate successful (since it's exponentially large!)
431 vector<qreal> out;
432 qindex numOut = powerOf2(qubits.size());
433 auto callback = [&]() { validate_tempAllocSucceeded(false, numOut, sizeof(qreal), __func__); };
434 util_tryAllocVector(out, numOut, callback);
435
436 calcProbsOfAllMultiQubitOutcomes(out.data(), qureg, qubits.data(), qubits.size());
437 return out;
438}
439
440Qureg calcPartialTrace(Qureg qureg, vector<int> traceOutQubits) {
441 return calcPartialTrace(qureg, traceOutQubits.data(), traceOutQubits.size());
442}
443
444
445Qureg calcReducedDensityMatrix(Qureg qureg, vector<int> retainQubits) {
446 return calcReducedDensityMatrix(qureg, retainQubits.data(), retainQubits.size());
447}
448
qcomp calcInnerProduct(Qureg quregA, Qureg quregB)
qreal calcFidelity(Qureg quregA, Qureg quregB)
qreal calcDistance(Qureg quregA, Qureg quregB)
qreal calcExpecFullStateDiagMatr(Qureg qureg, FullStateDiagMatr matrix)
qreal calcExpecFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr matrix, qreal exponent)
qcomp calcExpecNonHermitianFullStateDiagMatr(Qureg qureg, FullStateDiagMatr matrix)
qreal calcExpecPauliStrSum(Qureg qureg, PauliStrSum sum)
qcomp calcExpecNonHermitianPauliStrSum(Qureg qureg, PauliStrSum sum)
qcomp calcExpecNonHermitianFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr matrix, qcomp exponent)
qreal calcExpecPauliStr(Qureg qureg, PauliStr str)
Qureg calcReducedDensityMatrix(Qureg qureg, int *retainQubits, int numRetainQubits)
Qureg calcPartialTrace(Qureg qureg, int *traceOutQubits, int numTraceQubits)
qreal calcProbOfQubitOutcome(Qureg qureg, int qubit, int outcome)
void calcProbsOfAllMultiQubitOutcomes(qreal *outcomeProbs, Qureg qureg, int *qubits, int numQubits)
qreal calcProbOfBasisState(Qureg qureg, qindex index)
qreal calcProbOfMultiQubitOutcome(Qureg qureg, int *qubits, int *outcomes, int numQubits)
qreal calcPurity(Qureg qureg)
qreal calcTotalProb(Qureg qureg)
Definition qureg.h:49