cuStateVec Ex: State Vector Expansion#
The cuStateVec Ex API supports expanding a state vector dynamically by adding wires after the state vector instance is created.
Overview#
By default, a state vector is created with a fixed number of wires specified at
configuration time. When the resizable capability is enabled, the state vector is
instead created with zero wires and grown by successive calls to
custatevecExStateVectorAddWires(). Each call appends new wires initialized to the
\(|0\rangle\) state.
State vector expansion is supported for all distribution models: single-device, multi-device, and multi-process, including the migration domain where the distribution supports it. Host memory extension (the migration domain) is supported for single-device and multi-process state vectors, but not for multi-device state vectors.
Concepts#
Resizable capability#
To create a resizable state vector, set the CUSTATEVEC_EX_SV_CAPABILITY_RESIZABLE flag in
the capability argument of a configuration API
(custatevecExConfigureStateVectorSingleDevice(),
custatevecExConfigureStateVectorMultiDevice(), or
custatevecExConfigureStateVectorMultiProcess()).
With this capability enabled, the numeric arguments of the configuration API
(numWires, numDeviceWires, and the per-layer wire counts in the multi-process case)
are interpreted as maximum sizes rather than the actual sizes. The state vector is
created with zero wires, and wires are added later by custatevecExStateVectorAddWires()
up to the configured maxima.
If the state vector was not created with this capability,
custatevecExStateVectorAddWires() returns CUSTATEVEC_STATUS_INVALID_CONFIGURATION.
Index bit domains#
Each wire of a state vector is mapped to an index bit that belongs to one of the following
index bit domains, specified by custatevecExIndexBitDomain_t when adding wires:
CUSTATEVEC_EX_INDEX_BIT_DOMAIN_LOCAL: the wire is added to the local index bits. The size of the local sub state vector grows accordingly.CUSTATEVEC_EX_INDEX_BIT_DOMAIN_MIGRATION: the wire is added to the migration index bits, bringing additional host-memory-backed sub state vectors online. Available only when migration is configured (see State Vector Migration).CUSTATEVEC_EX_INDEX_BIT_DOMAIN_GLOBAL_DEVICE: the wire is added to the global device index bits, bringing additional devices or processes into the active set.
Adding wires#
State vector expansion is a discrete step in the simulation flow: wires are added only by an
explicit call to custatevecExStateVectorAddWires(), and no other API adds wires. An
expansion only extends the state vector and preserves the existing wires, their ordering, and
the stored amplitudes.
While the existing amplitudes are preserved, expansion can change how the state vector is distributed, and therefore the sub state vector index assignment depends on the index bit domain that grows:
Adding local wires only enlarges each sub state vector. The number of sub state vectors and their indices are unchanged.
Adding global device wires brings additional devices (multi-device) or processes (multi-process) into the active set, so the number of devices or processes that hold a sub state vector changes.
Adding migration wires changes the number of sub state vectors held by each process, and thus the sub state vector indices that a process owns.
The sub state vectors currently owned by the calling process can be queried at any time with
custatevecExStateVectorGetProperty(): CUSTATEVEC_EX_SV_PROP_NUM_SUBSVS returns their
count, and CUSTATEVEC_EX_SV_PROP_SUBSV_INDICES returns the list of their indices.
When adding wires, wires are inserted at the most significant index bit position of the
specified index bit domain. Accordingly, the new wire appears at the corresponding position
in the wire ordering. As a result, the arrangement of wires across the index bit positions
after expansion is the wire ordering itself.
Wire IDs are assigned sequentially. If the state vector currently has numWires wires,
the new wire IDs are numWires, numWires + 1, …, numWires + numWiresToAdd - 1,
and they are returned through the wiresAdded argument.
For the relationship between wires, index bits, and wire ordering, see State Vector Fundamentals.
custatevecExStateVectorAddWires() can be called repeatedly with any combination of
domains. Wire IDs are assigned in call order across all domains, so the wires of a single
domain are not necessarily contiguous in wire ID.
Within the global device domain, wires fill the global device index bit layers in the order in which the layers appear in the configuration (for example, the P2P layer before the communicator layer), spilling to the next layer only when the current layer’s capacity is exhausted.
Figure (a) shows several calls adding wires to the local and global device domains. Figure (b) shows adding wires including the migration domain.
Adding wires with custatevecExStateVectorAddWires(). The filling order within each
domain and the assigned wire IDs are shown. The bottom row shows the resulting wire
ordering, that is, the wire mapped to each index bit position. (a) Adding wires to the
local and global device index bit domains. (b) Adding wires including the migration index
bit domain.#
Wire initialization#
Newly added wires are initialized as specified by custatevecExWireInitMode_t. The
current release provides CUSTATEVEC_EX_WIRE_INIT_MODE_ZERO, which initializes each new
wire to \(|0\rangle\). The state vector is updated as the tensor product with the block
of new wires:
Configuration#
To create a resizable state vector, set the CUSTATEVEC_EX_SV_CAPABILITY_RESIZABLE flag in
the capability argument. The following example configures a single-device state vector whose
maximum size is 34 wires:
custatevecExDictionaryDescriptor_t svConfig;
int32_t maxNumWires = 34; // maximum total qubits
int32_t maxNumDeviceWires = 34; // maximum qubits on device
custatevecExConfigureStateVectorSingleDevice(
&svConfig, CUDA_C_64F, maxNumWires, maxNumDeviceWires, 0,
CUSTATEVEC_EX_SV_CAPABILITY_RESIZABLE);
custatevecExStateVectorDescriptor_t stateVector;
custatevecExStateVectorCreateSingleProcess(
&stateVector, svConfig, nullptr, 0, nullptr);
custatevecExDictionaryDestroy(svConfig);
The state vector is created with zero wires.
Expanding the state vector#
Use custatevecExStateVectorAddWires() to expand the state vector. The following example
adds 30 local wires:
std::vector<int32_t> wiresAdded(30);
custatevecExStateVectorAddWires(
stateVector,
CUSTATEVEC_EX_INDEX_BIT_DOMAIN_LOCAL,
30, // number of wires to add
CUSTATEVEC_EX_WIRE_INIT_MODE_ZERO,
wiresAdded.data());
Querying available capacity#
The maximum number of wires for each index bit domain is fixed at configuration time and
can be queried via custatevecExStateVectorGetProperty(). The difference between the
maximum and the current number of wires gives the remaining capacity that can still be
added.
Maximum |
Current |
|---|---|
|
|
|
|
|
|
|
|
|
|
When the state vector was not created with the resizable capability, each
MAX_NUM_*_WIRES property returns the same value as its NUM_*_WIRES counterpart.
Constraints#
The state vector must be created with the
CUSTATEVEC_EX_SV_CAPABILITY_RESIZABLEcapability.The number of wires of a domain after the call must not exceed the maximum configured at creation.
The number of migration wires must not exceed the number of local wires. Adding migration wires that would violate this prerequisite returns
CUSTATEVEC_STATUS_INVALID_VALUE.In the current release, the number of migration wires must not exceed
3.
Note
When a resizable state vector has no local wire, APIs that operate on the state vector, such
as gate application, measurement, sampling, and the state vector updater, return
CUSTATEVEC_STATUS_NOT_SUPPORTED. Add local wires with custatevecExStateVectorAddWires()
before applying operations. This restriction may be relaxed in a future release.