braket.default_simulator.simulation_strategies.batch_operation_strategy module

braket.default_simulator.simulation_strategies.batch_operation_strategy.apply_operations(state: numpy.ndarray, qubit_count: int, operations: List[braket.default_simulator.operation.GateOperation], batch_size: int) → numpy.ndarray[source]

Applies operations to a state vector in batches of size \(batch\_size\).

\(operations\) is partitioned into contiguous batches of size \(batch\_size\) (with remainder). The state vector is treated as a type \((qubit\_count, 0)\) tensor, and each operation is treated as a type \((target\_length, target\_length)\) tensor (where \(target\_length\) is the number of targets the operation acts on), and each batch is contracted in an order optimized among the operations in the batch. Larger batches can be significantly faster (although this is not guaranteed), but will use more memory.

For example, if we have a 4-qubit state \(S\) and a batch with two gates \(G1\) and \(G2\) that act on qubits 0 and 1 and 1 and 3, respectively, then the state vector after applying the batch is \(S^{mokp} = S^{ijkl} G1^{mn}_{ij} G2^{op}_{nl}\).

Depending on the batch size, number of qubits, and the number and types of gates, the speed can be more than twice that of applying operations one at a time. Empirically, noticeable performance improvements were observed starting with a batch size of 10, with increasing performance gains up to a batch size of 50. We tested this with 16 GB of memory. For batch sizes greater than 50, consider using an environment with more than 16 GB of memory.

Parameters:
  • state (np.ndarray) – The state vector to apply \(operations\) to, as a type \((qubit\_count, 0)\) tensor
  • qubit_count (int) – The number of qubits in the state
  • operations (List[GateOperation]) – The operations to apply to the state vector
  • batch_size – The number of operations to contract in each batch
Returns:

np.ndarray – The state vector after applying the given operations, as a type (num_qubits, 0) tensor