Jobs & Results¶
Quantinuum Nexus offers different types of Job
that represent a component of your workflow that is running in Nexus, Quantinuum Systems or a third-party.
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 manages the storage of Job outputs such as BackendResults, BackendInfo or compiled Circuits.
from datetime import datetime
from pytket import Circuit
import qnexus as qnx
my_job_name_prefix = datetime.now()
my_project_ref = qnx.projects.get_or_create(name="My Project")
my_circuit_ref = qnx.circuits.upload(
name=f"My Circuit from {datetime.now()}",
circuit=Circuit(2).H(0).CX(0, 1).measure_all(),
project=my_project_ref,
)
Compile Jobs¶
These jobs represent the TKET compilation of one or more circuit(s) in Nexus for a particular target backend.
BackendConfigs define the target, in this case we are targetting the H1-1LE
noiseless simulator.
# Run in an asyncronous manner to receieve a JobRef
compile_job_ref = qnx.start_compile_job(
programs=[my_circuit_ref],
name=f"{my_job_name_prefix}_compile_async",
optimisation_level=1,
backend_config=qnx.QuantinuumConfig(device_name="H1-1LE"),
project=my_project_ref,
skip_intermediate_circuits=False, # Store compiled circuits
)
# Block until the job is complete (or perform other tasks while we wait)
qnx.jobs.wait_for(compile_job_ref)
compiled_circuits = [item.get_output() for item in qnx.jobs.results(compile_job_ref)]
Note: By default, compile jobs do not store intermediate results. If you want to store intermediate circuits and compilation passes, you can set the skip_intermediate_circuits
parameter to False
when creating the job. This will store all intermediate results in Nexus, which can be useful for debugging or analysis purposes.
Checking details of the compilation¶
If desired, the input, output and compilation passes can be checked.
# Retrieve a CompilationResultRef for every Circuit that was compiled
compile_job_result_refs = qnx.jobs.results(compile_job_ref)
compile_job_result_refs.df()
# Retrieve the input CircuitRef for the Circuit
compile_job_result_refs[0].get_input()
# Retrieve the compiled CircuitRef for the Circuit
compile_job_result_refs[0].get_output()
# View the compilation passes that we applied when compiling the Circuit
compile_job_result_refs[0].get_passes().df()
Execute Jobs¶
These jobs represent the execution of one or more circuit(s) on a quantum computer or simulator.
# Run in an asyncronous manner to receieve a JobRef
execute_job_ref = qnx.start_execute_job(
programs=compiled_circuits,
name=f"{my_job_name_prefix}_execute_async",
n_shots=[100] * len(compiled_circuits),
backend_config=qnx.QuantinuumConfig(device_name="H1-1LE"),
project=my_project_ref,
)
# Block until the job is complete (or perform other tasks while we wait)
qnx.jobs.wait_for(execute_job_ref)
# Retrieve a ExecutionResultRef for every Circuit that was run
execute_job_result_refs = qnx.jobs.results(execute_job_ref)
execute_job_result_refs.df()
# Get the input CircuitRef
execute_job_result_refs[0].get_input()
# Get the results of the execution
result = execute_job_result_refs[0].download_result()
result.get_counts()
# Get the pytket BackendInfo to see the state of the device
execute_job_result_refs[0].download_backend_info()
Getting results for unfinished or incomplete jobs¶
By default, you can only retrieve results for jobs with the COMPLETED status (meaning that all items in the Job have successfully completed).
In some contexts you may want to retrieve results for the completed items in an otherwise pending or errored job. For example, maybe you have submitted 10 circuits to be executed on quantum hardware, but only 6 of them have completed before you ran out of credit quota for that device. In this case you can still get the results from the completed items.
# Fetch the completed results for an otherwise incomplete job
execute_job_result_refs = qnx.jobs.results(job=execute_job_ref, allow_incomplete=True)
Managing Jobs¶
You can use the API to check on jobs, but also perform operations like cancelling or retrying.
# View your current jobs that are in the SUBMITTED state
qnx.jobs.get_all(job_status=[qnx.jobs.JobStatusEnum.SUBMITTED]).df()
other_execute_job_ref = qnx.start_execute_job(
programs=compiled_circuits,
name=f"{my_job_name_prefix}_execute_other",
n_shots=[100] * len(compiled_circuits),
backend_config=qnx.QuantinuumConfig(device_name="H1-1LE"),
project=my_project_ref,
)
# Cancel the job
qnx.jobs.cancel(other_execute_job_ref)
# Retry the job
qnx.jobs.retry_submission(
other_execute_job_ref,
retry_status=[qnx.jobs.StatusEnum.CANCELLED],
remote_retry_strategy=qnx.jobs.RemoteRetryStrategy.FULL_RESTART,
)
Delete job¶
Deleting a job will delete all job items, results and backend snapshots. Circuits are not deleted.
qnx.jobs.delete(other_execute_job_ref)
Convenience methods¶
For small jobs that you are confident will complete quickly, we offer convenienve methods to run jobs in a less verbose manner.
# Compile a circuit (blocking), to receive a list of compiled CircuitRefs
compiled_circuits = qnx.compile(
programs=[my_circuit_ref],
name=f"{my_job_name_prefix}_compile",
optimisation_level=1,
backend_config=qnx.QuantinuumConfig(device_name="H1-1LE"),
project=my_project_ref,
timeout=500, # wait for the job for 500 seconds before timing out
)
compiled_circuits.df()
# Execute the circuit (blocking), to receive a list of pytket BackendResults
results = qnx.execute(
programs=compiled_circuits,
name=f"{my_job_name_prefix}_execute",
n_shots=[100] * len(compiled_circuits),
backend_config=qnx.QuantinuumConfig(device_name="H1-1LE"),
project=my_project_ref,
timeout=500, # wait for the job for 500 seconds before timing out
)
results[0].get_counts()