RNG Capability¶
Helios provides an integrated Random Number Generator (RNG) capability within the guppy.std.qsystem module. This enables generation and consumption of random integers and floats during real-time program execution. The RNG capability is also backwards compatible on System Model H2 via pytket. The RNG capability is executed on an ARM Cortex processor coupled to the Helios control system. The primary use case for an integrated RNG is adaptive circuit construction, e.g., challenge circuit construction for Random Circuit Sampling (RCS).
Usage with Guppy¶
Guppy provides an RNG object that needs to be instantiated prior to consumption, and deallocated prior to program completion. The RNG capability provides instance methods to generate bounded integers and floats. Additionally, users can advance or rewind the state of the RNG instance to regenerate previously drawn random numbers.
Name |
Gate |
Expression |
Guppy |
|---|---|---|---|
Parameterized 1-Qubit Gate |
\(R_{xy} (\theta, \phi)\) |
\(e^{ \frac{-i \theta}{2} \left(\cos(\phi) \hat{X} + \sin(\phi) \hat{Y}\right) }\) |
|
Software 1-Qubit Gate |
\(R_{z}(\lambda)\) |
\(e^{-i \frac{\lambda}{2} \hat{Z}}\) |
|
Fully entangling 2-Qubit gate |
\(ZZ()\) |
\(e^{-i \frac{\pi}{4} \hat{Z} \bigotimes \hat{Z}}\) |
|
Parameterized angle 2-Qubit gate |
\(R_{ZZ}(\theta)\) |
\(e^{-i \frac{\theta}{2} \hat{Z} \bigotimes \hat{Z} }\) |
|
Use Case 1: Generate Integers and Floats¶
The code sample generates random bounded 32-bit integers between (\([0, 4)\)) using guppylang.std.qsystem.random.RNG.random_int_bounded() and floats bounded between \([0, 1)\) guppylang.std.qsystem.random.RNG.random_float(). The random integers correspond to the following physical operations with a native guppylang.std.qsystem.phased_x() gate:
Pauli-X,
Pauli-Y,
Pauli-Z,
A random rotation around the X-Y plane of the Bloch sphere.
The random float is used as the gate angle for the random X-Y rotation. The float is defined in half-turns, so the angle function is required for conversion to radians.
from guppylang import guppy
from guppylang.std.angles import angle
from guppylang.std.builtins import result
from guppylang.std.qsystem import (
rz,
phased_x,
measure
)
from guppylang.std.quantum import qubit
from guppylang.std.qsystem.random import RNG
@guppy
def main() -> None:
q = qubit()
seed = 0
rng = RNG(seed)
for _ in range(5):
op = rng.random_int_bounded(4)
if op == 0:
result("op", 0)
phased_x(q, angle(1), angle(0))
elif op == 1:
result("op", 1)
phased_x(q, angle(0), angle(1))
elif op == 2:
result("op", 2)
phased_x(q, angle(1), angle(1))
else:
result("op", 3)
a0 = rng.random_float()
a1 = rng.random_float()
phased_x(q, angle(a0), angle(a1))
r = measure(q)
result("measurement", r)
rng.discard()
return None
hugr = main.compile()
main.emulator(1).run()
EmulatorResult(results=[QsysShot(entries=[('op', 0), ('op', 2), ('op', 0), ('op', 2), ('op', 0), ('measurement', 1)])])
Use Case 2: Generate Random Angles¶
The RNG instance enables generation of random angles in the range \([-\pi, \pi)\). These angles are defined in radians and can be specified directly as gate angles.
from guppylang import guppy
from guppylang.std.angles import angle
from guppylang.std.builtins import result
from guppylang.std.qsystem import (
rz,
phased_x,
measure
)
from guppylang.std.quantum import qubit
from guppylang.std.qsystem.random import RNG
@guppy
def main() -> None:
q = qubit()
seed = 0
rng = RNG(seed)
for _ in range(5):
op = rng.random_int_bounded(2)
if not op:
a0 = rng.random_angle()
a1 = rng.random_angle()
phased_x(q, a0, a1)
result("a0", a0.halfturns)
result("a1", a1.halfturns)
elif op:
a = rng.random_clifford_angle()
result("a", a.halfturns)
rz(q, a)
r = measure(q)
result("measurement", r)
rng.discard()
return None
hugr = main.compile()
main.emulator(1).run()
EmulatorResult(results=[QsysShot(entries=[('a0', 0.510710634291172), ('a1', 0.16628000000491738), ('a', 0.5), ('a', 1.0), ('a0', -0.44963058549910784), ('a1', -0.9905513450503349), ('a0', 0.00798026891425252), ('a1', -0.36336523247882724), ('measurement', 0)])])
Use Case 3: RNG Shuffle¶
The RNG shuffle function randomly permutes an array of items. The code sample uses guppylang.std.qsystem.random.RNG.shuffle() to shuffle a qubit indices (array[int, N]) prior to 2-qubit gates on all \(\left[(N/2)\right]\).
from guppylang import guppy
from guppylang.std.builtins import result, array, comptime
from guppylang.std.qsystem.random import RNG
from guppylang.std.qsystem import zz_phase
from guppylang.std.quantum import measure_array
N = 10
@guppy
def layer(
q_array: array[qubit, comptime(N)],
index_array: array[int, comptime(N)],
nqubits: int,
rng: RNG
) -> None:
for i in range(nqubits-1):
j = i + 1
a = rng.random_clifford_angle()
result("a", a.halfturns)
zz_phase(q_array[index_array[i]], q_array[index_array[j]], a)
return None
@guppy
def main() -> None:
rng = RNG(0)
qubit_array: array[qubit, comptime(N)] = array(qubit() for _ in range(comptime(N)))
index_array: array[int, comptime(N)] = array(i for i in range(comptime(N)))
for _ in range(comptime(N)):
rng.shuffle(index_array)
result("qubits", index_array)
layer(qubit_array, index_array, comptime(N), rng)
r = measure_array(qubit_array)
result("measurement", r)
rng.discard()
return None
hugr = main.compile()
main.emulator(10).run().collated_shots()
[{'qubits': [[1, 6, 7, 8, 4, 3, 9, 5, 2, 0],
[1, 3, 2, 8, 4, 5, 9, 6, 0, 7],
[2, 1, 9, 0, 5, 3, 7, 8, 4, 6],
[5, 1, 3, 9, 7, 4, 2, 6, 0, 8],
[2, 9, 1, 5, 7, 4, 8, 6, 3, 0],
[0, 8, 3, 2, 6, 7, 1, 9, 4, 5],
[9, 0, 7, 2, 3, 5, 6, 8, 1, 4],
[6, 2, 5, 8, 1, 3, 7, 9, 4, 0],
[9, 1, 0, 2, 5, 3, 8, 7, 4, 6],
[3, 6, 8, 0, 7, 9, 1, 4, 5, 2]],
'a': [1.0,
0.5,
0.5,
1.5,
1.0,
0.5,
1.0,
1.5,
0.5,
0.0,
1.0,
0.5,
0.5,
1.5,
0.0,
1.0,
0.0,
0.0,
1.5,
1.0,
0.0,
0.5,
1.0,
0.0,
0.5,
0.5,
1.0,
0.5,
1.5,
0.5,
1.0,
1.5,
1.5,
0.5,
1.0,
0.0,
1.0,
0.0,
0.5,
1.5,
1.0,
1.5,
1.5,
0.5,
1.0,
1.0,
0.0,
1.0,
0.5,
1.0,
0.0,
1.0,
1.0,
0.0,
0.0,
1.5,
1.0,
1.0,
0.5,
1.5,
0.0,
0.0,
1.0,
1.5,
0.0,
1.0,
1.5,
1.5,
1.5,
1.0,
1.5,
1.0,
1.5,
0.0,
0.0,
1.5,
1.5,
0.0,
1.0,
0.5,
1.5,
1.0,
1.0,
1.5,
0.5,
0.0,
0.0,
0.5,
1.0,
1.0],
'measurement': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}]
Use Case 4: Discrete Distribution¶
The guppylang.std.qsystem.random.make_discrete_distribution() uniformly samples from a distribution of integers and returns an integer. This integer is used to access an array element by index prior to a phased_x operation.
from guppylang import guppy
from guppylang.std.builtins import array, result
from guppylang.std.quantum import qubit, measure
from guppylang.std.qsystem import phased_x
from guppylang.std.qsystem.random import make_discrete_distribution, RNG
@guppy
def main() -> None:
rng = RNG(1)
dist = make_discrete_distribution(array(0.0, 1.0, 2.0, 3.0))
rint = dist.sample(rng)
result("cumulative_dist", dist.sums)
result("rint", rint)
q_array = array(qubit() for _ in range(5))
phased_x(q_array[rint], rng.random_angle(), rng.random_angle())
result("result", measure(q_array[rint]))
rng.discard()
hugr = main.compile()
main.emulator(1).run()
EmulatorResult(results=[QsysShot(entries=[('cumulative_dist', [0.0, 0.16666666666666666, 0.5, 1.0]), ('rint', 2)])])