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