Unpack the Stack - Intro to Quantinuum’s Next-Generation Stack¶
Date: 2025/09/18
Presenter: Seyon Sivarajah
YouTube Link: https://www.youtube.com/watch?v=TkFGY5tHaQI
Quantinuum launched a new software stack for the next-generation Helios and future generation systems, including Guppy, a new, open-source programming language, and Selene, which can emulate a subset of Helios. During this technical webinar from September 18, 2025, Seyon Sivarajah, Principal Manager of Software Engineering, provides a deep dive into the features and benefits of the new stack, and how it enables a lower barrier to entry, faster time-to-solution, industry-standard access, and the best possible user experience.
Installation¶
%pip install qnexus guppylang
Notebook¶
from datetime import datetime
from guppylang.decorator import guppy
from guppylang.std.quantum import h, cx, measure, discard_array, qubit
from guppylang.std.builtins import array, result, py
from guppylang.emulator.result import EmulatorResult
QEC: flagged Steane state preparation¶
We will implement the following circuit in Guppy:
"""Steane code implementation in Guppy.
Based on https://arxiv.org/abs/2107.07505.
"""
SEED = 42
N_SHOTS = 1000
N_QUBITS = 8
@guppy.struct
class SteaneQ:
data_qs: array[qubit, 7] # type: ignore
@guppy
def non_ft_zero() -> SteaneQ:
"""Non-fault-tolerant preparation of |0>_L in the Steane code."""
data_qubits = array(qubit() for _ in range(7)) # type: ignore
plus_ids = array(0, 4, 6)
for i in plus_ids:
h(data_qubits[i])
cx_pairs = array((0, 1), (4, 5), (6, 3), (6, 5), (4, 2), (0, 3), (4, 1), (3, 2))
for c, t in cx_pairs:
cx(data_qubits[c], data_qubits[t])
return SteaneQ(data_qubits)
@guppy
def flagged_ft_zero() -> tuple[SteaneQ, bool]:
"""Fault-tolerant preparation of |0>_L in the Steane code,
with flag qubits to detect errors.
Returns the prepared logical qubit and a flag indicating if an error was detected.
"""
q = non_ft_zero()
ancilla = qubit() # type: ignore
flags = array(1, 3, 5)
for f in flags:
cx(q.data_qs[f], ancilla)
return q, measure(ancilla)
MAX_ATTEMPTS = 1000
@guppy
def count_attempts() -> None:
"""Attempt fault-tolerant zero preparation multiple times,
until success or the maximum number of attempts is reached.
"""
for attempt in range(py(MAX_ATTEMPTS)):
q, failed = flagged_ft_zero()
discard_array(q.data_qs)
if not failed:
result("attempt", attempt)
break
count_attempts.check()
def mean_attempts(res: EmulatorResult) -> None:
"""Utility function to compute and print the mean number of attempts"""
mean = sum(shot.as_dict()["attempt"] + 1 for shot in res)/len(res) # type: ignore
print(f"Mean attempts: {mean}")
Create a new Nexus project¶
import qnexus as qnx
from quantinuum_schemas.models.emulator_config import QSystemErrorModel
my_project_ref = qnx.projects.get_or_create(name="Selene-Nexus")
qnx.context.set_active_project(my_project_ref)
Configure the Nexus emulator and submit job¶
Run our experiment remotely on Nexus, emulating the real quantum hardware.
# Upload our program to Nexus
program_ref = qnx.hugr.upload(
hugr_package=count_attempts.compile(),
name="steane_code_attempts",
)
# Define our Selene configuration
config = qnx.models.StandardEmulatorConfig(
n_qubits=N_QUBITS,
runtime=qnx.models.HeliosRuntime(seed=SEED),
# DEMO QSystemErrorModel - not representative of actual hardware at launch
error_model=QSystemErrorModel(seed=SEED),
simulator=qnx.models.StabilizerSimulator(seed=SEED)
)
# Start the job on Nexus
selene_quest_job_ref = qnx.start_execute_job(
programs=[program_ref],
n_shots=[N_SHOTS],
backend_config=config,
name=f"steane_code_attempts {datetime.now()}",
)
Run emulation locally¶
First run with no errors, state preparation should succeed in 1 attempt on average.
emulator = (
count_attempts
.emulator(n_qubits=N_QUBITS)
.stabilizer_sim()
.with_seed(SEED)
.with_shots(N_SHOTS)
.with_progress_bar()
)
mean_attempts(emulator.run())
Let’s emulate with a simple depolarizing noise model, now we should detect some failures and the mean attempts should be higher.
from selene_sim.backends.bundled_error_models import DepolarizingErrorModel
error_model = DepolarizingErrorModel(
# single qubit gate error rate
p_1q=1e-3,
# two qubit gate error rate
p_2q=2e-3,
)
mean_attempts(emulator.with_error_model(error_model).run())
Retrieve results from Nexus¶
Since we are emulating the device we can expect some retries.
qnx.jobs.wait_for(selene_quest_job_ref)
nexus_result = qnx.jobs.results(selene_quest_job_ref)[0].download_result()
mean_attempts(nexus_result) # type: ignore