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