The Quantum Exact Simulation Toolkit v4.3.0
Loading...
Searching...
No Matches
initialisations.h
1/** @file
2 * API signatures for initialising Quregs into
3 * particular states. Note when a Qureg is GPU-
4 * accelerated, these functions only update the
5 * state in GPU memory; the CPU amps are unchanged.
6 *
7 * @author Tyson Jones
8 *
9 * @defgroup initialisations Initialisations
10 * @ingroup api
11 * @brief Functions for preparing Quregs in particular states.
12 * @{
13 */
14
15#ifndef INITIALISATIONS_H
16#define INITIALISATIONS_H
17
18#include "quest/include/types.h"
19#include "quest/include/qureg.h"
20#include "quest/include/paulis.h"
21
22
23
24/*
25 * C AND C++ AGNOSTIC FUNCTIONS
26 */
27
28// enable invocation by both C and C++ binaries
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33
34
35/**
36 * @defgroup init_states States
37 * @brief Functions for initialising Qureg into physical states.
38 * @{
39 */
40
41
42/** Initialises @p qureg to the unnormalised all-zero-amplitude state.
43 *
44 * Every statevector amplitude, or every density-matrix element, is set to zero.
45 * This is not a physical quantum state, but is useful as a blank
46 * workspace before manually setting amplitudes.
47 *
48 * > [!CAUTION]
49 * > When @p qureg is GPU-accelerated, this function modifies only its GPU
50 * > amplitudes (Qureg::gpuAmps), leaving its CPU amps (Qureg::cpuAmps)
51 * > unchanged (like almost all QuEST operations). It is therefore necessary
52 * > to follow this function with syncQuregFromGpu() in order to make
53 * > further, manual changes from the host side.
54 *
55 * @equivalences
56 *
57 * - This function is equivalent to (but much faster than) overwriting every
58 * amplitude to zero, _except_ that it does not modify Qureg::cpuAmps.
59 * ```cpp
60 for (int i=0; i<qureg.numAmpsPerNode; i++)
61 qureg.cpuAmps[i] = 0;
62 syncQuregToGpu(qureg);
63
64 // restore qureg.cpuAmps when !qureg.isGpuAccelerated
65 * ```
66 *
67 * @myexample
68 *
69 * This function is useful for preparing sparse states, noting we must
70 * explicitly copy the newly-zeroed amplitudes from GPU memory, when
71 * @p qureg is GPU-accelerated (though such functions are always safe
72 * to call).
73 *
74 * ```cpp
75 initBlankState(qureg);
76 syncQuregFromGpu(qureg);
77
78 // manually modify qureg.cpuAmps in some manner that wouldn't
79 // be more sensible to perform with setQuregAmps, such as to a
80 // uniform superposition of random basis states
81 for (qindex i=0; i<500; i++) {
82 qindex j = rand % qureg.numAmpsPerNode;
83 qureg.cpuAmps[j] = 1;
84 }
85
86 syncQuregToGpu(qureg);
87 setQuregToRenormalized();
88 * ```
89 *
90 * @param[in,out] qureg the Qureg to overwrite.
91 * @throws @validationerror
92 * - if @p qureg is uninitialised.
93 * @see
94 * - setQuregAmps()
95 * - initZeroState()
96 * - initClassicalState()
97 * - initPlusState()
98 * - initRandomPureState()
99 * @author Tyson Jones
100 */
101void initBlankState(Qureg qureg);
102
103
104/** Initialises @p qureg to the zero computational basis state.
105 *
106 * > [!NOTE]
107 * > Like most of QuEST's API, this function leaves Qureg::cpuAmps unchanged when
108 * > @p qureg is GPU-accelerated, overwriting only the relevant GPU buffer Qureg::gpuAmps.
109 * > See initBlankState() for more information.
110 *
111 * @formulae
112 *
113 * Let @f$N@f$ be the number of qubits in @p qureg.
114 *
115 * - If @p qureg is a statevector, it is initialised to @f$\ket{0}^{\otimes N}@f$.
116 * - If @p qureg is a density matrix, it is initialised to @f$\ket{0}\bra{0}^{\otimes N}@f$.'
117 *
118 * @equivalences
119 *
120 * - The zero state is the first enumerated classical state.
121 * ```cpp
122 initClassicalState(qureg, 0);
123 * ```
124 * - The zero state has a zero amplitude everywhere except at the first index, which has one.
125 * The code below is equivalent to this function, _except_ Qureg::cpuAmps are also modified
126 * below, whereas initZeroState() leaves them unchanged when @p qureg is GPU-accelerated.
127 * ```cpp
128 initBlankState(qureg);
129 if (qureg.rank == 0)
130 qureg.cpuAmps[0] = 1;
131 syncQureg(qureg);
132
133 // restore qureg.cpuAmps when !qureg.isGpuAccelerated
134 * ```
135 *
136 * @param[in,out] qureg the Qureg to overwrite.
137 * @throws @validationerror
138 * - if @p qureg is uninitialised.
139 * @author Tyson Jones
140 */
141void initZeroState(Qureg qureg);
142
143
144/** Initialises @p qureg to the uniform plus state.
145 *
146 * > [!NOTE]
147 * > Like most of QuEST's API, this function leaves Qureg::cpuAmps unchanged when
148 * > @p qureg is GPU-accelerated, overwriting only the relevant GPU buffer Qureg::gpuAmps.
149 * > See initBlankState() for more information.
150 *
151 * @formulae
152 *
153 * Let @f$N@f$ be the number of qubits in @p qureg.
154 *
155 * - If @p qureg is a statevector, it is initialised to
156 * @f[
157 \begin{aligned}
158 \ket{+}^{\otimes N} &= \left( \frac{1}{\sqrt{2}} \ket{0} + \frac{1}{\sqrt{2}} \ket{1} \right)^{\otimes N} \\
159 &= \frac{1}{\sqrt{2^N}} \{ 1, 1, \dots, 1 \}
160 \end{aligned}
161 * @f]
162 * - If @p qureg is a density matrix, it is initialised to
163 * @f[
164 \ket{+}\bra{+}^{\otimes N} = \frac{1}{2^N}
165 \begin{pmatrix}
166 1 & 1 & \dots \\ 1 & \ddots \\ \vdots
167 \end{pmatrix}
168 * @f]
169 *
170 * @equivalences
171 *
172 * - The plus state can also be produced by applying a Hadamard gate upon every zero-state qubit.
173 * ```cpp
174 initZeroState(qureg);
175 for (int i=0; i<qureg.numQubits; i++)
176 applyHadamard(qureg, i);
177 * ```
178 *
179 * @param[in,out] qureg the Qureg to overwrite.
180 * @throws @validationerror
181 * - if @p qureg is uninitialised.
182 * @author Tyson Jones
183 */
184void initPlusState(Qureg qureg);
185
186
187/** Initialises @p qureg to the state in statevector @p pure.
188 *
189 * > [!NOTE]
190 * > Like most of QuEST's API, this function leaves Qureg::cpuAmps unchanged when
191 * > @p qureg is GPU-accelerated, overwriting only the relevant GPU buffer Qureg::gpuAmps.
192 * > See initBlankState() for more information.
193 *
194 * @formulae
195 *
196 * Let @f$N@f$ be the number of qubits in @p qureg or @p pure, and let @f$\ket{\psi} = @f$ @p pure,
197 * with amplitudes @f$\ket{\psi} = \sum_i \alpha_i \ket{i}@f$.
198 *
199 * - If @p qureg is a statevector, it is overwritten by the state in @p pure.
200 * - If @p qureg is a density matrix, it is initialised to
201 * @f[
202 \ket{\psi}\bra{\psi} =
203 \sum\limits_i\sum\limits_j \alpha_i \,\alpha_j^* \, \ket{i}\bra{j}
204 * @f]
205 *
206 * @equivalences
207 *
208 * - When @p qureg is a statevector, this function is entirely equivalent to
209 * ```cpp
210 setQuregToClone(qureg, pure);
211 * ```
212 * - When @p qureg is a density matrix, this function is equivalent to
213 * ```cpp
214 double prob = 1;
215 setQuregToMixture(qureg, &prob, &pure, 1);
216 * ```
217 *
218 * @param[in,out] qureg the Qureg to overwrite.
219 * @param[in] pure the statevector pure state to copy.
220 * @throws @validationerror
221 * - if @p qureg or @p pure are uninitialised.
222 * - if @p pure is not a statevector.
223 * - if @p qureg and @p pure have incompatible dimensions or deployments.
224 * @author Tyson Jones
225 */
226void initPureState(Qureg qureg, Qureg pure);
227
228
229/** Initialises @p qureg to a computational basis state.
230 *
231 * > [!NOTE]
232 * > Like most of QuEST's API, this function leaves Qureg::cpuAmps unchanged when
233 * > @p qureg is GPU-accelerated, overwriting only the relevant GPU buffer Qureg::gpuAmps.
234 * > See initBlankState() for more information.
235 *
236 * @formulae
237 *
238 * Let @f$N@f$ be the number of qubits in @p qureg, and let @f$i=@f$ @p stateInd.
239 *
240 * States are enumerated from @f$0@f$ to @f$2^N-1@f$, such that the bits of the indices
241 * match the qubits of the corresponding basis states.
242 *
243 * - If @p qureg is a statevector, it is initialised to @f$\ket{i}@f$.
244 * - If @p qureg is a density matrix, it is initialised to @f$\ket{i}\bra{i}@f$.
245 *
246 * The bits of @f$i@f$ will match the qubit values of the resulting state in @p qureg,
247 * where the zero-th qubit is the rightmost bit.
248 *
249 * @equivalences
250 *
251 * - The resulting state contains zero for all amplitudes except that at global index @f$i@f$
252 * (when @p qureg is a statevector) or the @f$i@f$-th diagonal (when @p qureg is a
253 * density matrix).
254 * ```cpp
255 initBlankState(qureg);
256
257 // determine where the single, global amp to modify is located
258 qindex numNewAmps = 1;
259 qindex densityDim = 1 + (1 << qureg.numQubits);
260 qindex globalAmpInd = stateInd * (qureg.isDensityMatrix? densityDim : 1);
261 qindex localAmpInd = globalAmpInd % qureg.numAmpsPerNode;
262 int rankContainingAmp = i / qureg.numAmpsPerNode;
263 bool isAmpInThisNode = (rankContainingAmp == qureg.rank);
264
265 // one node modifies 1 CPU amp
266 if (isAmpInThisNode)
267 qureg.cpuAmps[localAmpInd] = 1;
268
269 // all nodes sync to GPU but only one node specifies a more than zero amps
270 syncSubQuregToGpu(qureg, i, numNewAmps * isAmpInThisNode);
271 * ```
272 * - The resulting state can be (pointlessly slowly) produced by qubit flips from the
273 * zero state, according to the bits in @p stateInd.
274 * ```cpp
275 initZeroState(qureg);
276 for (int i=0; i<qureg.numQubits; i++)
277 if ((stateInd >> i) & 1)
278 applyPauliX(qureg, i);
279 * ```
280 *
281 * @param[in,out] qureg the Qureg to overwrite.
282 * @param[in] stateInd the computational basis-state index.
283 * @throws @validationerror
284 * - if @p qureg is uninitialised.
285 * - if @p stateInd is outside the computational basis of @p qureg.
286 * @author Tyson Jones
287 */
288void initClassicalState(Qureg qureg, qindex stateInd);
289
290
291/** Initialises @p qureg to the debug state.
292 *
293 * This is a non-physical, deterministic pattern useful for debugging.
294 * The @f$j@f$-th local amplitude becomes
295 * @f[
296 2j/10 + \iu(2j+1)/10,
297 * @f]
298 * even if @p qureg is a density matrix, in which case it is enumerated
299 * column-major.
300 *
301 * > [!CAUTION]
302 * > When @p qureg is GPU-accelerated, this function modifies only its GPU
303 * > amplitudes (Qureg::gpuAmps), leaving its CPU amps (Qureg::cpuAmps)
304 * > unchanged (like almost all QuEST operations). It is therefore necessary
305 * > to follow this function with syncQuregFromGpu() in order to make
306 * > further, manual changes from the host side. See initBlankState() for more
307 * > information.
308 *
309 * @myexample
310 *
311 * ```cpp
312 Qureg qureg = createQureg(3);
313 initDebugState(qureg);
314 reportQureg(qureg);
315 * ```
316 * ```text
317 Qureg (3 qubit statevector, 8 qcomps, 232 bytes):
318 0.1i |0⟩
319 0.2+0.3i |1⟩
320 0.4+0.5i |2⟩
321 0.6+0.7i |3⟩
322 0.8+0.9i |4⟩
323 1+1.1i |5⟩
324 1.2+1.3i |6⟩
325 1.4+1.5i |7⟩
326 * ```
327 *
328 * @param[in,out] qureg the Qureg to overwrite.
329 * @throws @validationerror
330 * - if @p qureg is uninitialised.
331 * @author Tyson Jones
332 */
333void initDebugState(Qureg qureg);
334
335
336/** Initialises @p qureg from the statevector amplitudes in @p amps.
337 *
338 * @formulae
339 *
340 * Let @f$N@f$ be the number of qubits in @p qureg, and let @f$\alpha_i@f$ be
341 * the amplitude `amps[i]`. Array @p amps must be length @f$2^N@f$.
342 *
343 * - If @p qureg is a statevector, its amplitudes are overwritten by @p amps, to become
344 * @f[
345 \sum\limits_i \alpha_i \ket{i}.
346 * @f]
347 *
348 * - If @p qureg is a density matrix, it is initialised to the pure state @f$\ket{\psi}@f$
349 * encoded by @p amps, i.e.
350 * @f[
351 \ket{\psi}\bra{\psi} =
352 \sum\limits_i\sum\limits_j \alpha_i \,\alpha_j^* \, \ket{i}\bra{j}
353 * @f]
354 *
355 * There is no need for @p amps to be normalised, although @p qureg will otherwise be left
356 * in an unnormalised, non-physical state.
357 *
358 * @param[in,out] qureg the Qureg to overwrite.
359 * @param[in] amps an array of @f$2^N@f$ pure-state amplitudes.
360 * @throws @validationerror
361 * - if @p qureg is uninitialised.
362 * @throws seg-fault
363 * - if @p amps has fewer than @f$2^N@f$ elements.
364 * @author Tyson Jones
365 */
366void initArbitraryPureState(Qureg qureg, qcomp* amps);
367
368
369/** Initialises @p qureg (a statevector or density matrix) to a pure state with
370 * uniformly random amplitudes.
371 *
372 * The resulting state is normalised, with basis state probabilities sampled
373 * from a chi-squared variate, as described
374 * [here](https://sumeetkhatri.com/wp-content/uploads/2020/05/random_pure_states.pdf).
375 *
376 * @param[in,out] qureg the Qureg to overwrite.
377 * @throws @validationerror
378 * - if @p qureg is uninitialised.
379 * @see
380 * - initRandomMixedState()
381 * @author Tyson Jones
382 */
383void initRandomPureState(Qureg qureg);
384
385
386/** Initialises a density matrix to a mixture of uniformly random pure states.
387 *
388 * The resulting density matrix is the equally weighted mixture of @p numPureStates
389 * independently sampled random pure states, each sampled as per initRandomPureState().
390 *
391 * @formulae
392 *
393 * Let @f$n=@f$ @p numPureStates, and let @f$\ket{\psi_i}@f$ be a random pure
394 * state with number of qubits as @p qureg.
395 *
396 * This function overwrites @p qureg to
397 * @f[
398 * \sum\limits_i^n \frac{1}{n} \ket{\psi_i}\bra{\psi_i}.
399 * @f]
400 *
401 * @param[in,out] qureg the density matrix to overwrite.
402 * @param[in] numPureStates the number of random pure states in the mixture.
403 * @throws @validationerror
404 * - if @p qureg is uninitialised.
405 * - if @p qureg is not a density matrix.
406 * - if @p numPureStates is invalid.
407 * @see
408 * - initRandomPureState()
409 * @author Tyson Jones
410 */
411void initRandomMixedState(Qureg qureg, qindex numPureStates);
412
413
414/** @} */
415
416
417
418/**
419 * @defgroup init_amps Amplitudes
420 * @brief Functions for overwriting Qureg amplitudes.
421 * @{
422 */
423
424
425/** Overwrites a contiguous range of statevector amplitudes.
426 *
427 * - Amplitudes outside the given range are unchanged.
428 * - There is no validation nor requirement that the new amplitudes,
429 * together with the remaining original amplitudes, produce a validly
430 * normalised state. Normalization can be re-established with a subsequent
431 * call to setQuregToRenormalized().
432 * - When @p qureg is distributed, @p startInd and @p numAmps are treated
433 * _globally_ and use of this function is ergo agnostic to distribution.
434 * Therefore, every process should contain identical @p amps, although
435 * only elements which fall within a process' statevector partition will
436 * be consulted by a particular process.
437 * - When @p qureg is GPU-accelerated, only its GPU amplitudes are updated.
438 *
439 * The equivalent function for a density matrix is setDensityQuregAmps().
440 *
441 * @formulae
442 *
443 * Let @f$\svpsi=@f$ @p qureg with @f$N@f$ qubits, and with @f$i@f$-th global amplitude @f$\alpha_i@f$.
444 * Let @f$s=@f$ @p startInd, @f$n=@f$ @p numAmps, and let @f$\beta_j@f$ be the @f$j@f$-th element of @p amps.
445 *
446 * This function overwrites @p qureg from @f$\svpsi=\sum_{i=0}^{2^N-1} \alpha_i \ket{i}@f$ to
447 * @f[
448 \svpsi \rightarrow
449 \sum\limits_{i=0}^{s-1} \alpha_i \ket{i} +
450 \sum\limits_{j=0}^{n-1} \beta_j \ket{j + s} +
451 \sum\limits_{i=s+n}^{2^N-1} \alpha_i \ket{i}
452 * @f]
453 * where amplitudes at global indices in @f$[s,s+n)@f$ have been modified. Expressed as a row-vector,
454 * @f[
455 \svpsi = \begin{pmatrix} \alpha_0 & \alpha_1 & \dots & \alpha_{2^N-1} \end{pmatrix}
456 * @f]
457 * is modified to become
458 * @f[
459 \svpsi \rightarrow \begin{pmatrix}
460 \alpha_0 & \alpha_1 & \dots & \alpha_{s-1} &
461 \beta_0 & \beta_1 & \dots & \beta_{n-1} &
462 \alpha_{s + n} & \dots & \alpha_{2^N-1}
463 \end{pmatrix}.
464 * @f]
465 *
466 * @constraints
467 *
468 * - Argument @p qureg must be a statevector, and ergo compatible with a 1D range.
469 * Density matrices can be overwritten at a 2D range with setDensityQuregAmps(),
470 * or with a 1D contiguous range when flattening the density matrix column-major
471 * with setDensityQuregFlatAmps(). Alternatively, setQuregAmps() can be called
472 * with validation disabled via setQuESTValidationOff(), accepting density matrices,
473 * and behaving identically to setDensityQuregFlatAmps().
474 *
475 * @equivalences
476 *
477 * - When @p qureg is **_not_** distributed, this function is equivalent to (but
478 * much faster than) manual modification of the CPU elements, followed by a copy
479 * to GPU (_except_ that this function does not modify Qureg::cpuAmps when @p qureg
480 * is not GPU-accelerated).
481 * ```cpp
482 for (qindex i=0; i<numAmps; i++)
483 qureg.cpuAmps[i + startInd] = amps[i];
484 syncSubQuregToGpu(qureg, startInd, numAmps);
485 // beware, syncQuregToGpu() would copy over stale, unmodified CPU amps
486
487 // restore qureg.cpuAmps when !qureg.isGpuAccelerated
488 * ```
489 * - When @p qureg _is_ distributed, the logic is complicated by the specified global
490 * range of amplitudes overlapping some, none or all of a node's partition.
491 *
492 * ```cpp
493 qcomp* amps[numAmps] = // global
494
495 qindex dim = qureg.numAmpsPerNode;
496 qindex endInd = startInd + numAmps;
497
498 qindex nodeStartInd = (qureg.rank ) * dim;
499 qindex nodeEndInd = (qureg.rank + 1) * dim;
500 bool nodeContainsAmps = (startInd < nodeEndInd) && (endInd > nodeStartInd);
501
502 qindex localStartInd = (startInd < nodeStartInd)? 0 : startInd % dim;
503 qindex localEndInd = (endInd > nodeEndInd)? dim : endInd % dim;
504 qindex numLocalAmps = nodeContainsAmps * (localEndInd - localStartInd);
505
506 qindex nodeOffset = nodeStartInd + localStartInd - startInd;
507 for (qindex i=0; i<numLocalAmps; i++)
508 qureg.cpuAmps[localStartInd + i] = amps[nodeOffset + i]
509
510 syncSubQuregToGpu(qureg, localStartInd, numLocalAmps);
511
512 // restore qureg.cpuAmps when !qureg.isGpuAccelerated
513 * ```
514 *
515 * @myexample
516 *
517 * - When @p numAmps is sufficiently small such that the array @p amps can
518 * fit onto every distributed node, this function can be used in a manner
519 * totally agnostic to distribution and/or @p qureg deployments.
520 *
521 * ```cpp
522 Qureg qureg = createQureg(35);
523 initBlankState(qureg);
524
525 qcomp amps[1000] = { ... };
526 setQuregAmps(qureg, 300000000, amps, 1000);
527 * ```
528 * - When @p numAmps is large, one can avoid the superfluous storing of all @p amps
529 * simultaneously, by repeatedly calling setQuregAmps(), each time passing a tractable
530 * sub-range, regardless of how @p qureg is distributed.
531 * ```cpp
532 Qureg qureg = createQureg(35);
533 initBlankState(qureg);
534
535 // global range
536 const qindex startInd = 1234567;
537 const qindex totalNumAmps = 1000000000; // 16 GB worth of double-prec qcomp
538
539 // local memory budget
540 const qindex batchSize = 10000000; // 160 MB worth
541 qcomp amps[batchSize];
542
543 int numBatches = totalNumAmps / batchSize; // divides evenly here for simplicity
544
545 for (int batchInd=0; batchInd<numBatches; batchInd++) {
546
547 // update amps, such that amps[i] is the desired amplitude
548 // for global index (startInd + batchInd * batchSize)
549 ...
550
551 setQuregAmps(qureg, startInd + batchInd * batchSize, amps, batchSize);
552 }
553 * ```
554 *
555 * @param[in,out] qureg the statevector to modify.
556 * @param[in] startInd the first global computational-basis index to overwrite.
557 * @param[in] amps an array of @p numAmps amplitudes.
558 * @param[in] numAmps the total number of amplitudes to overwrite.
559 * @throws @validationerror
560 * - if @p qureg is uninitialised.
561 * - if @p qureg is not a statevector.
562 * - if @p startInd and @p numAmps describes a range outside @p qureg.
563 * @see
564 * - setDensityQuregAmps()
565 * - setDensityQuregFlatAmps()
566 * - setQuregToWeightedSum()
567 * - setQuregToRenormalized()
568 * @author Tyson Jones
569 */
570void setQuregAmps(Qureg qureg, qindex startInd, qcomp* amps, qindex numAmps);
571
572
573/** Overwrites a rectangular block of density-matrix amplitudes.
574 *
575 * - Amplitudes outside the given block are unchanged.
576 * - There is no validation nor requirement that the new amplitudes,
577 * together with the remaining original amplitudes, produce a validly
578 * normalised density matrix. Normalization can be re-established with a
579 * subsequent call to setQuregToRenormalized().
580 * - When @p qureg is distributed, @p startRow, @p startCol, @p numRows and
581 * @p numCols are treated _globally_ and use of this function is ergo
582 * agnostic to distribution. Therefore, every process should contain
583 * identical @p amps.
584 * - When @p qureg is GPU-accelerated, only its GPU amplitudes are updated.
585 *
586 * The equivalent function for a statevector is setQuregAmps().
587 *
588 * @formulae
589 *
590 * Let @f$\dmrho=@f$ @p qureg with @f$N@f$ qubits, and with @f$(r,c)@f$-th global amplitude
591 * @f$\alpha_{r,c}@f$. Let @f$s_r=@f$ @p startRow, @f$s_c=@f$ @p startCol, @f$n_r=@f$
592 * @p numRows and @f$n_c=@f$ @p numCols, and let @f$\beta_{j,k}@f$ be the @f$(j,k)@f$-th
593 * element of @p amps, i.e. `amps[j][k]`.
594 *
595 * This function overwrites @p qureg from
596 * @f[
597 \dmrho = \sum\limits_{r=0}^{2^N-1} \sum\limits_{c=0}^{2^N-1}
598 \alpha_{r,c} \ket{r}\bra{c}
599 * @f]
600 * by modifying only the global rows @f$[s_r,s_r+n_r)@f$ and columns @f$[s_c,s_c+n_c)@f$,
601 * such that
602 * @f[
603 \alpha_{s_r+j,\,s_c+k} \rightarrow \beta_{j,k}
604 \quad\quad
605 \forall \; j \in [0,n_r), \; k \in [0,n_c).
606 * @f]
607 * Expressed as a matrix,
608 * @f[
609 \dmrho =
610 \begin{pmatrix}
611 \alpha_{0,0} & \alpha_{0,1} & \dots & \alpha_{0,2^N-1} \\
612 \alpha_{1,0} & \alpha_{1,1} & \\
613 \vdots & & \ddots \\
614 \alpha_{2^N-1,0} & & & \alpha_{2^N-1,2^N-1}
615 \end{pmatrix},
616 * @f]
617 * the state is modified to contain the sub-matrix below. Grey dots indicate @f$\alpha_{ij}@f$ above.
618 * @f[
619 \def\x{{\color{gray}\circ}}
620 \dmrho \rightarrow
621 \begin{array}{c@{\;}c}
622 & \hspace{0.5em}
623 \overset{\scriptstyle [s_c,\,s_c+n_c)}{\overline{\hspace{10.5em}}}
624 \hspace{-0.5em} \\[1ex]
625 \lower1.0em\hbox{$
626 \scriptstyle [s_r,\,s_r+n_r) \quad
627 \left\{\vphantom{\begin{matrix}
628 \beta_{0,0} \\
629 \beta_{1,0} \\
630 \vdots \\
631 \beta_{n_r-1,0}
632 \end{matrix}}\right.
633 $}
634 &
635 \begin{pmatrix}
636 \x & \x & \x & \x & \x & \x & \x \\
637 \x & \x & \x & \x & \x & \x & \x \\
638 \x & \x & \beta_{0,0} & \beta_{0,1} & \cdots & \beta_{0,n_c-1} & \x \\
639 \x & \x & \beta_{1,0} & \beta_{1,1} & \cdots & \beta_{1,n_c-1} & \x \\
640 \x & \x & \vdots & \vdots & \ddots & \vdots & \x \\
641 \x & \x & \beta_{n_r-1,0} & \beta_{n_r-1,1} & \cdots & \beta_{n_r-1,n_c-1} & \x \\
642 \x & \x & \x & \x & \x & \x & \x
643 \end{pmatrix}
644 \end{array}
645 * @f]
646 *
647 * @constraints
648 *
649 * - Argument @p qureg must be a density matrix, and ergo compatible with a 2D range.
650 * Statevectors can be overwritten at a 1D range with setQuregAmps(). Density
651 * matrices can also be overwritten with a 1D contiguous range when flattening
652 * the density matrix column-major with setDensityQuregFlatAmps().
653 *
654 * @equivalences
655 *
656 * - When @p qureg is **_not_** distributed, this function is equivalent to manual
657 * modification of the CPU elements, followed by copies to GPU (_except_ that this
658 * function does not modify Qureg::cpuAmps when @p qureg is not GPU-accelerated).
659 * ```cpp
660 for (qindex c=0; c<numCols; c++) {
661 qindex flatInd = (startCol + c) * (1LL << qureg.numQubits) + startRow;
662 for (qindex r=0; r<numRows; r++)
663 qureg.cpuAmps[flatInd + r] = amps[r][c];
664 syncSubQuregToGpu(qureg, flatInd, numRows);
665 }
666 // beware, syncQuregToGpu() would copy over stale, unmodified CPU amps
667
668 // restore qureg.cpuAmps when !qureg.isGpuAccelerated
669 * ```
670 * - When @p qureg _is_ distributed, the logic is complicated by each specified
671 * global column range overlapping some, none or all of a node's partition.
672 * It follows the same pattern as demonstrated in setQuregAmps(), through
673 * column-wise linearisation of the density matrix.
674 * - When @p amps are within a single column (`numCols==1`), or span multiple
675 * _full_ columns (`numRows==(1<<qureg.numQubits)`), this function becomes
676 * equivalent to calling setDensityQuregFlatAmps(), passing @p amps as a
677 * column-flattened 1D array.
678 *
679 * @myexample
680 *
681 * - When @p numRows and @p numCols are sufficiently small such that the matrix
682 * @p amps can fit onto every distributed node, this function can be used in a
683 * manner totally agnostic to distribution and/or @p qureg deployments.
684 * In C++, an overload accepts @p amps as nested `std::vector<qcomp>`:
685 * ```cpp
686 Qureg qureg = createDensityQureg(35);
687
688 std::vector<std::vector<qcomp>> amps = {
689 {1, 2, 3},
690 {4, 5, 6}};
691
692 setDensityQuregAmps(qureg, startRow, startCol, amps, 2, 3);
693 * ```
694 *
695 * In C, @p amps must be a double pointer (and alas not an array):
696 * ```cpp
697 Qureg qureg = createDensityQureg(35);
698 initBlankState(qureg);
699
700 qcomp ampsArr[2][3] = {
701 {1, 2, 3},
702 {4, 5, 6}};
703 qcomp* ampsPtr[] = {ampsArr[0], ampsArr[1]};
704
705 setDensityQuregAmps(qureg, startRow, startCol, ampsPtr, 2, 3);
706 * ```
707 * - When @p numRows or @p numCols is large, one can avoid the superfluous storing
708 * of all @p amps simultaneously, by repeatedly calling setDensityQuregAmps(),
709 * each time passing a tractable sub-block, regardless of how @p qureg is
710 * distributed.
711 * ```cpp
712 Qureg qureg = createDensityQureg(35);
713 initBlankState(qureg);
714
715 // global block
716 const qindex startRow = 1234567;
717 const qindex startCol = 7654321;
718 const qindex totalNumRows = 1000000; // divides evenly into batches below
719 const qindex totalNumCols = 1000000;
720
721 // local memory budget
722 const qindex batchNumRows = 100;
723 const qindex batchNumCols = 200;
724
725 // memory for a single batch
726 qcomp** ampsBatch = ... // 2D malloc of size (batchNumRows, batchNumCols)
727
728 for (qindex row=0; row<totalNumRows; row+=batchNumRows) {
729 for (qindex col=0; col<totalNumCols; col+=batchNumCols) {
730
731 // populate this two-dimensional batch
732 for (qindex r=0; r<batchNumRows; r++)
733 for (qindex c=0; c<batchNumCols; c++)
734 amps[r][c] = ...
735
736 setDensityQuregAmps(
737 qureg, startRow + row, startCol + col,
738 ampsBatch, batchNumRows, batchNumCols);
739 }
740 }
741 * ```
742 *
743 * @param[in,out] qureg the density matrix to modify.
744 * @param[in] startRow the first global density-matrix row index to overwrite.
745 * @param[in] startCol the first global density-matrix column index to overwrite.
746 * @param[in] amps a @p numRows by @p numCols matrix of amplitudes, as row-major nested pointers.
747 * @param[in] numRows the total number of rows to overwrite.
748 * @param[in] numCols the total number of columns to overwrite.
749 * @throws @validationerror
750 * - if @p qureg is uninitialised.
751 * - if @p qureg is not a density matrix.
752 * - if @p startRow, @p startCol, @p numRows and @p numCols describe a block outside @p qureg.
753 * @see
754 * - setQuregAmps()
755 * - setDensityQuregFlatAmps()
756 * - setQuregToWeightedSum()
757 * - setQuregToRenormalized()
758 * @notyetvalidated
759 * @author Tyson Jones
760 */
761void setDensityQuregAmps(Qureg qureg, qindex startRow, qindex startCol, qcomp** amps, qindex numRows, qindex numCols);
762
763
764/** Overwrites a contiguous range of density-matrix amplitudes, indexing @p qureg
765 * as a column-linearised form.
766 *
767 * @formulae
768 *
769 * Let @f$ \dmrho = @f$ @p qureg contain @f$N@f$ qubits, with amplitudes @f$ \alpha_{ij} @f$.
770 * @f[
771 \dmrho = \sum\limits_i^{2^N} \sum\limits_j^{2^N} \alpha_{ij} \ket{i}\bra{j}.
772 * @f]
773 * Internally, this matrix of dimension @f$ 2^N \times 2^N @f$ is stored in a vectorised
774 * form @f$ \ket{\rho} @f$ of dimension @f$ 2^{2N} \times 1 @f$, which concatenates the columns
775 * of @f$ \dmrho @f$.
776 * @f[
777 \begin{aligned}
778 \ket{\rho} &= \sum\limits_i^{2^N} \sum\limits_j^{2^N} \alpha_{ij} \ket{j} \ket{i} \\
779 &= \sum\limits_k^{2^{2N}} \beta_k \ket{k}
780 \end{aligned}
781 * @f]
782 * Let @f$s = @f$ @p startInd, @f$n = @f$ @p numAmps, and @f$\gamma_i = @f$ `amps[i]`.
783 * This function overwrites amplitudes @f$\beta_k : s \le k < s + n @f$ with @f$\gamma_k@f$.
784 *
785 * @equivalences
786 *
787 * - This is equivalent to calling setQuregAmps() upon @p qureg, with identical parameters,
788 * when treating the column-wise linearised @p qureg as a statevector.
789 *
790 * @param[in,out] qureg the density matrix to modify.
791 * @param[in] startInd the first flattened, global index to overwrite.
792 * @param[in] amps an array of @p numAmps amplitudes.
793 * @param[in] numAmps the number of flattened amplitudes to overwrite.
794 * @throws @validationerror
795 * - if @p qureg is uninitialised or is not a density matrix.
796 * - if @p startInd or @p numAmps describes a range outside the flattened matrix.
797 * @notyetvalidated
798 * @author Tyson Jones
799 */
800void setDensityQuregFlatAmps(Qureg qureg, qindex startInd, qcomp* amps, qindex numAmps);
801
802
803/// @notyetdoced
804/// @notyettested
805void setQuregToClone(Qureg outQureg, Qureg inQureg);
806
807
808/// @notyetdoced
809/// @notyettested
810void setQuregToWeightedSum(Qureg out, qcomp* coeffs, Qureg* in, int numIn);
811
812
813/// @notyetdoced
814/// @notyettested
815void setQuregToMixture(Qureg out, qreal* probs, Qureg* in, int numIn);
816
817
818/// @notyetdoced
819/// @notyetvalidated
820qreal setQuregToRenormalized(Qureg qureg);
821
822
823/// @notyetdoced
824/// @notyetvalidated
826
827
828/// @notyetdoced
829/// @notyettested
830void setQuregToPartialTrace(Qureg out, Qureg in, int* traceOutQubits, int numTraceQubits);
831
832
833/// @notyetdoced
834/// @notyettested
835void setQuregToReducedDensityMatrix(Qureg out, Qureg in, int* retainQubits, int numRetainQubits);
836
837
838/** @} */
839
840
841
842// end de-mangler
843#ifdef __cplusplus
844}
845#endif
846
847
848
849/*
850 * C++ OVERLOADS
851 *
852 * which are only accessible to C++ binaries, and accept
853 * arguments more natural to C++ (e.g. std::vector). We
854 * manually add these to their respective Doxygen doc groups.
855 */
856
857#ifdef __cplusplus
858
859#include <vector>
860
861
862/// @ingroup init_amps
863/// @notyettested
864/// @notyetdoced
865/// @notyetvalidated
866/// @cpponly
867/// @see setQuregAmps()
868void setQuregAmps(Qureg qureg, qindex startInd, std::vector<qcomp> amps);
869
870
871/// @ingroup init_amps
872/// @notyettested
873/// @notyetdoced
874/// @notyetvalidated
875/// @cpponly
876/// @see setDensityQuregAmps()
877void setDensityQuregAmps(Qureg qureg, qindex startRow, qindex startCol, std::vector<std::vector<qcomp>> amps);
878
879
880/// @ingroup init_amps
881/// @notyettested
882/// @notyetdoced
883/// @notyetvalidated
884/// @cpponly
885/// @see setDensityQuregFlatAmps()
886void setDensityQuregFlatAmps(Qureg qureg, qindex startInd, std::vector<qcomp> amps);
887
888
889/// @ingroup init_amps
890/// @notyettested
891/// @notyetdoced
892/// @notyetvalidated
893/// @cpponly
894/// @see setQuregToPartialTrace()
895void setQuregToPartialTrace(Qureg out, Qureg in, std::vector<int> traceOutQubits);
896
897
898/// @ingroup init_amps
899/// @notyettested
900/// @notyetdoced
901/// @notyetvalidated
902/// @cpponly
903/// @see setQuregToReducedDensityMatrix()
904void setQuregToReducedDensityMatrix(Qureg out, Qureg in, std::vector<int> retainQubits);
905
906
907/// @ingroup init_amps
908/// @notyetdoced
909/// @cpponly
910/// @see setQuregToWeightedSum()
911void setQuregToWeightedSum(Qureg out, std::vector<qcomp> coeffs, std::vector<Qureg> in);
912
913
914/// @ingroup init_amps
915/// @notyetdoced
916/// @cpponly
917/// @see setQuregToMixture()
918void setQuregToMixture(Qureg out, std::vector<qreal> probs, std::vector<Qureg> in);
919
920
921#endif // __cplusplus
922
923
924
925#endif // INITIALISATIONS_H
926
927/** @} */ // (end file-wide doxygen defgroup)
void setDensityQuregFlatAmps(Qureg qureg, qindex startInd, qcomp *amps, qindex numAmps)
void setQuregToReducedDensityMatrix(Qureg out, Qureg in, int *retainQubits, int numRetainQubits)
void setQuregToWeightedSum(Qureg out, qcomp *coeffs, Qureg *in, int numIn)
void setQuregToPauliStrSum(Qureg qureg, PauliStrSum sum)
void setQuregToClone(Qureg outQureg, Qureg inQureg)
void setQuregToMixture(Qureg out, qreal *probs, Qureg *in, int numIn)
void setQuregAmps(Qureg qureg, qindex startInd, qcomp *amps, qindex numAmps)
void setQuregToPartialTrace(Qureg out, Qureg in, int *traceOutQubits, int numTraceQubits)
qreal setQuregToRenormalized(Qureg qureg)
void setDensityQuregAmps(Qureg qureg, qindex startRow, qindex startCol, qcomp **amps, qindex numRows, qindex numCols)
void initArbitraryPureState(Qureg qureg, qcomp *amps)
void initRandomPureState(Qureg qureg)
void initPlusState(Qureg qureg)
void initZeroState(Qureg qureg)
void initPureState(Qureg qureg, Qureg pure)
void initDebugState(Qureg qureg)
void initRandomMixedState(Qureg qureg, qindex numPureStates)
void initClassicalState(Qureg qureg, qindex stateInd)
void initBlankState(Qureg qureg)
Definition qureg.h:49