.. |:spellcheck-disable:| replace:: \

Pre-Trajectory Sampling with Batch Execution (PTSBE)
====================================================

.. _ptsbe:

Pre-Trajectory Sampling with Batch Execution (PTSBE) is a method for
efficiently sampling noisy quantum circuits [Patti2025]_. It builds on
quantum trajectory methods [Carmichael2007]_, which simulate noise by
stochastically selecting a Kraus operator at each noise site and evolving a
pure statevector rather than the full density matrix. Traditional trajectory 
methods construct a new statevector for every measurement shot.
PTSBE instead pre-samples a set of unique noise
realizations (trajectories) and batches shots across them, so the number of
statevector simulations scales with unique trajectories but not the total shots.
As the noise pre-sampling and state post-sampling are tasks with only 
low-degree polynomial complexity, while the state construction
is, in general, of exponential complexity, PTSBE allows us to gather noisy quantum data
orders of magnitude quicker than traditional trajectory sampling methods by
allowing finely-tuned batch sampling of simulated statevectors. PTSBE can be used to 
capture millions of times more noisy shot data, which can then be used as e.g., training data 
in ML tasks such as AI decoders, or it can be deployed proportionally, capturing the exact 
statistics of the problem while still offering a considerable speedup. In particular,
PTSBE achieves traditional trajectory simulation accuracy at a fraction of the computational 
cost when the number of unique trajectories (errors) is much smaller than the total
shot count [Patti2025]_.

Conceptual Overview
-------------------

A quantum circuit subject to noise can be described by a set of Kraus operators
applied at each gate location. At each noise site, the environment selects one
Kraus operator with some probability. A **trajectory** is one complete
assignment of Kraus operators across all noise sites in the circuit. Its
probability is the product of the probabilities of the chosen operators at
each site.

PTSBE works in three phases:

.. list-table::
   :widths: 10 30 60
   :header-rows: 1

   * - Phase
     - Name
     - Description
   * - 1
     - Trajectory Sampling
     - Draw *T* unique trajectories from the full noise space using a sampling
       strategy. Each trajectory specifies which Kraus operator fires at every
       noise site.
   * - 2
     - Shot Allocation
     - Distribute the total *N* shots across the *T* trajectories according to
       a shot allocation strategy (e.g. proportional to trajectory probability).
   * - 3
     - Batch Execution
     - Simulate each trajectory as a pure-state circuit. The per-trajectory
       measurement outcomes are merged into a single :class:`~cudaq.SampleResult`.

Because trajectories are reused across many shots, the number of circuit
simulations scales with the number of unique trajectories *T*, not the shot
count *N*.

When to Use PTSBE
-----------------

PTSBE is most beneficial when:

- The circuit has few distinct noise sites so the trajectory space is
  manageable.
- A large shot count is required (1 000 – 1 000 000+) so the reuse of
  trajectories provides a significant speed-up.
- The shots are intended for a data-hungry downstream task that is not necessarily
  inhibited by correlated sampling, such as training AI models

Benchmarks from the original paper [Patti2025]_ illustrate the potential
speed-ups:

- 35-qubit statevector simulation (magic state distillation): up to
  10⁶× speedup over conventional trajectory methods, producing one
  trillion shots on 4 NVIDIA H100 GPUs.
- 85-qubit tensor network simulation (magic state distillation): 16×
  speedup, producing one million shots.

PTSBE is particularly well-suited for generating large synthetic datasets of
noisy measurement outcomes, such as training data for machine-learning–based
quantum error correction (QEC) decoders [Patti2025]_.

PTSBE requires:

- A static circuit — no mid-circuit measurements or
  measurement-dependent conditional logic.
- A local simulator backend.

Quick Start
-----------

The example below simulates a two-qubit Bell circuit under depolarizing noise.

.. tab:: Python

   .. literalinclude:: ../../snippets/python/using/examples/ptsbe/bell_circuit_under_depol_noise.py
        :language: python
        :start-after: [Begin PTSBE_Bell]
        :end-before: [End PTSBE_Bell]


.. tab:: C++

   .. literalinclude:: ../../snippets/cpp/using/examples/ptsbe/bell_circuit_under_depol_noise.cpp
        :language: cpp
        :start-after: [Begin PTSBE_Bell]
        :end-before: [End PTSBE_Bell]



Usage Tutorial
--------------

Controlling the Number of Trajectories
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

By default, PTSBE generates up to ``shots_count`` unique trajectories.
For large shot counts set ``max_trajectories`` to cap trajectory generation
and gain the batching benefit:

.. tab:: Python

   .. code-block:: python

      result = ptsbe.sample(
          bell,
          shots_count=100_000,
          noise_model=noise,
          max_trajectories=500,
      )

.. tab:: C++

   .. code-block:: cpp

      cudaq::ptsbe::sample_options opts;
      opts.shots = 100'000;
      opts.noise = noise;
      opts.ptsbe.max_trajectories = 500;

      auto result = cudaq::ptsbe::sample(opts, bell);

Choosing a Trajectory Sampling Strategy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Four strategies control which trajectories are selected from the noise space.

- :cpp:class:`cudaq::ptsbe::ProbabilisticSamplingStrategy` (default)
- :cpp:class:`cudaq::ptsbe::OrderedSamplingStrategy`
- :cpp:class:`cudaq::ptsbe::ExhaustiveSamplingStrategy`
- :cpp:class:`cudaq::ptsbe::ConditionalSamplingStrategy`

.. tab:: Python

   .. literalinclude:: ../../snippets/python/using/examples/ptsbe/sampling_strategies.py
        :language: python
        :start-after: [Begin PTSBE_Sampling]
        :end-before: [End PTSBE_Sampling]



.. tab:: C++

   .. literalinclude:: ../../snippets/cpp/using/examples/ptsbe/sampling_strategies.cpp
        :language: cpp
        :start-after: [Begin PTSBE_Sampling]
        :end-before: [End PTSBE_Sampling]



.. _ptsbe-shot-allocation:

Shot Allocation Strategies
~~~~~~~~~~~~~~~~~~~~~~~~~~~

After trajectories are selected, shots are distributed across them:

.. doxygenstruct:: cudaq::ptsbe::ShotAllocationStrategy
    :members:

.. doxygenenum:: cudaq::ptsbe::ShotAllocationStrategy::Type

.. tab:: Python

   .. literalinclude:: ../../snippets/python/using/examples/ptsbe/shot_allocation.py
        :language: python
        :start-after: [Begin PTSBE_Shot_Allocation]
        :end-before: [End PTSBE_Shot_Allocation]



.. tab:: C++

   .. literalinclude:: ../../snippets/cpp/using/examples/ptsbe/shot_allocation.cpp
        :language: cpp
        :start-after: [Begin PTSBE_Shot_Allocation]
        :end-before: [End PTSBE_Shot_Allocation]



Inspecting Execution Data
~~~~~~~~~~~~~~~~~~~~~~~~~~

Set ``return_execution_data=True`` to attach the full execution trace —
circuit instructions, sampled trajectories, and per-trajectory counts — to
the result. This API is experimental and may be subject to change in future releases.

.. tab:: Python

   .. literalinclude:: ../../snippets/python/using/examples/ptsbe/inspect_execution_data.py
        :language: python
        :start-after: [Begin PTSBE_Execution_Data]
        :end-before: [End PTSBE_Execution_Data]

.. tab:: C++

   .. literalinclude:: ../../snippets/cpp/using/examples/ptsbe/inspect_execution_data.cpp
        :language: cpp
        :start-after: [Begin PTSBE_Execution_Data]
        :end-before: [End PTSBE_Execution_Data]

Trajectory vs Shot Trade-offs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The central tension in PTSBE is between trajectory count *T* and
shots per trajectory *N/T*.

Using more trajectories covers more of the noise space and reduces bias in the
estimated distribution, but since each trajectory is simulated independently the
simulation cost scales linearly with *T*.

Fewer trajectories mean each one accumulates more shots, which reduces
shot-noise variance per trajectory and lowers wall-clock time.

.. rubric:: Practical guidance

As a rule of thumb, ``max_trajectories`` between 100 and 10 000 covers the
majority of practical use cases. Below 100, bias may dominate. Above 10 000,
the simulation cost approaches that of a conventional density-matrix run.
Useful to perform a warm-up run sweeping the number of trajectories to understand
the convergence behavior.

Backend Requirements
^^^^^^^^^^^^^^^^^^^^^

PTSBE requires a backend that supports trajectory-based noisy simulation.
The supported targets are those marked with ``*`` in the
`simulator table <https://nvidia.github.io/cuda-quantum/latest/using/backends/simulators.html>`_,
plus ``density-matrix-cpu`` and ``qpp-cpu``:

.. list-table::
   :widths: 35 65
   :header-rows: 1

   * - Target
     - Notes
   * - ``nvidia``
     - Single GPU, single/double precision. General purpose.
   * - ``nvidia, option=mgpu``
     - Multi-GPU / multi-node. Suitable for large qubit counts (33+).
   * - ``nvidia, option=mqpu``
     - Multi-QPU asynchronous distribution across multiple GPUs.
   * - ``tensornet``
     - Tensor network method. Exact simulation of shallow-depth circuits.
       Handles thousands of qubits.
   * - ``tensornet-mps``
     - Matrix product state (approximate). Efficient for square-shaped circuits.
   * - ``qpp-cpu``
     - CPU state vector simulator. Lightweight option for small circuits
       (< 28 qubits).

Set the target:

.. tab:: Python

   .. code-block:: python

      # Single GPU (most common for production)
      cudaq.set_target("nvidia")

      # CPU density matrix
      cudaq.set_target("density-matrix-cpu")

      # Multi-GPU for large circuits
      cudaq.set_target("nvidia", option="mgpu")

      # Tensor network for wide shallow circuits
      cudaq.set_target("tensornet")

.. tab:: C++

   .. code-block:: cpp

      // Set via CMake target or the --target flag at runtime.
      // See backend documentation for available options.

See :doc:`../backends/backends` for full details on each target including precision and
qubit count limits.

References
^^^^^^^^^^^

.. [Carmichael2007] Carmichael, H. J. *Quantum jumps revisited: An overview of quantum trajectory theory.* Quantum Future From Volta and
   Como to the Present and Beyond: Proceedings of the Xth Max Born Symposium Held in Przesieka, Poland, 24–27 September 1997. Berlin,
   Heidelberg: Springer Berlin Heidelberg, 2007.
   https://link.springer.com/chapter/10.1007/bfb0105336

.. [Patti2025] Taylor L. Patti, Thien Nguyen, Justin G. Lietz,
   Alexander J. McCaskey, Brucek Khailany,
   *Augmenting Simulated Noisy Quantum Data Collection by Orders of Magnitude Using Pre-Trajectory Sampling with Batched Execution.*
   Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis. 2025.
   https://arxiv.org/abs/2504.16297

.. |:spellcheck-enable:| replace:: \
