The Quantum Exact Simulation Toolkit v4.3.0
Loading...
Searching...
No Matches
calculations.h
1/** @file
2 * API signatures for calculating properties of quantum states,
3 * such as probabilities, expectation values and partial traces.
4 *
5 * @author Tyson Jones
6 *
7 * @defgroup calculations Calculations
8 * @ingroup api
9 * @brief Functions for calculating properties of quantum states without modifying them.
10 * @{
11 */
12
13#ifndef CALCULATIONS_H
14#define CALCULATIONS_H
15
16#include "quest/include/types.h"
17#include "quest/include/qureg.h"
18#include "quest/include/paulis.h"
19#include "quest/include/matrices.h"
20
21
22
23/*
24 * These signatures are divided into three partitions; those which are
25 * natively C and C++ compatible (first partition), then those which are
26 * only exposed to C++ (second partition) because they return 'qcomp'
27 * which cannot cross the C++-to-C ABI, and then finally the C++-only
28 * convenience overloads. The first partition defines the doc groups, and
29 * the latter partition functions are added into them.
30 */
31
32
33
34/*
35 * C AND C++ AGNOSTIC FUNCTIONS
36 */
37
38// enable invocation by both C and C++ binaries
39#ifdef __cplusplus
40extern "C" {
41#endif
42
43
44
45/**
46 * @defgroup calc_expec Expectation values
47 * @brief Functions for calculating expected values of Hermitian observables.
48 * @{
49 */
50
51
52/** Calculates the expectation value of the given Pauli string observable @p str under the given
53 * state @p qureg without modifying it.
54 *
55 * @formulae
56 *
57 * Let @f$ \pstr = @f$ @p str, which notates a tensor product of single-qubit Pauli operators.
58 * - When @p qureg is a statevector @f$\svpsi@f$, this function returns
59 * @f[
60 \brapsi \pstr \svpsi \in \mathbb{R}.
61 * @f]
62 * - When @p qureg is a density matrix @f$\dmrho@f$, this function returns the real component of
63 * @f[
64 \tr{ \pstr \dmrho }
65 * @f]
66 * which is exact when @f$\dmrho@f$ is physical (specifically Hermitian).
67 *
68 * @constraints
69 *
70 * - Postcondition validation will check that the calculated expectation value is approximately
71 * real (i.e. the imaginary component is smaller in size than the validation epsilon), as admitted
72 * when @p qureg is correctly normalised. This behaviour can be adjusted using setQuESTValidationEpsilon().
73 * - Regardless of the validation epsilon, the returned value is always real and the imaginary component
74 * is discarded. The full complex value can be obtained using calcExpecNonHermitianPauliStrSum().
75 *
76 * @equivalences
77 *
78 * - When @p str is general, this function is equivalent to calling calcExpecPauliStrSum() with a
79 * PauliStrSum composed of only a single PauliStr term and a unity coefficient.
80 * - When @p str @f$ = \id^\otimes @f$, the output is equivalent to that of calcTotalProb().
81 *
82 * @myexample
83 * ```
84 Qureg qureg = createQureg(10);
85 initRandomPureState(qureg);
86
87 PauliStr str = getInlinePauliStr("XYZ", {0,2,3});
88
89 qreal expec = calcExpecPauliStr(qureg, str);
90 reportScalar("expec", expec);
91 * ```
92 *
93 * @see
94 * - calcExpecPauliStrSum()
95 * - calcExpecFullStateDiagMatr()
96 * @param[in] qureg the reference state.
97 * @param[in] str the observable operator.
98 * @returns The real component of the expectation value.
99 * @throws @validationerror
100 * - if @p qureg is uninitialised.
101 * - if @p str contains a (non-identity) Pauli upon a higher-index qubit than exists in @p qureg.
102 * - if the output (with unreturned imaginary component) is not approximately real.
103 * @notyetvalidated
104 * @author Tyson Jones
105 */
106qreal calcExpecPauliStr(Qureg qureg, PauliStr str);
107
108
109/** Calculates the expectation value of the given Hermitian observable @p sum - a weighted sum of
110 * Pauli strings - under the given state @p qureg, without modifying it.
111 *
112 * @formulae
113 *
114 * Let @f$ \hat{H} = @f$ @p sum.
115 * - When @p qureg is a statevector @f$\svpsi@f$, this function returns
116 * @f[
117 \brapsi \hat{H} \svpsi \in \mathbb{R}.
118 * @f]
119 * - When @p qureg is a density matrix @f$\dmrho@f$, this function returns the real component of
120 * @f[
121 \tr{ \hat{H} \dmrho }
122 * @f]
123 * which is the exact expectation value when @f$\dmrho@f$ is physical (or at least, Hermitian).
124 *
125 * @constraints
126 *
127 * - Hermiticity of @p sum requires that every coefficient within is real.
128 * Validation will check @p sum is _approximately_ Hermitian, i.e. that
129 * @f[
130 |\im{c}| \le \valeps
131 * @f]
132 * for all @f$c \in @f$ `sum.coeffs`. Adjust @f$\valeps@f$ using setQuESTValidationEpsilon().
133 * The sub-epsilon imaginary components of the coefficients _are_ included in calculation.
134 * - Postcondition validation will check that the calculated expectation value is approximately
135 * real (i.e. the imaginary component is smaller in size than the validation epsilon), as should be
136 * admitted when @p qureg is correctly normalised, and @p sum is Hermitian.
137 * - The returned value is always real, and the imaginary component is neglected even when
138 * Hermiticity validation is relaxed and/or @p qureg is an unnormalised density matrix.
139 * The full complex value can be obtained using calcExpecNonHermitianPauliStrSum().
140 *
141 * @equivalences
142 *
143 * - This function is mathematically equivalent to (albeit faster than) calling calcExpecPauliStr() upon
144 * each constituent @p PauliStr within @p sum, weighting each by its corresponding coefficient, and
145 * summing the outputs.
146 * - When @p sum contains only @f$\pauliz@f$ and @f$\id@f$ operators, its corresponding operator matrix
147 * is diagonal, and could be instead effected with calcExpecFullStateDiagMatr(). This may be faster when
148 * @p sum contains very-many terms and operates upon all qubits of the register.
149 *
150 * @myexample
151 * ```
152 Qureg qureg = createQureg(5);
153 PauliStrSum sum = createInlinePauliStrSum(R"(
154 0.123 XXIXX
155 1.234 XYZXZ
156 -1E-2 IIIII
157 )");
158
159 qreal expec = calcExpecPauliStrSum(qureg, sum);
160 reportScalar("expec", expec);
161 * ```
162 *
163 * @param[in] qureg the reference state.
164 * @param[in] sum the observable operator.
165 * @returns The real component of the expectation value.
166 * @throws @validationerror
167 * - if @p qureg or @p sum are uninitialised.
168 * - if any PauliStr in @p sum targets a higher-index qubit than exists in @p qureg.
169 * - if @p sum is not approximately Hermitian.
170 * - if the output (with unreturned imaginary component) is not approximately real.
171* @notyetvalidated
172 * @see
173 * - calcExpecNonHermitianPauliStrSum()
174 * - calcExpecFullStateDiagMatr()
175 * @author Tyson Jones
176 */
177qreal calcExpecPauliStrSum(Qureg qureg, PauliStrSum sum);
178
179
180/** Calculates the expectation value of the given Hermitian observable @p matr - a diagonal,
181 * Hermitian matrix spanning the full Hilbert space - under the given state @p qureg, without
182 * modifying it.
183 *
184 * @formulae
185 *
186 * Let @f$ \hat{D} = @f$ @p matr.
187 * - When @p qureg is a statevector @f$\svpsi@f$, this function returns
188 * @f[
189 \brapsi \hat{D} \svpsi \in \mathbb{R}.
190 * @f]
191 * - When @p qureg is a density matrix @f$\dmrho@f$, this function returns the real component of
192 * @f[
193 \tr{ \hat{D} \dmrho }
194 * @f]
195 * which is the exact expectation value when @f$\dmrho@f$ is physical (or at least, Hermitian).
196 *
197 * @constraints
198 *
199 * - Hermiticity of @p matr requires that every element within is real.
200 * Validation will check @p matr is _approximately_ Hermitian, i.e. that
201 * @f[
202 |\im{c}| \le \valeps
203 * @f]
204 * for all @f$c \in @f$ `matr.cpuElems`. Adjust @f$\valeps@f$ using setQuESTValidationEpsilon().
205 * - Postcondition validation will check that the calculated expectation value is approximately
206 * real (i.e. the imaginary component is smaller in size than the validation epsilon), as should be
207 * admitted when @p qureg is correctly normalised, and @p matr is Hermitian.
208 * - The returned value is always real, and the imaginary component is neglected even when @p matr
209 * Hermiticity validation is relaxed and/or @p qureg is an unnormalised density matrix.
210 * The full complex value can be obtained using calcExpecNonHermitianFullStateDiagMatr().
211 *
212 * @equivalences
213 *
214 * - This function is mathematically equivalent to (albeit much faster than) calling calcExpecPauliStrSum()
215 * with a PauliStrSum consisting of all permutations of @f$\hat{I}@f$ and @f$\hat{Z}@f$ Pauli operators
216 * with a precise, linear combination of coefficients.
217 *
218 * @myexample
219 *
220 * ```
221 Qureg qureg = createQureg(5);
222 initPlusState(qureg);
223
224 FullStateDiagMatr matr = createFullStateDiagMatr(qureg.numQubits);
225
226 // profanely inefficient per-element initialisation
227 for (int n=0; n<matr.numElems; n++) {
228 qcomp elem = getQcomp(n, 0);
229 setFullStateDiagMatr(matr, n, &elem, 1);
230 }
231
232 // prints "expec: 15.5"
233 qreal expec = calcExpecFullStateDiagMatr(qureg, matr);
234 reportScalar("expec", expec);
235 * ```
236 *
237 * @param[in] qureg the reference state.
238 * @param[in] matr the observable operator.
239 * @returns The real component of the expectation value.
240 * @throws @validationerror
241 * - if @p qureg or @p matr are uninitialised.
242 * - if @p matr does not match the dimension of @p qureg
243 * - if @p matr is distributed but @p qureg is not
244 * - if @p matr is not approximately Hermitian.
245 * - if the output (with unreturned imaginary component) is not approximately real.
246* @notyetvalidated
247 * @see
248 * - calcExpecFullStateDiagMatrPower()
249 * - calcExpecNonHermitianFullStateDiagMatr()
250 * - calcExpecPauliStrSum()
251 * @author Tyson Jones
252 */
254
255
256/** Calculates the expectation value of the given Hermitian observable @p matrix - a diagonal,
257 * Hermitian matrix spanning the full Hilbert space - when raised to the given @p exponent,
258 * under the given state @p qureg, which is not modified.
259 *
260 * @formulae
261 *
262 * Let @f$ \hat{D} = @f$ @p matrix and @f$x = @f$ @p exponent.
263 * - When @p qureg is a statevector @f$\svpsi@f$, this function returns
264 * @f[
265 \brapsi \hat{D}^x \svpsi \in \mathbb{R}.
266 * @f]
267 * - When @p qureg is a density matrix @f$\dmrho@f$, this function returns the real component of
268 * @f[
269 \tr{ \hat{D}^x \dmrho }
270 * @f]
271 * which is the exact expectation value when @f$\dmrho@f$ is physical (or at least, Hermitian).
272 *
273 * @constraints
274 *
275 * - Hermiticity of @p matrix itself requires that every element within is real.
276 * Validation will check @p matrix is _approximately_ Hermitian, i.e. that
277 * @f[
278 |\im{c}| \le \valeps
279 * @f]
280 * for all @f$c \in @f$ `matr.cpuElems`. Adjust @f$\valeps@f$ using setQuESTValidationEpsilon().
281 *
282 * > [!CAUTION]
283 * > Unlike other functions (including calcExpecFullStateDiagMatr()), this function will _NOT_
284 * > consult the imaginary components of the elements of @p matrix, since a non-complex exponentiation
285 * > function is used. That is, while validation permits the imaginary components to be small, they
286 * > will be internally treated as precisely zero. This is true even when Hermiticity validation
287 * > is disabled using setQuESTValidationOff(). To consult the imaginary components of @p matrix, use
288 * > calcExpecNonHermitianFullStateDiagMatrPower().
289 *
290 * - Hermiticity of @p matrix when raised to @p exponent further requires that, when @p exponent is
291 * a non-integer, @p matrix does not contain any negative elements which would otherwise produce
292 * complex elements in @f$\hat{D}^x@f$. This validation is always strict (i.e. independent of
293 * @f$\valeps@f$), and demands that
294 * @f[
295 \min(\hat{D}) \ge 0 \text{ when } x \notin \mathbb{R}.
296 * @f]
297 * - Numerical stability requires that if @p exponent is negative, @p matrix does not contain any
298 * zero elements which would otherwise create divergences in @f$\hat{D}^x@f$. Validation ergo
299 * checks that when @p exponent is (strictly) negative, @p matrix contains no elements within
300 * distance @f$\valeps@f$ to zero (regardless of the magnitude of @p exponent). Adjust
301 * @f$\valeps@f$ using setQuESTValidationEpsilon().
302 * - The passed @p exponent is always real, but can be relaxed to a general complex scalar via
303 * calcExpecNonHermitianFullStateDiagMatrPower().
304 * - The returned value is always real, and the imaginary component is neglected even when
305 * Hermiticity validation is relaxed and/or @p qureg is an unnormalised density matrix.
306 * The full complex value can be obtained using calcExpecNonHermitianFullStateDiagMatrPower().
307 *
308 * @equivalences
309 *
310 * - When @p exponent is @c 1, this function is equivalent to calcExpecFullStateDiagMatr().
311 *
312 * @myexample
313 * ```
314 Qureg qureg = createQureg(5);
315 initPlusState(qureg);
316
317 FullStateDiagMatr matrix = createFullStateDiagMatr(qureg.numQubits);
318
319 // profanely inefficient per-element initialisation
320 for (int n=0; n<matrix.numElems; n++) {
321 qcomp elem = getQcomp(n+1, 0);
322 setFullStateDiagMatr(matrix, n, &elem, 1);
323 }
324
325 // prints "expec: 0.044503"
326 qreal exponent = -2.3;
327 qreal expec = calcExpecFullStateDiagMatrPower(qureg, matrix, exponent);
328 reportScalar("expec", expec);
329 * ```
330 * @param[in] qureg the reference state.
331 * @param[in] matrix the observable operator.
332 * @param[in] exponent the exponent to which to raise @p matrix
333 * @returns The real component of the expectation value of @p matrix raised to @p exponent.
334 * @throws @validationerror
335 * - if @p qureg or @p matrix are uninitialised.
336 * - if @p matrix does not match the dimension of @p qureg
337 * - if @p matrix is distributed but @p qureg is not
338 * - if @p matrix is not approximately Hermitian.
339 * - if @p exponent is (precisely) non-integer but @p matrix contains (precisely) negative elements.
340 * - if @p exponent is (precisely) negative but @p matrix contains elements which are approximately zero.
341 * - if the output (with unreturned imaginary component) is not approximately real.
342 * @notyetvalidated
343 * @see
344 * - calcExpecNonHermitianFullStateDiagMatrPower()
345 * @author Tyson Jones
346 */
347qreal calcExpecFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr matrix, qreal exponent);
348
349
350/** @} */
351
352
353
354/**
355 * @defgroup calc_prob Probabilities
356 * @brief Functions for non-destructively calculating the probabilities of measurement outcomes.
357 * @{
358 */
359
360
361/** Calculates the probability of the full computational basis state of the specified
362 * @p index. This is the probability that, when measured in the @f$ \hat{Z} @f$ basis,
363 * every qubit of @p qureg is consistent with the bits of @p index.
364 *
365 * Indexing is little-endian and from zero, such that (for example) computational basis state
366 * @f$ \ket{0011} @f$ (where qubits at indices @f$0@f$ and @f$1@f$ are in the @f$\ket{1}@f$ state)
367 * corresponds to @p index @f$ = 3 @f$. The maximum legal @p index of an @f$N@f$-qubit
368 * register is @p index @f$ = 2^N-1 @f$.
369 *
370 * @formulae
371 *
372 * Let @f$ i = @f$ @p index.
373 *
374 * - When @p qureg is a statevector @f$ \svpsi @f$, this function returns
375 * @f[
376 P(i) = |\braket{i}{\psi}|^2 = |\psi_i|^2
377 * @f]
378 * where @f$\psi_i@f$ is the @f$i@f$-th amplitude of @f$\svpsi@f$.
379 * - When @p qureg is a density matrix @f$\dmrho@f$, this function returns
380 * @f[
381 P(i) = \re{ \tr{ \ketbra{i}{i} \dmrho } } = \re{ \bra{i} \dmrho \ket{i} } = \re{ \dmrho_{ii} }
382 * @f]
383 * where @f$ \dmrho_{ii} @f$ is the @f$i@f$-th diagonal element of @f$\dmrho@f$, and is
384 * real whenever @f$ \dmrho @f$ is valid (or at least, Hermitian).
385 *
386 * When @p qureg is correctly normalised, these quantities are within @f$[0, 1]@f$, and satisfy
387 * @f[
388 \sum\limits_{i=0}^{2^N-1} P(i) = 1
389 * @f]
390 * where @f$N@f$ is the number of qubits in @p qureg.
391 *
392 * @equivalences
393 *
394 * - This function is equivalent to obtaining the corresponding @p qureg amplitude directly
395 * and evaluating the probability.
396 * ```
397 // qureg is statevector
398 qcomp amp = getQuregAmp(qureg, index);
399 qreal prob = pow(abs(amp, 2));
400
401 // qureg is a density matrix
402 qcomp amp = getDensityQuregAmp(qureg, index, index);
403 qreal prob = real(amp);
404 * ```
405 * - This function is slightly faster than, but otherwise mathematically equivalent to, invoking
406 * calcProbOfMultiQubitOutcome() and passing explicitly the bits of @p index. I.e.
407 * ```
408 int qubits[qureg.numQubits];
409 int outcomes[qureg.numQubits];
410
411 for (int q=0; q<qureg.numQubits; q++) {
412 qubits[q] = q;
413 outcomes[q] = (index >> q) & 1;
414 }
415
416 qreal prob = calcProbOfMultiQubitOutcome(qureg, qubits, outcomes, qureg.numQubits);
417 * ```
418 * Use of calcProbOfMultiQubitOutcome() may be more convenient if only the individual qubit
419 * outcomes are known.
420 * - This function is significantly faster than, but mathematically equivalent to, preparing
421 * a secondary Qureg in the basis state @p index and computing their overlap.
422 * ```
423 Qureg alt = createCloneQureg(qureg);
424 initClassicalState(alt, index);
425 qcomp amp = calcInnerProduct(alt, qureg);
426 qreal prob = pow(abs(amp), 2);
427 * ```
428 *
429 * @myexample
430 * ```
431 Qureg qureg = createQureg(5);
432 initPlusState(qureg);
433
434 qreal prob = calcProbOfBasisState(qureg, 2);
435 reportScalar("prob of |00010>", prob);
436 * ```
437 *
438 * @param[in] qureg the reference state, which is unchanged.
439 * @param[in] index the index of the queried basis state among the ordered set of all basis states.
440 * @returns The probability of the basis state at @p index.
441 * @throws @validationerror
442 * - if @p qureg is uninitialised.
443 * - if @p index is less than zero or beyond (or equal to) the dimension of @p qureg.
444* @notyetvalidated
445 * @see
446 * - calcProbOfQubitOutcome()
447 * - calcProbOfMultiQubitOutcome()
448 * - getQuregAmp()
449 * - getDensityQuregAmp()
450 * @author Tyson Jones
451 */
452qreal calcProbOfBasisState(Qureg qureg, qindex index);
453
454
455/** Calculates the probability of the single qubit at index @p qubit being in the
456 * given computational basis @p outcome (`0` or `1`).
457 *
458 * @formulae
459 *
460 * Let @f$ q = @f$ @p qubit and @f$ x = @f$ @p outcome, and let @f$\ketbra{x}{x}_q@f$
461 * notate a projector operating upon qubit @f$ q @f$.
462 *
463 * - When @p qureg is a statevector @f$ \svpsi @f$, this function returns
464 * @f[
465 P_q(x) = \tr{ \ketbra{x}{x}_q \, \ketbra{\psi}{\psi} }
466 = \sum\limits_i |\psi_i|^2 \delta_{x,i_{[q]}}
467 * @f]
468 * where @f$\psi_i@f$ is the @f$i@f$-th amplitude of @f$\svpsi@f$, and @f$i_{[q]}@f$
469 * notates the @f$q@f$-th bit of @f$i@f$.
470 * - When @p qureg is a density matrix @f$ \dmrho @f$, this function returns
471 * @f[
472 P_q(x) = \tr{ \ketbra{x}{x}_q \, \dmrho }
473 = \sum\limits_i \re{ \dmrho_{ii} } \delta_{x,i_{[q]}}
474 * @f]
475 * where @f$ \dmrho_{ii} @f$ is the @f$i@f$-th diagonal element of @f$\dmrho@f$. This
476 * is real whenever @f$\dmrho@f$ is validly normalised (specifically, Hermitian).
477 *
478 * When @p qureg is correctly normalised, these quantities are within @f$[0, 1]@f$, and
479 * satisfy
480 * @f[
481 P_q(x=0) + P_q(x=1) = 1.
482 * @f]
483 *
484 * @equivalences
485 *
486 * - This function is a single-qubit convenience overload of calcProbOfMultiQubitOutcome(),
487 * which itself has optimised implementations for few-qubit outcomes.
488 * ```
489 calcProbOfMultiQubitOutcome(qureg, &qubit, &outcome, 1);
490 * ```
491 * - This function is much faster than, but mathematically equivalent to, summing the probability
492 * of every computational basis state (e.g. via calcProbOfBasisState()) which is consistent
493 * with the given qubit outcome.
494 * ```
495 qreal prob = 0;
496 qindex dim = 1 << qureg.numQubits;
497 for (qindex i=0; i<dim; i++)
498 if (outcome == (i >> qubit) & 1)
499 prob += calcProbOfBasisState(qureg, i);
500 * ```
501 *
502 * @myexample
503 * ```
504 Qureg qureg = createQureg(5);
505
506 int qubit = 2;
507 int outcome = 1;
508 qreal theta = 0.3;
509 applyRotateX(qureg, qubit, theta);
510
511 // prob = cos(theta/2)^2
512 qreal prob = calcProbOfQubitOutcome(qureg, qubit, outcome);
513 * ```
514 *
515 * @param[in] qureg the reference state, which is unchanged.
516 * @param[in] qubit the target qubit to query.
517 * @param[in] outcome the outcome of @p qubit to query (i.e. `0` oe `1`).
518 * @returns The probability that the given qubit is in the given outcome.
519 * @throws @validationerror
520 * - if @p qureg is uninitialised.
521 * - if @p qubit is less than zero or beyond the number of qubits in @p qureg.
522 * - if @p outcome is not `0` or `1`.
523* @notyetvalidated
524 * @see
525 * - calcProbOfMultiQubitOutcome()
526 * @author Tyson Jones
527 */
528qreal calcProbOfQubitOutcome(Qureg qureg, int qubit, int outcome);
529
530
531/** Calculates the probability that the given list of @p qubits are simultaneously in the
532 * respective single-qubit states specified in @p outcomes.
533 *
534 * @formulae
535 *
536 * Let @f$q_j@f$ and @f$x_j@f$ notate the @f$j@f$-th qubit in @p qubits and its respective
537 * outcome in @p outcomes.
538 *
539 * - When @p qureg is a statevector @f$ \svpsi @f$, this function returns
540 * @f[
541 \tr{
542 \bigotimes\limits_j \ketbra{x_j}{x_j}_{q_j} \; \ketbra{\psi}{\psi}
543 }
544 =
545 \sum\limits_i |\psi_i|^2 \prod\limits_j \delta_{x_j, \, i_{[q_j]}}
546 * @f]
547 * where @f$\psi_i@f$ is the @f$i@f$-th amplitude of @f$\svpsi@f$, and
548 * @f$i_{[q]}@f$ notates the @f$q@f$-th bit of @f$i@f$.
549 * - When @p qureg is a density matrix @f$ \dmrho @f$, this function returns
550 * @f[
551 \tr{
552 \bigotimes\limits_j \ketbra{x_j}{x_j}_{q_j} \; \dmrho
553 }
554 =
555 \sum\limits_i \re{\dmrho_{ii}} \prod\limits_j \delta_{x_j, \, i_{[q_j]}}
556 * @f]
557 * where @f$ \dmrho_{ii} @f$ is the @f$i@f$-th diagonal element of @f$\dmrho@f$. This
558 * is real whenever @f$\dmrho@f$ is validly normalised (specifically, Hermitian).
559 *
560 * When @p qureg is correctly normalised, these quantities are within @f$[0, 1]@f$, and their sum
561 * across all possible values of @p outcomes equals one.
562 *
563 * @equivalences
564 *
565 * - The output of this function is equal to that found by in-turn finding the probability of each
566 * qubit being in the specified outcome, then projecting @p qureg into it (i.e. forcing that
567 * measurement outcome). That approach is however slower and modifies @p qureg, whereas this
568 * function leaves @p qureg unchanged.
569 * ```
570 qreal prob = 1;
571 for (int j=0; j<numQubits; j++)
572 prob *= applyForcedQubitMeasurement(qureg, qubits[j], outcomes[j]);
573 * ```
574 *
575 * - This function is much faster than, but mathematically equivalent to, summing the probability
576 * of every computational basis state (e.g. via calcProbOfBasisState()) which is consistent
577 * with the given qubit outcomes.
578 *
579 * @myexample
580 * ```
581 Qureg qureg = createQureg(5);
582 initRandomPureState(qureg);
583
584 int num = 3;
585 int qubits[] = {0, 3, 4};
586 int outcomes[] = {1, 1, 0};
587
588 qreal prob = calcProbOfMultiQubitOutcome(qureg, qubits, outcomes, num);
589 * ```
590 *
591 * @param[in] qureg the reference state, which is unchanged.
592 * @param[in] qubits a list of target qubits to query.
593 * @param[in] outcomes a list of corresponding qubit outcomes (each `0` or `1`).
594 * @param[in] numQubits the length of list @p qubits (and @p outcomes).
595 * @returns The probability that the given qubits are simultaneously in the specified outcomes.
596 * @throws @validationerror
597 * - if @p qureg is uninitialised.
598 * - if @p qubits contains any duplicates.
599 * - if any element of @p qubits is less than zero or beyond the number of qubits in @p qureg.
600 * - if any element of @p outcomes is not `0` or `1`.
601 * - if @p numQubits is less than one or exceeds the number of qubits in @p qureg.
602 * @throws @segfault
603 * - if either of @p qubits or @p outcomes are not lists of length @p numQubits.
604* @notyetvalidated
605 * @see
606 * - calcProbsOfAllMultiQubitOutcomes()
607 * - calcProbOfBasisState()
608 * @author Tyson Jones
609 */
610qreal calcProbOfMultiQubitOutcome(Qureg qureg, int* qubits, int* outcomes, int numQubits);
611
612
613/** Populates @p outcomeProbs with the probabilities of the specified list of @p qubits
614 * being in _all_ of their possible, simultaneous outcomes (of which there are `2^`
615 * @p numQubits).
616 *
617 * The list @p qubits is taken to be in order of _increasing_ significance, determining
618 * the ordering of the output @p outcomeProbs.
619 * For example, if @p qubits @f$ = \{ 1, 3 \} @f$, then @p outcomeProbs will be populated
620 * with _four_ values; the probabilities of qubits @f$(3,1)@f$ being in the respective
621 * simultaneously outcomes @f$(0,0), \, (0,1), \, (1,0) @f$ and @f$(1,1)@f$. In contrast,
622 * @p qubits @f$ = \{ 3, 1 \} @f$ would see the middle two outputs swapped.
623 *
624 * @formulae
625 *
626 * Let @f$ n = @f$ @p numQubits, and @f$ q_i @f$ be the @f$i@f$-th element of @p qubits,
627 * such that @p qubits = @f$ \{ q_0, q_1, \dots, q_{n-1} \} @f$.
628 * Let @f$ P_{\ket{q_{n-1} \dots q_1 q_0}}(\ket{i}) @f$ denote the probability that the specified
629 * substate is in the computational basis substate @f$\ket{i}@f$. Explicitly, that
630 * qubit @f$q_j@f$ is in the outcome given by the @f$j@f$-th bit of @f$n@f$-digit integer
631 * @f$i@f$ (simultaneously for all @f$j@f$).
632 *
633 * Then, this function sets
634 * @f[
635 \text{outcomeProbs}[i] = P_{\ket{q_{n-1} \dots q_1 q_0}}(\ket{i})
636 * @f]
637 * for all @f$i \in \{0, 1, \dots 2^n-1\} @f$.
638 *
639 * Explicitly, expressing substate @f$\ket{i}@f$ in terms of its individual qubits;
640 * @f[
641 \begin{gathered}
642 \text{outcomeProbs}[0] = P_{\ket{q_{n-1} \dots q_1 q_0}}( \ket{0\dots00} ) \\
643 \text{outcomeProbs}[1] = P_{\ket{q_{n-1} \dots q_1 q_0}}( \ket{0\dots01} ) \\
644 \text{outcomeProbs}[2] = P_{\ket{q_{n-1} \dots q_1 q_0}}( \ket{0\dots10} ) \\
645 \text{outcomeProbs}[3] = P_{\ket{q_{n-1} \dots q_1 q_0}}( \ket{0\dots11} ) \\
646 \vdots \\
647 \text{outcomeProbs}[2^n-1] = P_{\ket{q_{n-1} \dots q_1 q_0}}( \ket{1\dots11} )
648 \end{gathered}
649 * @f]
650 *
651 * Each probability is that which would be output by calcProbOfMultiQubitOutcome() when
652 * passed @p qubits and the bits of @f$ i @f$.
653 *
654 * When @p qureg is correctly normalised, all probabilities are within @f$[0, 1]@f$, and
655 * the sum of all elements written to @p outcomeProbs equals one.
656 *
657 * @equivalences
658 *
659 * - This function is significantly faster than, but otherwise equivalent to, populating
660 * each element of @p outcomeProbs in-turn with the output of calcProbOfMultiQubitOutcome().
661 * ```
662 qindex numOut = (1 << numQubits);
663
664 for (qindex i=0; i<numOut; i++) {
665
666 // set outcomes to the bits of i
667 int outcomes[numQubits];
668 for (int j=0; j<numQubits; j++)
669 outcomes[j] = (i >> j) & 1;
670
671 outcomeProbs[i] = calcProbOfMultiQubitOutcome(qureg, qubits, outcomes, numQubits);
672 }
673 * ```
674 *
675 * @myexample
676 * ```
677 Qureg qureg = createQureg(5);
678 initRandomPureState(qureg);
679
680 int num = 3;
681 int qubits[] = {0, 3, 4};
682
683 qreal probs[8];
684 calcProbsOfAllMultiQubitOutcomes(probs, qureg, qubits, num);
685 * ```
686 * @param[out] outcomeProbs the array to which the output is written.
687 * @param[in] qureg the reference state, which is unchanged.
688 * @param[in] qubits a list of target qubits to query.
689 * @param[in] numQubits the length of list @p qubits.
690 * @throws @validationerror
691 * - if @p qureg is uninitialised.
692 * - if @p qubits contains any duplicates.
693 * - if any element of @p qubits is less than zero or beyond the number of qubits in @p qureg.
694 * - if @p numQubits is less than one or exceeds the number of qubits in @p qureg.
695 * @throws @segfault
696 * - if @p outcomeProbs is not a pre-allocated list of length `2^` @p numQubits.
697 * - if @p qubits is not a list of length @p numQubits.
698* @notyetvalidated
699 * @see
700 * - calcProbOfMultiQubitOutcome()
701 * - calcProbOfBasisState()
702 * @author Tyson Jones
703 */
704void calcProbsOfAllMultiQubitOutcomes(qreal* outcomeProbs, Qureg qureg, int* qubits, int numQubits);
705
706
707/** @} */
708
709
710
711/**
712 * @defgroup calc_properties Properties
713 * @brief Functions for calculating single-state properties like normalisation and purity.
714 * @{
715 */
716
717
718/** Calculates the probability normalisation of the given @p qureg. This is the probability
719 * of the @p qureg being in _any_ outcome state, which is expected to equal `1`.
720 *
721 * @formulae
722 *
723 * Let @f$N@f$ be the number of qubits in @p qureg.
724 *
725 * - When @p qureg is a statevector @f$ \svpsi @f$ with @f$i@f$-th amplitude @f$\psi_i@f$,
726 * this function returns
727 * @f[
728 \sum\limits_{i=0}^{2^N-1} |\psi_i|^2.
729 * @f]
730 * - When @p qureg is a density matrix @f$ \dmrho @f$ with @f$i@f$-th diagonal element
731 * @f$ \dmrho_{ii} @f$, this function returns
732 * @f[
733 \sum\limits_{i=0}^{2^N-1} \re{ \rho_{ii} }
734 * @f]
735 *
736 * @constraints
737 *
738 * - As above, only the real components of the diagonal elements of a density matrix are consulted;
739 * these are the only amplitudes consulted by functions which calculate probabilities in the
740 * computational basis. As such, this function gives no indication of the general validity of density
741 * matrices, such as whether they are Hermitian, whether the diagonals are real, and whether the
742 * off-diagoanl elements are valid.
743 *
744 * @equivalences
745 *
746 * - This function is faster than, but mathematically equivalent to, summing the outputs of other
747 * functions which calculate probabilitie across all possible outcomes.
748 * ```
749 // choice is arbitrary
750 int qubit = 0;
751
752 qreal totalProb = (
753 calcProbOfQubitOutcome(qureg, qubit, 0) +
754 calcProbOfQubitOutcome(qureg, qubit, 1));
755 * ```
756 *
757 * @myexample
758 * ```
759 Qureg qureg = createDensityQureg(5);
760 initRandomMixedState(qureg, 1<<5);
761
762 // differs from 1 by numerical error
763 qreal totalProb = calcTotalProb(qureg);
764 * ```
765 *
766 * @param[in] qureg the reference state, which is unchanged.
767 * @returns The probability normalisation of @p qureg.
768 * @throws @validationerror
769 * - if @p qureg is uninitialised.
770* @notyetvalidated
771 * @see
772 * - calcPurity()
773 * - calcProbsOfAllMultiQubitOutcomes()
774 * @author Tyson Jones
775 */
776qreal calcTotalProb(Qureg qureg);
777
778
779/** Calculates the purity of @p qureg, which is a measure of its mixedness.
780 *
781 * @formulae
782 *
783 * Let @f$N@f$ be the number of qubits in @p qureg.
784 *
785 * - When @p qureg is a density matrix @f$ \dmrho @f$ (as expected), this function returns
786 * @f[
787 \tr{ \dmrho^2 } = \sum\limits_{i,j} \left| \dmrho_{ij} \right|^2
788 * @f]
789 * where @f$ \dmrho_{ij} @f$ is the @f$(i,j)@f$-th element of @f$ \dmrho @f$.
790 *
791 * A purity of `1` indicates that the matrix is _pure_ and can be expressed as
792 * @f[
793 \dmrho \equiv \ketbra{\phi}{\phi}
794 * @f]
795 * where @f$ \ket{\phi} @f$ is some pure state expressible as a statevector.
796 *
797 * In contrast, a purity less than `1` indicates the matrix is _mixed_ and can be
798 * understood as a convex combination of multiple (at least _two_) pure states.
799 * That is,
800 * @f[
801 \dmrho \equiv \sum\limits_n p_n \ketbra{\phi}{\phi}_n,
802 * @f]
803 * where @f$p_n \in [0,1]@f$ and sum to `1` whenever @f$\dmrho@f$ is a valid and correctly
804 * normalised density matrix. Mixedness can result, for example, from @ref decoherence.
805 *
806 * The minimum purity of an @f$N@f$-qubit density matrix is @f$ 1/2^N @f$, which is
807 * admitted only by the maximally-mixed state @f$ \dmrho = \hat{\id} / 2^N @f$.
808 *
809 * - When @p qureg is a statevector @f$ \svpsi @f$, this function returns
810 * @f[
811 \tr{ \ketbra{\psi}{\psi} \; \ketbra{\psi}{\psi} }
812 = \left( \sum\limits_i |\psi_i|^2 \right)^2
813 * @f]
814 * where @f$\psi_i@f$ is the @f$i@f$-th amplitude of @f$\svpsi@f$. This is always `1` for
815 * any valid statevector, and is otherwise equivalent to the output of calcTotalProb(), squared.
816 *
817 * @constraints
818 *
819 * - The output of this function is only a reliable measure of purity when @p qureg is correctly
820 * normalised. For example, an invalid density matrix can return a purity of `1`, such as the
821 * @f$N@f$-qubit maximally-mixed state scaled by factor @f$ 2^N @f$. Note that the function
822 * calcTotalProb() alone _cannot_ be used to validate validity since it only consults diagonal
823 * elements, whereas the purity is informed by all elements.
824 *
825 * @equivalences
826 *
827 * - When @p qureg is a valid density matrix (specifically, Hermitian), this function is faster
828 * than, but mathematically equivalent to, calling calcInnerProduct() and passing @p qureg twice.
829 * ```
830 qcomp out = calcInnerProduct(qureg, qureg);
831 qreal pur = real(out); // im=0
832 * ```
833 * - When @p qureg is a statevector, this function returns the output of calcTotalProb(), squared.
834 *
835 * @myexample
836 * ```
837 Qureg qureg = createDensityQureg(5);
838 initRandomPureState(qureg);
839
840 // = 1
841 qreal purity1 = calcPurity(qureg);
842 reportScalar("purity1", purity1);
843
844 mixTwoQubitDepolarising(qureg, 0, 1, 0.5);
845
846 // < 1
847 qreal purity2 = calcPurity(qureg);
848 reportScalar("purity2", purity2);
849 * ```
850 *
851 * @param[in] qureg the reference state, which is unchanged.
852 * @returns The purity of @p qureg.
853 * @throws @validationerror
854 * - if @p qureg is uninitialised.
855* @notyetvalidated
856 * @see
857 * - calcFidelity()
858 * - calcTotalProb()
859 * @author Tyson Jones
860 */
861qreal calcPurity(Qureg qureg);
862
863
864/** @} */
865
866
867
868/**
869 * @defgroup calc_comparisons Comparisons
870 * @brief Functions for comparing multiple quantum states.
871 * @{
872 */
873
874
875/** Calculates the fidelity between @p qureg and @p other, where at least one is a
876 * statevector.
877 *
878 * @formulae
879 *
880 * - When both @p qureg and @p other are statevectors (respectively @f$\ket{\psi}@f$ and
881 * @f$\ket{\phi}@f$), this function returns
882 * @f[
883 \left| \braket{\phi}{\psi} \right|^2.
884 * @f]
885 * - When @p qureg is a density matrix @f$\dmrho@f$ and @p other is a statevector @f$\svpsi@f$,
886 * this function returns
887 * @f[
888 \bra{\psi} \dmrho \ket{\psi},
889 * @f]
890 * and similarly when @p qureg is a statevector and @p other is a density matrix.
891 *
892 * @constraints
893 *
894 * - The output of this function is always real, which validation will check after computing the
895 * fidelity as a complex scalar. Specifically, validation will assert that the result has an
896 * absolute imaginary component less than the validation epsilon, which can be adjusted with
897 * setQuESTValidationEpsilon().
898 *
899 * - This function does not yet support both @p qureg and @p other being density matrices, for
900 * which the fidelity calculation is more substantial.
901 *
902 * - When @p qureg and @p other are _both_ statevectors, or _both_ density matrices, then _both_ or
903 * _neither_ must be GPU-accelerated. That is, their CPU vs GPU deployments must agree. They are
904 * permitted to differ in distribution however. Such considerations are only relevant when
905 * creating the registers using createCustomQureg(), since the automatic deployments of createQureg()
906 * and createDensityQureg() will always agree.
907 *
908 * - When @p qureg and @p other dimensionally _differ_ (i.e. one is a statevector while the other is a
909 * density matrix), the statevector must not be distributed _unless_ the density matrix is distributed.
910 * The CPU vs GPU deployments however are permitted to disagree. These requirements are again
911 * consistent with the automatic deployments of the createQureg() and createDensityQureg() functions.
912 *
913 * @equivalences
914 *
915 * - When both @p qureg and @p other are statevectors, this function is equivalent to calling
916 * calcInnerProduct() and squaring the absolute value of the result.
917 * ```
918 qcomp prod = calcInnerProduct(qureg, other);
919 qreal fid = pow(abs(prod), 2);
920 * ```
921 * - When one of @p qureg or @p other is a statevector in the computational basis state @f$\ket{i}@f$
922 * (e.g. as can be produced via initClassicalState()), this function is slower but equivalent to
923 * finding directly the probability of the basis state.
924 * ```
925 // initClassicalState(other, index);
926
927 qreal fid = calcProbOfBasisState(qureg, index);
928 * ```
929 *
930 * @myexample
931 * ```
932 // rho = |psi><psi|
933 Qureg psi = createQureg(5);
934 Qureg rho = createDensityQureg(5);
935 initRandomPureState(psi);
936 initPureState(rho, psi);
937
938 qreal fid0 = calcFidelity(rho, psi); // = 1
939
940 mixDepolarising(rho, 0, 0.5);
941 qreal fid1 = calcFidelity(rho, psi); // < 1
942 * ```
943 *
944 * @param[in] qureg a state
945 * @param[in] other another state containing an equal number of qubits.
946 * @returns The fidelity between @p qureg and @p other.
947 * @throws @validationerror
948 * - if @p qureg or @p other is uninitialised.
949 * - if @p qureg and @p other contain a different number of qubits.
950 * - if @p qureg and @p other are incompatible deployed.
951 * - if both @p qureg and @p other are density matrices (as is not yet supported).
952 * - if @p qureg or @p other is unnormalised such that the calculated fidelity is non-real.
953 * @notyetvalidated
954 * @see
955 * - calcInnerProduct()
956 * - calcDistance()
957 * @author Tyson Jones
958 */
959qreal calcFidelity(Qureg qureg, Qureg other);
960
961
962/** Calculates one of three distance measures between @p qureg and @p other, depending
963 * upon whether one or both are density matrices. These are the Hilbert-Schmidt distance,
964 * Bures distance and purified distance.
965 *
966 * @formulae
967 *
968 * - When both @p qureg and @p other are statevectors (respectively @f$\ket{\psi}@f$ and
969 * @f$\ket{\phi}@f$), this function returns the **Bures distance** defined as
970 * @f[
971 d_B\left(\ket{\psi},\ket{\phi}\right) = \sqrt{2 - 2 \left| \braket{\phi}{\psi} \right|}
972 * @f]
973 * where @f$\left| \braket{\phi}{\psi} \right|@f$ is the square-root of the fidelity
974 * between @f$\ket{\psi}@f$ and @f$\ket{\phi}@f$ as would be computed by calcFidelity().
975 *
976 * - When both @p qureg and @p other are density matrices (respectively @f$\mathbf{\rho}@f$
977 * and @f$\mathbf{\sigma}@f$), this function returns the **Hilbert-Schmidt distance** defined as
978 * @f[
979 d_{HS}\left(\mathbf{\rho}, \mathbf{\sigma}\right)
980 =
981 \sqrt{ \tr{
982 \left| \mathbf{\rho} - \mathbf{\sigma} \right|^2
983 } }
984 =
985 \sqrt{
986 \sum\limits_{ij} \left| \rho_{ij} - \sigma_{ij} \right|^2
987 }.
988 * @f]
989 *
990 * - When one of @p qureg or @p other is a statevector @f$\svpsi@f$, and the other is a density
991 * matrix @f$\dmrho@f$, this function returns the **purified distance** defined as
992 * @f[
993 d_p\left(\svpsi,\dmrho\right) = \sqrt{ 1 - \brapsi \dmrho \svpsi }
994 * @f]
995 * where @f$\brapsi \dmrho \svpsi@f$ is the fidelity as returned by calcFidelity().
996 *
997 * @constraints
998 *
999 * - The output of this function is always real, which is always mathematically satisfied by the
1000 * Hilbert-Schmidt distance, but may be violated by the Bures and purified distances when the
1001 * input Qureg are not normalised, or otherwise due to numerical imprecision. Postcondition
1002 * validation of the Bures distance will check that
1003 * @f[
1004 \left| \braket{\phi}{\psi} \right| \le 1 + \valeps
1005 * @f]
1006 * while the purified distance validation will check that
1007 * @f[
1008 \left| \, \im{ \brapsi \dmrho \svpsi } \, \right| \le \valeps, \\
1009 \re{ \brapsi \dmrho \svpsi } \le 1 + \valeps,
1010 * @f]
1011 * where @f$\valeps@f$ is the validation epsilon, adjustable via setQuESTValidationEpsilon().
1012 *
1013 * - Even when the above postcondition validation is disabled, the Bures and purified distance
1014 * calculations will respectively replace @f$\left| \braket{\phi}{\psi} \right|@f$ and
1015 * @f$\re{ \brapsi \dmrho \svpsi }@f$ which exceed @f$1@f$ with value @f$1@f$, and the imaginary
1016 * component of @f$\brapsi \dmrho \svpsi@f$ is discarded.
1017 *
1018 * - When @p qureg and @p other are _both_ statevectors, or _both_ density matrices, then _both_ or
1019 * _neither_ must be GPU-accelerated. That is, their CPU vs GPU deployments must agree. They are
1020 * permitted to differ in distribution however. Such considerations are only relevant when
1021 * creating the registers using createCustomQureg(), since the automatic deployments of createQureg()
1022 * and createDensityQureg() will always agree.
1023 *
1024 * - When @p qureg and @p other dimensionally _differ_ (i.e. one is a statevector while the other is a
1025 * density matrix), the statevector must not be distributed _unless_ the density matrix is distributed.
1026 * The CPU vs GPU deployments however are permitted to disagree. These requirements are again
1027 * consistent with the automatic deployments of the createQureg() and createDensityQureg() functions.
1028 *
1029 * @equivalences
1030 *
1031 * - When both @p qureg and @p other are statevectors, this function wraps calcInnerProduct().
1032 * ```
1033 qcomp prod = calcInnerProduct(qureg, other); // <qureg|other>
1034 qreal mag = abs(prod);
1035 mag = (mag > 1)? 1 : mag;
1036 qreal dist = std::sqrt(2 - 2 * mag);
1037 * ```
1038 *
1039 * - When @p qureg is a density matrix and @p other is a statevector, this function wraps calcInnerProduct()
1040 * as a complex-valued proxy for calcFidelity().
1041 * ```
1042 qcomp prod = calcInnerProduct(other, qureg); // <other|qureg|other>
1043 qreal re = real(prod);
1044 re = (re > 1)? 1 : re;
1045 qreal dist = sqrt(1 - re);
1046 * ```
1047 *
1048 * @myexample
1049 * ```
1050 Qureg rho1 = createDensityQureg(5);
1051 Qureg rho2 = createDensityQureg(5);
1052
1053 initRandomMixedState(rho1, 10);
1054 setQuregToClone(rho2, rho1);
1055 qreal distA = calcDistance(rho1, rho2); // = 0
1056
1057 initRandomMixedState(rho2, 10);
1058 qreal distB = calcDistance(rho1, rho2); // > 0
1059 * ```
1060 *
1061 * @param[in] qureg a state
1062 * @param[in] other another state containing an equal number of qubits
1063 * @returns The distance between @p qureg and @p other, according to the above measures.
1064 * @throws @validationerror
1065 * - if @p qureg or @p other is uninitialised.
1066 * - if @p qureg and @p other contain a different number of qubits.
1067 * - if @p qureg and @p other are incompatible deployed.
1068 * - if @p qureg or @p other is unnormalised such that the Bures or purified distances would be non-real.
1069 * @notyetvalidated
1070 * @see
1071 * - calcInnerProduct()
1072 * - calcFidelity()
1073 * @author Tyson Jones
1074 */
1075qreal calcDistance(Qureg qureg, Qureg other);
1076
1077
1078/** @} */
1079
1080
1081
1082/**
1083 * @defgroup calc_partialtrace Partial trace
1084 * @brief Functions for calculating reduced density matrices, creating a new output Qureg.
1085 * @{
1086 */
1087
1088
1089/** Creates and populates a new Qureg which is a reduced density matrix resulting from tracing out
1090 * the specified qubits of @p qureg. This should be later freed by the user like all Qureg.
1091 *
1092 * Note that the deployments of the output Qureg (i.e. whether multithreaded, GPU-accelerated and
1093 * distributed) will match those of @p qureg. It is ergo intended that this function is used to
1094 * trace out few qubits, and may show worsening performance when tracing many qubits.
1095 *
1096 * The ordering of @p traceOutQubits has no effect, and the ordering of the remaining qubits in
1097 * the output Qureg match their original relative ordering in @p qureg.
1098 *
1099 * @formulae
1100 *
1101 * Let @f$\dmrho_{\text{in}} = @f$ @p qureg and let @f$\vec{t} = @f$ @p traceOutQubits which is a list of
1102 * length @f$n = @f$ @p numTraceQubits.
1103 *
1104 * This function returns a new Qureg @f$\dmrho_{\text{out}}@f$ which satisfies
1105 * @f[
1106 \dmrho_{\text{out}} = \text{Tr}_{\vec{t}} \left( \dmrho_{\text{in}} \right)
1107 =
1108 \sum\limits_i^{2^n}
1109 (\hat{\id} \otimes \bra{i}_{\vec{t}} ) \,
1110 \dmrho_{\text{in}} \,
1111 (\hat{\id} \otimes \ket{i}_{\vec{t}} )
1112 * @f]
1113 * where @f$\ket{i}_{\vec{t}}@f$ notates the @f$i@f$-th basis state (in any orthonormal basis) of the
1114 * targeted qubits, and @f$(\hat{\id} \otimes \ket{i}_{\vec{t}})@f$ notates interleaved identity operators
1115 * upon the non-targeted qubits.
1116 *
1117 * Given an @f$N@f$-qubit Qureg @f$\dmrho_{\text{in}}@f$, the output @f$\dmrho_{\text{out}}@f$ contains
1118 * @f$N-n@f$ qubits.
1119 *
1120 * @constraints
1121 *
1122 * - The given @p qureg must be a density matrix. It is however straightforward to prepare a density matrix
1123 * from a statevector.
1124 * ```
1125 // let qureg be the intended initial statevector
1126
1127 Qureg temp = createDensityQureg(qureg.numQubits);
1128 initPureState(temp, qureg);
1129
1130 Qureg reduced = calcPartialTrace(temp, traceOutQubits, numTraceQubits);
1131 destroyQureg(temp);
1132 * ```
1133 *
1134 * - When @p qureg is distributed, the returned Qureg will also be distributed, which imposes a minimum on
1135 * the number of qubits contained within; @f$\log_2(W)@f$ where @f$W@f$ is the number of distributed nodes
1136 * (or "world size"). This imposes a maximum upon @p traceOutQubits of
1137 * ```
1138 * numTraceQubits <= qureg.numQubits - qureg.logNumNodes
1139 * ```
1140 *
1141 * @equivalences
1142 *
1143 * - The function calcReducedDensityMatrix() is entirely equivalent, but conveniently permits specifying
1144 * a list of which qubits to _retain_ during partial tracing.
1145 *
1146 * - The functions setQuregToPartialTrace() and setQuregToReducedDensityMatrix() are also equivalent but
1147 * permit overwriting an existing Qureg.
1148 *
1149 * @myexample
1150 *
1151 * ```
1152 Qureg state = createDensityQureg(5);
1153 initRandomMixedState(state, 10);
1154 reportQureg(state);
1155
1156 int qubits[] = {0,2,4};
1157 Qureg reduced = calcPartialTrace(state, qubits, 3);
1158 reportQureg(reduced);
1159
1160 // state's qubits {1,3} have become reduced's qubits {0,1}
1161 * ```
1162 *
1163 * @param[in] qureg a density matrix which is not modified.
1164 * @param[in] traceOutQubits a list of qubits to trace out and ergo from the output Qureg.
1165 * @param[in] numTraceQubits the length of @p traceOutQubits.
1166 * @returns A new, smaller Qureg initialised to the reduced density matrix of @p qureg.
1167 * @throws @validationerror
1168 * - if @p qureg is uninitialised.
1169 * - if @p numTraceQubits is less than one.
1170 * - if @p numTraceQubits is equal or greater than the number of qubits in @p qureg.
1171 * - if @p qureg is distributed and @p numTraceQubits exceeds `qureg.numQubits - qureg.logNumNodes`.
1172 * - if the system contains insufficient RAM (or VRAM) to store the new Qureg in any deployment.
1173 * - if any memory allocation of the output Qureg unexpectedly fails.
1174 * @throws seg-fault
1175 * - if @p traceOutQubits is not a list of length @p numTraceQubits.
1176 * @notyetvalidated
1177 * @see
1178 * - calcReducedDensityMatrix()
1179 * - setQuregToPartialTrace()
1180 * - setQuregToReducedDensityMatrix()
1181 * @author Tyson Jones
1182 */
1183Qureg calcPartialTrace(Qureg qureg, int* traceOutQubits, int numTraceQubits);
1184
1185
1186/** Creates and populates a new Qureg which is a reduced density matrix of @p qureg,
1187 * retaining only the specified qubits and tracing out all others.
1188 *
1189 * Note that the deployments of the output Qureg (i.e. whether multithreaded, GPU-accelerated and
1190 * distributed) will match those of @p qureg. It is ergo intended that this function is used to
1191 * preserve most qubits of @p qureg, and may show worsening performance when retaining only few.
1192 *
1193 * > [!CAUTION]
1194 * > The ordering of @p retainQubits has no effect on the output state. The ordering of the
1195 * > retained qubits will match their original, relative ordering in @p qureg.
1196 *
1197 * @formulae
1198 *
1199 * This function is entirely equivalent to calcPartialTrace() except that here the _retained_ qubits
1200 * are specified, whereas calcPartialTrace() accepts those to be traced out.
1201 *
1202 * Let @f$\dmrho_{\text{in}} = @f$ @p qureg, @f$\vec{r} = @f$ @p retainQubits, and let @f$\vec{q}@f$
1203 * be a list containing _all_ qubits of @p qureg. This function partially traces out all qubits in
1204 * list @f$\vec{t} = \vec{q} \setminus \vec{r}@f$, and returns a new Qureg @f$\dmrho_{\text{out}}@f$
1205 * which satisfies
1206 * @f[
1207 \dmrho_{\text{out}} = \text{Tr}_{\vec{t}} \left( \dmrho_{\text{in}} \right)
1208 =
1209 \sum\limits_i^{2^n}
1210 (\hat{\id} \otimes \bra{i}_{\vec{t}} ) \,
1211 \dmrho_{\text{in}} \,
1212 (\hat{\id} \otimes \ket{i}_{\vec{t}} )
1213 * @f]
1214 * where @f$\ket{i}_{\vec{t}}@f$ notates the @f$i@f$-th basis state (in any orthonormal basis) of the
1215 * qubits in @f$\vec{t}@f$, and @f$(\hat{\id} \otimes \ket{i}_{\vec{t}})@f$ notates interleaved identity
1216 * operators upon the qubits in @f$\vec{r}@f$.
1217 *
1218 * @constraints
1219 *
1220 * - The given @p qureg must be a density matrix. It is however straightforward to prepare a density matrix
1221 * from a statevector.
1222 * ```
1223 // let qureg be the intended initial statevector
1224
1225 Qureg temp = createDensityQureg(qureg.numQubits);
1226 initPureState(temp, qureg);
1227
1228 Qureg reduced = calcReducedDensityMatrix(temp, retainQubits, numRetainQubits);
1229 destroyQureg(temp);
1230 * ```
1231 *
1232 * - When @p qureg is distributed, the returned Qureg will also be distributed, which imposes a minimum on
1233 * the number of qubits contained within; @f$\log_2(W)@f$ where @f$W@f$ is the number of distributed nodes
1234 * (or "world size"). This imposes bounds upon @p numRetainQubits of
1235 * ```
1236 * qureg.logNumNodes <= numRetainQubits <= qureg.numQubits - 1
1237 * ```
1238 *
1239 * @equivalences
1240 *
1241 * - The function calcPartialTrace() is entirely equivalent, but permits directly specifying the qubits to
1242 * be traced out.
1243 *
1244 * - The functions setQuregToPartialTrace() and setQuregToReducedDensityMatrix() are also equivalent but
1245 * permit overwriting an existing Qureg.
1246 *
1247 * @myexample
1248 *
1249 * ```
1250 Qureg state = createDensityQureg(5);
1251 initRandomMixedState(state, 10);
1252 reportQureg(state);
1253
1254 int qubits[] = {1,3};
1255 Qureg reduced = calcReducedDensityMatrix(state, qubits, 2);
1256 reportQureg(reduced);
1257
1258 // state's qubits {1,3} have become reduced's qubits {0,1}
1259 * ```
1260 *
1261 * @param[in] qureg a density matrix.
1262 * @param[in] retainQubits a list of qubits to retain in the reduced density matrix (at shifted, contiguous indices).
1263 * @param[in] numRetainQubits the length of @p retainQubits.
1264 * @returns A new Qureg containing @p numRetainQubits qubits, initialised to the reduced density matrix of @p qureg.
1265 * @throws @validationerror
1266 * - if @p qureg is uninitialised.
1267 * - if @p numRetainQubits is less than one.
1268 * - if @p numRetainQubits is equal or greater than the number of qubits in @p qureg.
1269 * - if @p qureg is distributed and @p numRetainQubits is less than `qureg.logNumNodes`.
1270 * - if the system contains insufficient RAM (or VRAM) to store the new Qureg in any deployment.
1271 * - if any memory allocation of the output Qureg unexpectedly fails.
1272 * @throws seg-fault
1273 * - if @p retainQubits is not a list of length @p numRetainQubits.
1274 * @notyetvalidated
1275 * @see
1276 * - calcPartialTrace()
1277 * - setQuregToPartialTrace()
1278 * - setQuregToReducedDensityMatrix()
1279 * @author Tyson Jones
1280 */
1281Qureg calcReducedDensityMatrix(Qureg qureg, int* retainQubits, int numRetainQubits);
1282
1283
1284/** @} */
1285
1286
1287// end de-mangler
1288#ifdef __cplusplus
1289}
1290#endif
1291
1292
1293
1294/*
1295 * C++ ONLY FUNCTIONS
1296 *
1297 * which are not directly C-compatible because they pass or
1298 * return qcomp primitives by-value (rather than by pointer).
1299 * This is prohibited because the C and C++ ABI does not agree
1300 * on a complex type, though C's _Complex has the same memory
1301 * layout as C++'s std::complex<>. To work around this, the
1302 * below functions have a C-compatible wrapper defined in
1303 * wrappers.h which passes/receives the primitives by pointer;
1304 * a qcomp ptr can be safely passed from the C++ source binary
1305 * the user's C binary. We manually add these functions to their
1306 * respective Doxygen doc groups defined above
1307 */
1308
1309
1310/** @ingroup calc_comparisons
1311 *
1312 * Calculates the inner product of state @p qureg with @p other.
1313 *
1314 * @formulae
1315 *
1316 * - When both @p qureg and @p other are statevectors (respectively @f$\ket{\psi}@f$ and
1317 * @f$\ket{\phi}@f$), this function returns
1318 * @f[
1319 \braket{\psi}{\phi} = \sum\limits_i \psi_i^* \phi_i
1320 * @f]
1321 * where @f$\psi_i@f$ and @f$\phi_i@f$ are the @f$i@f$-th amplitudes of @f$\ket{\psi}@f$
1322 * (@p qureg) and @f$\ket{\phi}@f$ (@p other) respectively, and @f$\alpha^*@f$ notates
1323 * the complex conjugate of scalar @f$\alpha@f$.
1324 *
1325 * - When both @p qureg and @p other are density matrices (respectively @f$\mathbf{\rho}@f$
1326 * and @f$\mathbf{\sigma}@f$), this function returns
1327 * @f[
1328 \tr{ \rho^\dagger \sigma } = \sum\limits_{ij} {\rho_{ij}}^* \, \sigma_{ij}.
1329 * @f]
1330 *
1331 * - When @p qureg is a density matrix @f$\dmrho@f$ and @p other is a statevector @f$\ket{\phi}@f$,
1332 * this function returns
1333 * @f[
1334 \bra{\phi} \dmrho^\dagger \ket{\phi}.
1335 * @f]
1336 *
1337 * - When @p qureg is a statevector @f$\svpsi@f$ and @p other is a density matrix @f$\mathbf{\sigma}@f$,
1338 * this function returns
1339 * @f[
1340 \brapsi \mathbf{\sigma} \svpsi.
1341 * @f]
1342 *
1343 * @constraints
1344 *
1345 * - When @p qureg and @p other are _both_ statevectors, or _both_ density matrices, then _both_ or
1346 * _neither_ must be GPU-accelerated. That is, their CPU vs GPU deployments must agree. They are
1347 * permitted to differ in distribution however. Such considerations are only relevant when
1348 * creating the registers using createCustomQureg(), since the automatic deployments of createQureg()
1349 * and createDensityQureg() will always agree.
1350 *
1351 * - When @p qureg and @p other dimensionally _differ_ (i.e. one is a statevector while the other is a
1352 * density matrix), the statevector must not be distributed _unless_ the density matrix is distributed.
1353 * The CPU vs GPU deployments however are permitted to disagree. These requirements are again
1354 * consistent with the automatic deployments of the createQureg() and createDensityQureg() functions.
1355 *
1356 * @myexample
1357 * ```
1358 Qureg rho1 = createDensityQureg(5);
1359 Qureg rho2 = createDensityQureg(5);
1360
1361 // rho1 = rho2 = |psi><psi|
1362 initRandomPureState(rho1);
1363 setQuregToClone(rho2, rho1);
1364 qcomp prodA = calcInnerProduct(rho1, rho2); // = 1
1365
1366 // rho1 = rho2 = sum_i prob_i |psi_i><psi_i|
1367 initRandomMixedState(rho1, 10);
1368 setQuregToClone(rho2, rho1);
1369 qcomp prodB = calcInnerProduct(rho1, rho2); // < 1, real
1370
1371 // rho1 != rho2
1372 initRandomMixedState(rho2, 10);
1373 qcomp prodC = calcInnerProduct(rho1, rho2); // abs < 1, complex
1374 * ```
1375 *
1376 * @param[in] qureg a state
1377 * @param[in] other another state with an equal number of qubits
1378 * @returns The inner product of @p qureg with @p other.
1379 * @throws @validationerror
1380 * - if @p qureg or @p other is uninitialised.
1381 * - if @p qureg and @p other contain a different number of qubits.
1382 * - if @p qureg and @p other are incompatibly deployed.
1383 * @notyetvalidated
1384 * @see
1385 * - calcDistance()
1386 * - calcFidelity()
1387 * @author Tyson Jones
1388 */
1389qcomp calcInnerProduct(Qureg qureg, Qureg other);
1390
1391
1392/** @ingroup calc_expec
1393 *
1394 * Calculates the expectation value of the given permittedly non-Hermitian operator @p sum
1395 * - a weighted sum of Pauli strings with complex weights - under the given state @p qureg,
1396 * which is not modified.
1397 *
1398 * @formulae
1399 *
1400 * This function is mathematically equivalent to calcExpecPauliStrSum(), _except_ that here a
1401 * complex scalar is returned. This permits obtaining the full scalar when @p sum contains non-real
1402 * weights, and/or when @p qureg is unnormalised.
1403 *
1404 * @myexample
1405 * ```
1406 Qureg qureg = createQureg(5);
1407 PauliStrSum sum = createInlinePauliStrSum(R"(
1408 0.123 + 3.5i ZIZIZI
1409 1.234 - 1E-5i XYZXZ
1410 -1E-2 IIIII
1411 )");
1412
1413 // prints "expec: 0.113+3.5i"
1414 qcomp expec = calcExpecNonHermitianPauliStrSum(qureg, sum);
1415 reportScalar("expec", expec);
1416 * ```
1417 *
1418 * @param[in] qureg the permittedly unnormalised reference state.
1419 * @param[in] sum the permittedly non-Hermitian operator.
1420 * @returns The permittedly complex expectation value.
1421 * @throws @validationerror
1422 * - if @p qureg or @p sum are uninitialised.
1423 * - if any PauliStr in @p sum targets a higher-index qubit than exists in @p qureg.
1424* @notyetvalidated
1425 * @see
1426 * - calcExpecPauliStrSum()
1427 * @author Tyson Jones
1428 */
1430
1431
1432/** @ingroup calc_expec
1433 *
1434 * Calculates the expectation value of the given permittedly non-Hermitian operator @p matr,
1435 * under the given state @p qureg, without modifying it.
1436 *
1437 * @formulae
1438 *
1439 * This function is mathematically equivalent to calcExpecFullStateDiagMatr(), _except_ that here a
1440 * complex scalar is returned. This permits obtaining the full scalar when @p sum contains non-real
1441 * elements, and/or when @p qureg is unnormalised.
1442 *
1443 * @myexample
1444 * ```
1445 Qureg qureg = createQureg(5);
1446 initPlusState(qureg);
1447
1448 FullStateDiagMatr matr = createFullStateDiagMatr(qureg.numQubits);
1449
1450 // profanely inefficient per-element initialisation
1451 for (int n=0; n<matr.numElems; n++) {
1452 qcomp elem = getQcomp(n, n+1);
1453 setFullStateDiagMatr(matr, n, &elem, 1);
1454 }
1455
1456 // prints "expec: 15.5+16.5i"
1457 qcomp expec = calcExpecNonHermitianFullStateDiagMatr(qureg, matr);
1458 reportScalar("expec", expec);
1459 * ```
1460 *
1461 * @param[in] qureg the permittedly unnormalised reference state.
1462 * @param[in] matr the permittedly non-Hermitian operator.
1463 * @returns The permittedly complex expectation value.
1464 * @throws @validationerror
1465 * - if @p qureg or @p matr are uninitialised.
1466 * - if @p matr does not match the dimension of @p qureg
1467 * - if @p matr is distributed but @p qureg is not
1468* @notyetvalidated
1469 * @see
1470 * - calcExpecFullStateDiagMatr()
1471 * - calcExpecFullStateDiagMatrPower()
1472 * - calcExpecNonHermitianFullStateDiagMatrPower()
1473 * @author Tyson Jones
1474 */
1476
1477
1478/** @ingroup calc_expec
1479 *
1480 * Calculates the expectation value of the given permittedly non-Hermitian operator @p matrix,
1481 * raised to the arbitrary complex @p exponent, under the given state @p qureg, which is not modified.
1482 *
1483 * @formulae
1484 *
1485 * This function is mathematically equivalent to calcExpecFullStateDiagMatrPower(), _except_ that
1486 * here a complex scalar is returned, in addition to @p exponent being permittedly complex.
1487 * This permits obtaining the full scalar when @p qureg is unnormalised or @p matrix (after being
1488 * raised to @p exponent) is non-Hermitian.
1489 *
1490 * @myexample
1491 * ```
1492 Qureg qureg = createQureg(5);
1493 initPlusState(qureg);
1494
1495 FullStateDiagMatr matrix = createFullStateDiagMatr(qureg.numQubits);
1496
1497 // profanely inefficient per-element initialisation
1498 for (int n=0; n<matrix.numElems; n++) {
1499 qcomp elem = getQcomp(n, n+1);
1500 setFullStateDiagMatr(matrix, n, &elem, 1);
1501 }
1502
1503 qcomp exponent = 3+4_i;
1504
1505 // prints "expec: -257.26-613.8i"
1506 qcomp expec = calcExpecNonHermitianFullStateDiagMatrPower(qureg, matrix, exponent);
1507 reportScalar("expec", expec);
1508 * ```
1509 *
1510 * @param[in] qureg the permittedly unnormalised reference state.
1511 * @param[in] matrix the permittedly non-Hermitian operator.
1512 * @param[in] exponent the permittedly complex exponent.
1513 * @returns The permittedly complex expectation value.
1514 * @throws @validationerror
1515 * - if @p qureg or @p matrix are uninitialised.
1516 * - if @p matrix does not match the dimension of @p qureg
1517 * - if @p matrix is distributed but @p qureg is not
1518* @notyetvalidated
1519 * @see
1520 * - calcExpecFullStateDiagMatr()
1521 * @author Tyson Jones
1522 */
1523qcomp calcExpecNonHermitianFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr matrix, qcomp exponent);
1524
1525
1526
1527/*
1528 * C++ OVERLOADS
1529 *
1530 * which are only accessible to C++ binaries, and accept
1531 * arguments more natural to C++ (e.g. std::vector). We
1532 * manually add these to their respective Doxygen doc groups.
1533 */
1534
1535#ifdef __cplusplus
1536
1537#include <vector>
1538
1539
1540/// @ingroup calc_prob
1541/// @notyettested
1542/// @notyetdoced
1543/// @notyetvalidated
1544/// @cppvectoroverload
1545/// @see calcProbOfMultiQubitOutcome()
1546qreal calcProbOfMultiQubitOutcome(Qureg qureg, std::vector<int> qubits, std::vector<int> outcomes);
1547
1548
1549/// @ingroup calc_prob
1550/// @notyettested
1551/// @notyetdoced
1552/// @notyetvalidated
1553/// @cpponly
1554/// @cppvectoroverload
1555/// @see calcProbsOfAllMultiQubitOutcomes()
1556std::vector<qreal> calcProbsOfAllMultiQubitOutcomes(Qureg qureg, std::vector<int> qubits);
1557
1558
1559/// @ingroup calc_partialtrace
1560/// @notyettested
1561/// @notyetdoced
1562/// @notyetvalidated
1563/// @cppvectoroverload
1564/// @see calcPartialTrace()
1565Qureg calcPartialTrace(Qureg qureg, std::vector<int> traceOutQubits);
1566
1567
1568/// @ingroup calc_partialtrace
1569/// @notyettested
1570/// @notyetdoced
1571/// @notyetvalidated
1572/// @cppvectoroverload
1573/// @see calcReducedDensityMatrix()
1574Qureg calcReducedDensityMatrix(Qureg qureg, std::vector<int> retainQubits);
1575
1576
1577#endif // __cplusplus
1578
1579
1580#endif // CALCULATIONS_H
1581
1582/** @} */ // (end file-wide doxygen defgroup)
qreal calcFidelity(Qureg qureg, Qureg other)
qreal calcDistance(Qureg qureg, Qureg other)
qcomp calcInnerProduct(Qureg qureg, Qureg other)
qreal calcExpecFullStateDiagMatr(Qureg qureg, FullStateDiagMatr matr)
qcomp calcExpecNonHermitianFullStateDiagMatr(Qureg qureg, FullStateDiagMatr matr)
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