pub unsafe extern "C" fn createQureg(
numQubits: c_int,
env: QuESTEnv,
) -> QuregExpand description
Creates a state-vector Qureg object representing a set of qubits which will remain in a pure state.
Allocates space for a state-vector of complex amplitudes, which assuming a single ::qreal floating-point number requires qrealBytes, requires memory \f[ \text{qrealBytes} \times 2 \times 2^\text{numQubits};;\text{(bytes)}, \f] though there are additional memory costs in GPU and distributed modes.
The returned ::Qureg begins in the zero state, as produced by initZeroState().
Once created, the following ::Qureg fields are relevant in all backends:
- Qureg.numQubitsRepresented
- Qureg.isDensityMatrix
::QuESTEnv \p env must be prior created with createQuESTEnv().
§Serial
In serial and local (non-distributed) multithreaded modes, a state-vector \p Qureg costs only the memory above. For example, at double precision (#QuEST_PREC = 2, qrealBytes = 8), the memory costs are: \p numQubits | memory ———–– | ———–– 10 | 16 KiB 16 | 1 MiB 20 | 16 MiB 26 | 1 GiB 30 | 16 GiB
Individual amplitudes should be fetched and modified with functions like getAmp() and setAmps(). However, it is sometimes useful to access the state-vector directly, for example to create your own low-level (high performance) multithreaded functions. In those instants, Qureg.stateVec can be accessed directly, storing the real and imaginary components of the state-vector amplitudes in:
Qureg.stateVec.realQureg.stateVec.imag
The total number of amplitudes in the state-vector is
- Qureg.numAmpsTotal
For example,
Qureg qureg = createQureg(10, env);
// ruin a perfectly good state-vector
for (long long int i=0; i<qureg.numAmpsTotal; i++) {
qureg.stateVec.real[i] = rand();
qureg.stateVec.imag[i] = rand();
}\n
§GPU
In GPU-accelerated mode, an additional state-vector is created in GPU memory. Therefore both RAM and VRAM must be of sufficient memory to store the state-vector, each of the size indicated in the Serial table above.
Note that many GPUs do not support quad precision ::qreal.
Individual amplitudes of the created ::Qureg should be fetched and modified with functions like getAmp() and setAmps(). This is especially important since the GPU state-vector can be accessed directly, and changes to Qureg.stateVec will be ignored and overwritten. To modify the state-vector “directly”, one must use copyStateFromGPU() and copyStateToGPU() before and after.
For example,
Qureg qureg = createQureg(10, env);
// ruin a perfectly good state-vector
for (long long int i=0; i<qureg.numAmpsTotal; i++) {
qureg.stateVec.real[i] = rand();
qureg.stateVec.imag[i] = rand();
}
copyStateToGPU();\n
§Distributed
In distributed mode, the state-vector is uniformly partitioned between the N distributed nodes.
Only a power-of-2 number of nodes N may be used (e.g. N = 1, 2, 4, 8, …). There must additionally be at least 1 amplitude of a state-vector stored on each node. This means one cannot create a state-vector ::Qureg with fewer than \f$\log_2(\text{N})\f$ qubits.
In addition to Qureg.stateVec, additional memory is allocated on each node for communication buffers, of size equal to the state-vector partition. Hence the total memory per-node required is: \f[ 2 \times \text{qrealBytes} \times 2 \times 2^\text{numQubits}/N ;;\text{(bytes)}, \f]
For example, at double precision (#QuEST_PREC = 2, qrealBytes = 8), the memory costs are:
| \p numQubits | memory per node | ||||
|---|---|---|---|---|---|
| N = 2 | N = 4 | N = 8 | N = 16 | N = 32 | |
| 10 | 16 KiB | 8 KiB | 4 KiB | 2 KiB | 1 KiB |
| 20 | 16 MiB | 8 MiB | 4 MiB | 2 MiB | 1 MiB |
| 30 | 16 GiB | 8 GiB | 4 GiB | 2 GiB | 1 GiB |
| 40 | 16 TiB | 8 TiB | 4 TiB | 2 TiB | 1 TiB |
State-vector amplitudes should be set and modified using getAmp() and setAmps(). Direct modification is possible, but should be done extremely carefully, since each node only stores a partition of the full state-vector, which itself mightn’t fit on any single node. Furthermore, an asynchronous MPI process may may unexpectedly modify local amplitudes; avoid this with syncQuESTEnv().
The fields relevant to distribution are:
- Qureg.numAmpsPerChunk: the length of Qureg.stateVec (
.realand.imag) on each node. - Qureg.chunkId: the id of the node, from 0 to N-1.
Therefore, this code is valid
syncQuESTEnv(env);
// set state |i> to have amplitude i
for (long long int i=0; i<qureg.numAmpsPerChunk; i++)
qureg.stateVec.real[i] = i + qureg.chunkId * qureg.numAmpsPerChunk;while the following erroneous code would cause a segmentation fault:
// incorrectly attempt to set state |i> to have amplitude i
for (long long int i=0; i<qureg.numAmpsTotal; i++)
qureg.stateVec.real[i] = i;\n
@see
- createDensityQureg() to create a density matrix of the equivalent number of qubits, which can enter noisy states.
- createCloneQureg() to create a new qureg of the size and state of an existing qureg.
- destroyQureg() to free the allocated ::Qureg memory.
- reportQuregParams() to print information about a ::Qureg.
- copyStateFromGPU() and copyStateToGPU() for directly modifying state-vectors in GPU mode.
- syncQuESTEnv() for directly modifying state-vectors in distributed mode.
@ingroup type @returns an object representing the set of qubits @param[in] numQubits number of qubits in the system @param[in] env object representing the execution environment (local, multinode etc) @throws invalidQuESTInputError()
- if \p numQubits <= 0
- if \p numQubits is so large that the number of amplitudes cannot fit in a long long int type,
- if in distributed mode, there are more nodes than elements in the would-be state-vector @throws exit
- if in GPU mode, but GPU memory cannot be allocated. @author Ania Brown @author Tyson Jones (validation, doc)