FrameSimulator#

class cuquantum.stabilizer.FrameSimulator(
num_qubits: int,
num_paulis: int,
num_measurements: int = 0,
num_detectors: int = 0,
randomize_measurements: bool = True,
x_table: ndarray | ndarray | None = None,
z_table: ndarray | ndarray | None = None,
measurement_table: ndarray | ndarray | None = None,
bit_packed: bool = False,
package: Literal['numpy', 'cupy'] = 'numpy',
seed: int | None = None,
stream: int | Stream | None = None,
options: Options | None = None,
)[source]#

Simulates quantum circuits using the stabilizer frame formalism.

This class simulates quantum circuits by tracking Pauli frame errors. It manages the X and Z bit tables, measurement table, and applies circuits.

Parameters:
  • num_qubits – Number of qubits in the simulation.

  • num_paulis – Number of Pauli frame samples (shots).

  • num_measurements – Number of measurements in the circuit (default: 0).

  • randomize_measurements – Whether to randomize frame after measurements (default: True).

  • x_table – Optional initial X bit table. If provided, memory is not owned by simulator.

  • z_table – Optional initial Z bit table. If provided, memory is not owned by simulator.

  • measurement_table – Optional initial measurement table.

  • bit_packed – Whether the input tables are in bit-packed format.

  • package – Package to use for the tables. “numpy” or “cupy”. This has lower priority than the package of the input tables, if any.

  • random_seed – Random seed for the simulation (default: None).

  • stream – Optional CUDA stream.

  • options – Optional Options configuration.

num_qubits#

Number of qubits.

num_paulis#

Number of Pauli samples.

num_measurements#

Number of measurements.

Example

>>> circ = Circuit("H 0\nCNOT 0 1\nM 1")
>>> sim = FrameSimulator(2, 1024, num_measurements=1)
>>> sim.apply(circ)
>>> measurements = sim.get_measurement_bits()

Methods

__init__(
num_qubits: int,
num_paulis: int,
num_measurements: int = 0,
num_detectors: int = 0,
randomize_measurements: bool = True,
x_table: ndarray | ndarray | None = None,
z_table: ndarray | ndarray | None = None,
measurement_table: ndarray | ndarray | None = None,
bit_packed: bool = False,
package: Literal['numpy', 'cupy'] = 'numpy',
seed: int | None = None,
stream: int | Stream | None = None,
options: Options | None = None,
)[source]#

Initialize a FrameSimulator.

If input bit tables are not provided, the simulator will allocate internal memory and own it. The simulator converts inputs and owns the converted tables if the input is

  1. a numpy.ndarray and bit_packed=False

  2. a cupy.ndarray and bit_packed=False

If input is a cupy.ndarray and bit_packed=True, the simulator will not allocate internal memory and will use the provided tables.

Parameters:
  • num_qubits – Number of qubits to simulate.

  • num_paulis – Number of Pauli frame samples.

  • num_measurements – Number of measurements to track.

  • num_detectors – Number of detector instructions.

  • randomize_measurements – Randomize frame after measurement gates.

  • x_table – Pre-allocated X bit table.

  • z_table – Pre-allocated Z bit table.

  • measurement_table – Pre-allocated measurement table.

  • bit_packed – Whether the input tables are in bit-packed format.

  • package – Package to use for the tables, either "numpy" or "cupy". This has lower priority than the package of the input tables, if any.

  • seed – Seed for a generator that will produce default seed for every call of apply().

  • stream – Optional CUDA stream.

  • options – Optional Options configuration.

apply(
circuit: Circuit,
seed: int | None = None,
stream: int | Stream | None = None,
) None[source]#

Apply a circuit to the Pauli frames.

Parameters:
  • circuit – Circuit object to apply.

  • seed – Optional random seed for measurement randomization. If provided, overrides the seed set during initialization.

  • randomize_measurements – Optional boolean to randomize measurements. If provided, overrides the setting from initialization.

Raises:

ValueError – If circuit has more qubits than simulator.

get_measurement_bits(
bit_packed: bool = True,
) ndarray | ndarray[source]#

Retrieve the measurement table.

Parameters:

bit_packed – If True, return as bit-packed array (default). If False, unpack bits and return as (num_measurements, num_paulis) array.

Returns:

Measurement results array.

If bit_packed=False, and FrameSimulator.operands_package tt operands_package is "cupy", the returned array is a view into the simulator state. In other cases, the returned array is a copy of the simulator state.

Example

>>> sim = FrameSimulator(2, 1024, num_measurements=1)
>>> measurements = sim.get_measurement_bits(bit_packed=False)
>>> measurements.shape
(1, 1024)
get_pauli_table(
bit_packed: bool = True,
) PauliTable[source]#

Retrieve the X and Z Pauli tables.

Parameters:

bit_packed – If True, return as bit-packed arrays (default). If False, unpack bits and return as (num_qubits, num_paulis) arrays.

Returns:

PauliTable object.

The

If bit_packed=False, and self.operands_package is cupy, the returned PauliTable has a view into the simulator state. That is, the contents of PauliTable can be indirectly changed by the simulator. In other cases, the returned PauliTable references a copy of the simulator state.

Example

>>> sim = FrameSimulator(2, 1024)
>>> pauli_table = sim.get_pauli_table(bit_packed=False)
>>> pauli_table.num_qubits
2
>>> pauli_table.num_paulis
1024
get_pauli_xz_bits(
bit_packed: bool = True,
) Tuple[ndarray | ndarray, ndarray | ndarray][source]#

Get the X and Z bits as raw arrays. :param bit_packed: If True, return as bit-packed arrays (default).

If False, unpack bits and return as (num_qubits, num_paulis) arrays.

Returns:

Tuple of (x_bits, z_bits) as arrays.

The package of arrays is determined by self.operands_package, which is set by the last call to set_input_tables or the package parameter to constructor.

If bit_packed=False, and package is cupy, the returned arrays are views into the simulator state. In other cases, the returned arrays are copies of the simulator state.

Example

>>> sim = FrameSimulator(2, 1024)
>>> x_bits, z_bits = sim.get_pauli_xz_bits(bit_packed=False)
>>> x_bits.shape
(2, 1024)
>>> z_bits.shape
(2, 1024)
set_input_tables(
x: ndarray | ndarray | None = None,
z: ndarray | ndarray | None = None,
m: ndarray | ndarray | None = None,
bit_packed: bool = True,
stream: int | Stream | None = None,
) None[source]#

Set the X and Z Pauli tables.

Parameters:
  • x_table – New X bit table.

  • z_table – New Z bit table. Must be the same package and shape as x_table.

  • m_table – New measurement table.

  • bit_packed – If True, input is expected to be bit-packed (default). If False, will pack bits automatically

  • stream – Optional CUDA stream for the operation.

The tables can be located either on CPU or device as specified by device_id. This call will update operands_package. The returned data from subsequent calls to get_pauli_table(), get_pauli_xz_bits(), and get_measurement_bits() will be of the same type and device as the input tables.

Example

>>> sim = FrameSimulator(2, 1024)
>>> x_table = np.zeros(2*1024//8, dtype=np.uint8)
>>> z_table = np.zeros(2*1024//8, dtype=np.uint8)
>>> sim.set_pauli_table(x_table, z_table)

Attributes

device_id#

The device for inputs and outputs.

handle#

Return the underlying C handle object.

operands_package#

The package last used inputs and of returned outputs.

randomize_measurements: bool = True#