Function createQureg

Source
pub unsafe extern "C" fn createQureg(
    numQubits: c_int,
    env: QuESTEnv,
) -> Qureg
Expand 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.real
  • Qureg.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 numQubitsmemory per node
N = 2N = 4N = 8N = 16N = 32
1016 KiB8 KiB4 KiB2 KiB1 KiB
2016 MiB8 MiB4 MiB2 MiB1 MiB
3016 GiB8 GiB4 GiB2 GiB1 GiB
4016 TiB8 TiB4 TiB2 TiB1 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 (.real and .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)