Getting Started with qnexus
¶
Quantinuum Nexus is a cloud-based platform that enables users to seamlessly run, review, and collaborate on quantum computing projects. The platform integrates support for various quantum targets using the TKET quantum programming tools to optimize circuit performance and translation between different targets. One such target is H-Series, powered by Honeywell. Each quantum target in nexus is called a BackendConfig
and can be configured to access hardware, emulator or simulator. Quantinuum Nexus offers different types of jobs that represent a component of your workflow that is running on Nexus-hosted emulators or H-series.
CompileJobs
represent the TKET compilation of circuits for a particular target device.ExecuteJobs
represent the execution of circuits on a quantum computer or simulator.Nexus stores
Job
inputs and outputs such asBackendResult
,BackendInfo
andCircuit
in addition to job metadata. These are searchable by a user friendly name.qnexus
uses references to access these resources. For access across multiple python sessions, these references should be saved to and loaded from local disk.
To access Nexus from an external environment, a python client must be installed with pip install qnexus
.
Authentication¶
External Environment¶
Quantinuum Nexus API usage requires you to have valid authentication tokens. You can obtain these by logging in with qnexus
. Authentication is performed silently in the Nexus Jupyterhub environment. However, in an external environment, qnexus
requires explicit authentication. There are two ways:
Authentication via Web Browser
Authentication via a Python prompt
The credentials one uses to authenticate access to qnexus are the same as the credentials to login to the Quantinuum Nexus website.
Authentication via a Web Browser
The code snippet below opens a windows in a web browser and requires the user to login to the nexus portal. A code is also displayed in both the python session and the web browser. The user is asked to verify the code is the same.
import qnexus as qnx
qnx.login()
Calling qnx.login
will first lead to a prompt being displayed as output. The prompt will contain an authentication code alongside instructions.
In the web browser, the following page opens displaying:
An authentication code. This needs to be compared with the authentication code displayed.
The option to link a new device to nexus.
Subsequently, the end-user must login to the nexus portal to grant the external environment access to nexus.
Once the user login is complete, a message will be displayed confirming successful authentication.
Authentication via a Python prompt
import qnexus as qnx
qnx.login_with_credentials()
Authentication Token Storage¶
Auth tokens will be stored on your system and should last 30 days before logging in again is required.
Within Nexus Jupyterhub¶
Within the JupyterHub environment within Nexus, user authentication is not required for qnexus
. The workflow defined for user using qnexus
in external environments does not need to be followed.
Project Setup¶
Within Quantinuum Nexus, all jobs are contained within projects. At the start of each Python session, a qnexus
user must authenticate using the workflow above and define an active project. The method qnx.projects.get_or_create
can be used to retrieve an existing project from the database, called Nexus-Workflow-Demonstration
. The method qnx.context.set_active_project
ensures this project is used within the context of this session by default.
Nexus additionally provides a way for you to create custom labels to organise your data within a project, these are called Properties
.
Learn more about Projects and properties in qnexus.
import qnexus as qnx
project = qnx.projects.get_or_create(name="Nexus-Workflow-Demonstration")
qnx.context.set_active_project(project)
Within the scope of a nexus project, the job name is unique. Trying to create a new job using an existing job name from the database will lead to an error. A suffix is defined with the python datetime
library.
import datetime
jobname_suffix = datetime.datetime.now().strftime("%Y_%m_%d-%H-%M-%S")
Backend Configuration¶
A BackendConfig
must also be specified to start compilation jobs and execution jobs. A project can contain jobs using multiple BackendConfig
specifications. To access H-Series devices and emulators, qnx.QuantinuumConfig
, needs to be instantiated. The code-cell defines QuantinuumConfig
to target a nexus-hosted emulator, H1-Emulator
.
Learn more about how to configure jobs to target specific devices in qnexus.
Learn more about checking the devices you have access to in qnexus.
config = qnx.QuantinuumConfig(device_name="H1-Emulator")
Uploading and Downloading Circuits¶
For the demonstration of the qnexus workflow, an existing circuit on the nexus database will be used. The method qnx.circuits.get
retrieves the reference of the circuit called GHZ-Circuit
. This is visualized using TKET. It is a requirement for a circuit to be uploaded into the nexus database before compilation and execution jobs can be used. This can be achieved with the method qnx.circuits.upload
.
from pytket.circuit import Circuit
circuit = Circuit(10)
circuit.H(0)
for i, j in zip(circuit.qubits[:-1], circuit.qubits[1:]):
circuit.CX(i, j)
circuit.measure_all();
from pytket.circuit.display import render_circuit_jupyter
render_circuit_jupyter(circuit)
ref = qnx.circuits.upload(circuit=circuit, name=f"GHZ-Circuit-{jobname_suffix}")
from pytket.circuit.display import render_circuit_jupyter
render_circuit_jupyter(ref.download_circuit())
Compilation Jobs¶
Quantinuum Nexus enables remote compilation of jobs for different backend configurations. A compilation job requires:
a database reference to the uncompiled
Circuit
,the specified
BackendConfig
,optimisation_level
a job
name
.
The output would be a reference to the compilation job. This reference can be used to query job status and to retrieve the job result when its ready. For compilation jobs, the job result would be a reference to the compiled circuit. The output reference can be used to download the compiled circuit into the local python session as a Circuit
instance.
ref_compile_job = qnx.start_compile_job(
circuits=[ref],
backend_config=config,
optimisation_level=2,
name=f"compilation-job-{jobname_suffix}"
)
The method qnx.jobs.wait_for
can be used to block any further operations whilst the job is running. The method also has a timeout. Upon timeout, an exception is raised. The subsequent method, qnx.jobs.results
won’t be called until the compilation job completes.
qnx.jobs.results
requires the compilation job reference as an input and outputs the reference for the compiled job result. The get_output
method returns the reference for the compiled circuit and download_circuit
returns an instance of the compiled circuit.
qnx.jobs.wait_for(ref_compile_job)
ref_compiled_circuit = qnx.jobs.results(ref_compile_job)[0].get_output()
compiled_circuit = ref_compiled_circuit.download_circuit()
from pytket.circuit.display import render_circuit_jupyter
render_circuit_jupyter(compiled_circuit)
Execution Jobs¶
Quantinuum nexus enables execution of quantum circuits across different quantum targets using the same interface. A prequisite step before an execution job can be started is a compilation job to ensure the circuit to be executed satisfies the gateset predicate. An execution job requires:
A database reference to the compiled circuit. The references must be passed as a list.
A list of integers for each circuit to be submitted for execution.
A BackendConfig to specify which quantum target to use for execution
A name to assign on the execution job for job management purposes. This name is unique within the scope of the active project.
ref_execute_job = qnx.start_execute_job(
circuits=[ref_compiled_circuit],
n_shots=[100],
backend_config=config,
name=f"execution-job-{jobname_suffix}"
)
Similar to the compile job workflow, the methods qnx.jobs.wait_for
and qnx.jobs.results
are used together to retrieve a reference to the job result. As an alternative, qnx.jobs.status
is used to query the status of the execution job.
qnx.jobs.status(ref_execute_job)
The user can call download_result
directly on the result reference. For execution jobs, get_output
does not need to be called. The method, download_result
, will download the result data into a local instance of BackendResult
.
qnx.jobs.wait_for(ref_execute_job)
ref_result = qnx.jobs.results(ref_execute_job)[0]
backend_result = ref_result.download_result()
backend_result.get_distribution()
Cancel job¶
ref_execute_job1 = qnx.start_execute_job(
circuits=[ref_compiled_circuit],
n_shots=[100],
backend_config=qnx.QuantinuumConfig(device_name="H1-1E"),
name=f"execution-job-to-cancel-{jobname_suffix}"
)
qnx.jobs.status(ref_execute_job1)
config = qnx.QuantinuumConfig(device_name="H1-Emulator", attempt_batching=True)
Local Reference Storage¶
Nexus stores all circuits, results and jobs on the nexus database. Each type of resource is searchable by a user-friendly name. With the qnexus
client, an end-user can request these resources across different sessions with references. To this end, these references must be saved and loaded from local disk using the qnx.filesystem
module.
Learn more about querying your data/jobs in qnexus.
Learn more about saving and loading Ref objects in qnexus.
from pathlib import Path
qnx.filesystem.save(
path=Path.cwd() / "my_job_folder" / ref_execute_job.annotations.name,
ref=ref_execute_job,
mkdir=True
)
ref_execute_job_2 = qnx.filesystem.load(path=Path.cwd() / "my_job_folder" / ref_execute_job.annotations.name)
qnx.jobs.status(ref_execute_job_2)
If we’d like to share our work, we can grant access to team members using collaboration and role-based access control features.
Learn more about Access and Collaboration features in qnexus.
To put all of this together in a real-world example, please see our knowledge articles.