Backend Sampling#

backend_sampler(backend)[source]#

Route sample() and expectation_value() to a backend.

Calls to these functions are executed on a real backend instead of the Jaspify simulator.

Warning

The decorated function must use sample() or expectation_value() to trigger quantum execution. Direct quantum operations (gates, measurements) without a surrounding sample/EV call will raise a RuntimeError pointing you to jaspify().

Warning

Sampling kernels that rely on real-time feedback (e.g. mid-circuit measurements whose outcomes condition subsequent gates) are not supported. backend_sampler extracts and flattens the quantum circuit into a single static circuit before execution, so any classical control flow that depends on measurement results inside the kernel cannot be captured. Use jaspify() for such workloads.

Note

Only the quantum circuit is executed on the backend. All orchestration logic (the code in the decorated function that calls sample() and expectation_value(), passes arguments, and combines results) is traced into a Jaspr and compiled via jax.jit(). This means the non-coherence wrapping logic runs at JAX speed, even when orchestrating many sampling calls.

Parameters:
backendBackend Interface

The backend to execute on. See the Backend Interface documentation for available backends.

Returns:
callable

A decorator wrapping a Jasp-compatible function.

Raises:
RuntimeError

If the decorated function contains quantum operations without a surrounding sample() or expectation_value() call. Use jaspify() for single-shot simulation.

RuntimeError

If a sampling kernel contains real-time feedback (mid-circuit measurements whose outcomes — after classical post-processing — control subsequent quantum gates). The kernel’s quantum circuit must be fully static so it can be extracted and executed once. Use jaspify() for such workloads.

Examples

Basic sampling through a backend:

from qrisp import QuantumFloat, h, measure
from qrisp.jasp import sample, expectation_value, backend_sampler
from qrisp.interface import QrispSimulatorBackend

backend = QrispSimulatorBackend()

@backend_sampler(backend=backend)
def main(k):
    def kernel(k):
        qf = QuantumFloat(4)
        h(qf[0])
        return measure(qf)
    return sample(kernel, shots=100)(k)

result = main(1)
# result is a JAX array of shape (100,) with backend results

Using a different backend – any Backend works, for instance Qiskit’s AerSimulator:

from qiskit_aer import AerSimulator
from qrisp.interface import QiskitBackend

backend = QiskitBackend(backend=AerSimulator())

@backend_sampler(backend=backend)
def main():
    def kernel():
        qv = QuantumFloat(3)
        h(qv)
        return measure(qv)
    return sample(kernel, shots=200)()

result = main()

Using expectation_value():

@backend_sampler(backend=backend)
def main():
    def kernel():
        qf = QuantumFloat(4)
        h(qf[0])
        h(qf[1])
        return measure(qf)
    return expectation_value(kernel, shots=500)()

ev = main()  # scalar or vector JAX array

Multiple sample / expectation_value calls in the same function:

@backend_sampler(backend=backend)
def main():
    def kernel_a():
        qf = QuantumFloat(3)
        h(qf[0])
        return measure(qf)

    def kernel_b():
        qf = QuantumFloat(3)
        h(qf[1])
        return measure(qf)

    samples_a = sample(kernel_a, shots=100)()
    samples_b = sample(kernel_b, shots=50)()
    return samples_a, samples_b

a, b = main()
# Each call is independently routed through the backend.