{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Jobs & Results\n", "\n", "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.\n", "\n", "- CompileJobs represent the TKET compilation of circuits for a particular target device.\n", "- ExecuteJobs represent the execution of circuits on a quantum computer or simulator.\n", "\n", "Nexus manages the storage of Job outputs such as BackendResults, BackendInfo or compiled Circuits." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from datetime import datetime\n", "\n", "from pytket import Circuit\n", "\n", "import qnexus as qnx" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_job_name_prefix = datetime.now()\n", "\n", "my_project_ref = qnx.projects.get_or_create(name=\"My Project\")\n", "\n", "my_circuit_ref = qnx.circuits.upload(\n", " name=f\"My Circuit from {datetime.now()}\",\n", " circuit=Circuit(2).H(0).CX(0, 1).measure_all(),\n", " project=my_project_ref,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Compile Jobs\n", "\n", "These jobs represent the TKET compilation of one or more circuit(s) in Nexus for a particular target backend.\n", "\n", "BackendConfigs define the target, in this case we are targetting the `H1-1LE` noiseless simulator." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Run in an asyncronous manner to receieve a JobRef\n", "\n", "compile_job_ref = qnx.start_compile_job(\n", " programs=[my_circuit_ref],\n", " name=f\"{my_job_name_prefix}_compile_async\",\n", " optimisation_level=1,\n", " backend_config=qnx.QuantinuumConfig(device_name=\"H1-1LE\"),\n", " project=my_project_ref,\n", " skip_intermediate_circuits=False, # Store compiled circuits\n", ")\n", "\n", "# Block until the job is complete (or perform other tasks while we wait)\n", "qnx.jobs.wait_for(compile_job_ref)\n", "\n", "compiled_circuits = [item.get_output() for item in qnx.jobs.results(compile_job_ref)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Checking details of the compilation\n", "\n", "If desired, the input, output and compilation passes can be checked." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Retrieve a CompilationResultRef for every Circuit that was compiled\n", "compile_job_result_refs = qnx.jobs.results(compile_job_ref)\n", "\n", "compile_job_result_refs.df()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Retrieve the input CircuitRef for the Circuit\n", "compile_job_result_refs[0].get_input()\n", "\n", "\n", "# Retrieve the compiled CircuitRef for the Circuit\n", "compile_job_result_refs[0].get_output()\n", "\n", "\n", "# View the compilation passes that we applied when compiling the Circuit\n", "compile_job_result_refs[0].get_passes().df()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Execute Jobs\n", "\n", "These jobs represent the execution of one or more circuit(s) on a quantum computer or simulator." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Run in an asyncronous manner to receieve a JobRef\n", "\n", "execute_job_ref = qnx.start_execute_job(\n", " programs=compiled_circuits,\n", " name=f\"{my_job_name_prefix}_execute_async\",\n", " n_shots=[100] * len(compiled_circuits),\n", " backend_config=qnx.QuantinuumConfig(device_name=\"H1-1LE\"),\n", " project=my_project_ref,\n", ")\n", "\n", "# Block until the job is complete (or perform other tasks while we wait)\n", "qnx.jobs.wait_for(execute_job_ref)\n", "\n", "# Retrieve a ExecutionResultRef for every Circuit that was run\n", "execute_job_result_refs = qnx.jobs.results(execute_job_ref)\n", "\n", "execute_job_result_refs.df()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Get the input CircuitRef\n", "execute_job_result_refs[0].get_input()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Get the results of the execution\n", "result = execute_job_result_refs[0].download_result()\n", "\n", "result.get_counts()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Get the pytket BackendInfo to see the state of the device\n", "execute_job_result_refs[0].download_backend_info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Getting results for unfinished or incomplete jobs\n", "\n", "By default, you can only retrieve results for jobs with the COMPLETED status (meaning that all items in the Job have successfully completed). \n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Fetch the completed results for an otherwise incomplete job\n", "execute_job_result_refs = qnx.jobs.results(job=execute_job_ref, allow_incomplete=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Managing Jobs\n", "\n", "You can use the API to check on jobs, but also perform operations like cancelling or retrying." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# View your current jobs that are in the SUBMITTED state\n", "qnx.jobs.get_all(job_status=[qnx.jobs.JobStatusEnum.SUBMITTED]).df()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "other_execute_job_ref = qnx.start_execute_job(\n", " programs=compiled_circuits,\n", " name=f\"{my_job_name_prefix}_execute_other\",\n", " n_shots=[100] * len(compiled_circuits),\n", " backend_config=qnx.QuantinuumConfig(device_name=\"H1-1LE\"),\n", " project=my_project_ref,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Cancel the job\n", "qnx.jobs.cancel(other_execute_job_ref)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Retry the job\n", "qnx.jobs.retry_submission(\n", " other_execute_job_ref,\n", " retry_status=[qnx.jobs.StatusEnum.CANCELLED],\n", " remote_retry_strategy=qnx.jobs.RemoteRetryStrategy.FULL_RESTART,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Delete job\n", "\n", "Deleting a job will delete all job items, results and backend snapshots. Circuits are not deleted." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "qnx.jobs.delete(other_execute_job_ref)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Convenience methods\n", "\n", "For small jobs that you are confident will complete quickly, we offer convenienve methods to run jobs in a less verbose manner." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Compile a circuit (blocking), to receive a list of compiled CircuitRefs\n", "\n", "compiled_circuits = qnx.compile(\n", " programs=[my_circuit_ref],\n", " name=f\"{my_job_name_prefix}_compile\",\n", " optimisation_level=1,\n", " backend_config=qnx.QuantinuumConfig(device_name=\"H1-1LE\"),\n", " project=my_project_ref,\n", " timeout=500, # wait for the job for 500 seconds before timing out\n", ")\n", "\n", "compiled_circuits.df()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Execute the circuit (blocking), to receive a list of pytket BackendResults\n", "\n", "results = qnx.execute(\n", " programs=compiled_circuits,\n", " name=f\"{my_job_name_prefix}_execute\",\n", " n_shots=[100] * len(compiled_circuits),\n", " backend_config=qnx.QuantinuumConfig(device_name=\"H1-1LE\"),\n", " project=my_project_ref,\n", " timeout=500, # wait for the job for 500 seconds before timing out\n", ")\n", "\n", "results[0].get_counts()" ] } ], "metadata": { "kernelspec": { "display_name": "qnexus-Rou6F43i-py3.11", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.8" } }, "nbformat": 4, "nbformat_minor": 2 }