Sampling#

sample(sampling_kernel=None, shots=0, post_processor=None)[source]#

Takes samples from a quantum computation specified by a sampling kernel.

The sample function allows to take samples from a quantum computation specified by a sampling kernel — a Python function that receives only classical arguments and returns arbitrary values. Any QuantumVariables in the return are automatically measured and decoded.

The samples are returned in the form of a Jax Array which is shaped according to the shots parameter. Because of this, shots can only be a static integer (no dynamic values!). If you want to sample with a dynamic shot amount, look into Expectation Value.

Sample calls can be efficiently simulated via terminal sampling by setting the corresponding keyword within jaspify() to True.

Note

Terminal sampling (terminal_sampling=True inside jaspify()) does not support sampling kernels that return classical values alongside quantum variables, or kernels that return only classical values. Use terminal_sampling=False for those cases.

Even when the kernel returns only QuantumVariables, terminal sampling relies on the quantum state being independent of mid-circuit measurement outcomes. If a classical measurement result influences the quantum circuit (e.g. via control), terminal sampling may produce an invalid distribution because it simulates the quantum part only once. See terminal_sampling() for details and an example.

Parameters:
sampling_kernelcallable

A sampling kernel — a function receiving only classical arguments and returning one or more QuantumVariables, classical measurement results, or a mixture of both. The function must not receive quantum arguments because a quantum value would need to be copied for each sampling iteration, which is prohibited by the no-cloning theorem.

shotsint

The amounts of samples to take.

post_processorcallable, optional

A function to apply to the samples directly after measuring. By default no post processing is applied.

Returns:
callable

A classical, Jax traceable function returning a jax array containing the measurement results of each shot.

Raises:
Exception

Tried to sample with dynamic shots value (static integer required)

Exception

Tried to sample from sampling kernel taking a quantum value

Exception

Tried to use terminal sampling with a kernel that returns classical values (use terminal_sampling=False instead)

Examples

We prepare the state

\[\ket{\psi} = \frac{1}{\sqrt{2}} \left(\ket{0}\ket{0}\ket{\text{True}} + \ket{k}\ket{k}\ket{\text{True}})\right)\]
from qrisp import *
from qrisp.jasp import *

def sampling_kernel(k):
    a = QuantumFloat(4)
    b = QuantumFloat(4)

    qbl = QuantumBool()
    h(qbl)

    with control(qbl[0]):
        a[:] = k

    cx(a, b)

    return a, b

And subsequently sample from the QuantumFloats:

@jaspify
def main(k):

    sampling_function = sample(sampling_kernel,
                               shots = 10)

    return sampling_function(k)

print(main(3))

# Yields e.g.
# [[3. 3.]
#  [0. 0.]
#  [0. 0.]
#  [3. 3.]
#  [0. 0.]
#  [0. 0.]
#  [3. 3.]
#  [3. 3.]
#  [0. 0.]
#  [0. 0.]]
#
# Each row is either [0, 0] or [3, 3] with 50% probability each.
# The exact order of rows is random and varies between runs.

To demonstrate the post processing feature, we write a simple post processing function:

def post_processor(x, y):
    return 2*x + y//2

@jaspify
def main(k):

    sampling_function = sample(sampling_kernel,
                               shots = 10,
                               post_processor = post_processor)

    return sampling_function(k)

print(main(4))
# Yields e.g.
# [10. 10.  0.  0.  0.  0.  0.  0. 10. 10.]
#
# Each entry is either 0 or 10 with 50% probability each. The exact
# order of entries is random and varies between runs.

Sampling kernels returning classical values

A sampling kernel may also return classical values from mid-circuit measurements alongside (or instead of) quantum variables:

def mixed_kernel():
    qf = QuantumFloat(4)
    h(qf[0])
    h(qf[1])
    mes = measure(qf[1])      # classical measurement result
    return qf, mes            # mixed: quantum + classical

@jaspify
def main():
    return sample(mixed_kernel, shots=20)()

print(main())
# Yields e.g.:
# [[0. 0.]
#  [0. 0.]
#  [1. 0.]
#  ...]

Note

The above example uses @jaspify (which defaults to terminal_sampling=False). Using @jaspify(terminal_sampling=True) with a mixed-returns kernel will raise an error.