{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Getting Started with `qnexus`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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 the Quantinuum machine, H2-1. 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 or Quantinuum-hosted emulators.\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", "* Nexus stores `Job` inputs and outputs such as `BackendResult`, `BackendInfo` and `Circuit` 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.\n", "\n", "To access Nexus from an external environment, a python client must be installed with `pip install qnexus`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Authentication" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### External Environment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:\n", "1. Authentication via Web Browser\n", "1. Authentication via a Python prompt\n", "\n", "The credentials one uses to authenticate access to qnexus are the same as the credentials to login to the [Quantinuum Nexus website](https://nexus.quantinuum.com)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Authentication via a Web Browser**\n", "\n", "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.\n", "\n", "```\n", "import qnexus as qnx\n", "qnx.login()\n", "```\n", "\n", "Calling `qnx.login` will first lead to a prompt being displayed as output. The prompt will contain an authentication code alongside instructions. \n", "\n", "```{image} figures/nexus_workflow_1.png\n", ":alt: Nexus Login URL - figures/nexus_workflow_1.png\n", ":width: 800\n", ":align: center\n", "```\n", "\n", "In the web browser, the following page opens displaying: \n", "\n", "1. An authentication code. This needs to be compared with the authentication code displayed.\n", "2. The option to link a new device to nexus.\n", "\n", "```{image} figures/nexus_workflow_2.png\n", ":alt: Nexus Login Browser Window - figures/nexus_workflow_2.png\n", ":width: 800\n", ":align: center\n", "```\n", "\n", "Subsequently, the end-user must login to the nexus portal to grant the external environment access to nexus.\n", "\n", "```{image} figures/nexus_workflow_3.png\n", ":alt: Nexus Login Credential Window - figures/nexus_workflow_3.png\n", ":width: 800\n", ":align: center\n", "```\n", "\n", "Once the user login is complete, a message will be displayed confirming successful authentication.\n", "\n", "```{image} figures/nexus_workflow_4.png\n", ":alt: Nexus Browser Login Success - figures/nexus_workflow_4.png\n", ":width: 800\n", ":align: center\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Authentication via a Python prompt**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "import qnexus as qnx\n", "qnx.login_with_credentials()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Authentication Token Storage\n", "\n", "Auth tokens will be stored on your system and should last 30 days before logging in again is required." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Within Nexus Jupyterhub" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Project Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "Nexus additionally provides a way for you to create custom labels to organise your data within a project, these are called `Properties`.\n", "\n", "[Learn more about Projects and properties in qnexus.](projects_properties_context.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import qnexus as qnx\n", "\n", "project = qnx.projects.get_or_create(name=\"Nexus-Workflow-Demonstration\")\n", "qnx.context.set_active_project(project)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import datetime\n", "\n", "jobname_suffix = datetime.datetime.now().strftime(\"%Y_%m_%d-%H-%M-%S\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Backend Configuration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `BackendConfig` must also be specified to start compilation jobs and execution jobs. A project can contain jobs using multiple `BackendConfig` specifications. To access Quantinuum hardware and emulators, `qnx.QuantinuumConfig`, needs to be instantiated. The code-cell defines `QuantinuumConfig` to target a nexus-hosted emulator, `H1-Emulator`.\n", "\n", "[Learn more about how to configure jobs to target specific devices in qnexus.](backend_configuration.ipynb)\n", "\n", "[Learn more about checking the devices you have access to in qnexus.](devices_credentials.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "config = qnx.QuantinuumConfig(device_name=\"H1-Emulator\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Uploading and Downloading Circuits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pytket.circuit import Circuit\n", "\n", "circuit = Circuit(10)\n", "circuit.H(0)\n", "for i, j in zip(circuit.qubits[:-1], circuit.qubits[1:]):\n", " circuit.CX(i, j)\n", "circuit.measure_all();" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pytket.circuit.display import render_circuit_jupyter\n", "\n", "render_circuit_jupyter(circuit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ref = qnx.circuits.upload(circuit=circuit, name=f\"GHZ-Circuit-{jobname_suffix}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pytket.circuit.display import render_circuit_jupyter\n", "\n", "render_circuit_jupyter(ref.download_circuit())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Compilation Jobs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quantinuum Nexus enables remote compilation of jobs for different backend configurations. A compilation job requires: \n", "* a database reference to the uncompiled `Circuit`, \n", "* the specified `BackendConfig`, \n", "* `optimisation_level`\n", "* a job `name`. \n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ref_compile_job = qnx.start_compile_job(\n", " circuits=[ref],\n", " backend_config=config,\n", " optimisation_level=2,\n", " name=f\"compilation-job-{jobname_suffix}\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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. \n", "\n", "`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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qnx.jobs.wait_for(ref_compile_job)\n", "ref_compiled_circuit = qnx.jobs.results(ref_compile_job)[0].get_output()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "compiled_circuit = ref_compiled_circuit.download_circuit()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pytket.circuit.display import render_circuit_jupyter\n", "\n", "render_circuit_jupyter(compiled_circuit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Execution Jobs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:\n", "\n", "* A database reference to the compiled circuit. The references must be passed as a list.\n", "* A list of integers for each circuit to be submitted for execution.\n", "* A BackendConfig to specify which quantum target to use for execution\n", "* A name to assign on the execution job for job management purposes. This name is unique within the scope of the active project." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ref_execute_job = qnx.start_execute_job(\n", " circuits=[ref_compiled_circuit],\n", " n_shots=[100],\n", " backend_config=config,\n", " name=f\"execution-job-{jobname_suffix}\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qnx.jobs.status(ref_execute_job)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qnx.jobs.wait_for(ref_execute_job)\n", "ref_result = qnx.jobs.results(ref_execute_job)[0]\n", "backend_result = ref_result.download_result()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backend_result.get_distribution()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Learn more about Jobs in qnexus.](jobs_results.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cancel job\n", "\n", "Jobs submitted to quantum backends can be requested to be cancelled by Nexus, however please note that depending on the status of the job cancellation cannot be guaranteed. Please check the job status once the cancellation request has been made to make sure." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Lets start a job (please note this example assumes you have access to the H1-1E device)\n", "ref_execute_job1 = qnx.start_execute_job(\n", " circuits=[ref_compiled_circuit],\n", " n_shots=[100],\n", " backend_config=qnx.QuantinuumConfig(device_name=\"H1-1E\"),\n", " name=f\"execution-job-to-cancel-{jobname_suffix}\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qnx.jobs.status(ref_execute_job1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qnx.jobs.cancel(ref_execute_job1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Double check the job is cancelled (this might take some time).\n", "qnx.jobs.status(ref_execute_job1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Local Reference Storage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "[Learn more about querying your data/jobs in qnexus.](refs_nexus_iterator.ipynb)\n", "\n", "[Learn more about saving and loading Ref objects in qnexus.](saving_refs.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "qnx.filesystem.save(\n", " path=Path.cwd() / \"my_job_folder\" / ref_execute_job.annotations.name,\n", " ref=ref_execute_job,\n", " mkdir=True\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ref_execute_job_2 = qnx.filesystem.load(path=Path.cwd() / \"my_job_folder\" / ref_execute_job.annotations.name)\n", "\n", "qnx.jobs.status(ref_execute_job_2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we'd like to share our work, we can grant access to team members using collaboration and role-based access control features.\n", "\n", "[Learn more about Access and Collaboration features in qnexus.](teams_roles.ipynb)\n", "\n", "To put all of this together in a real-world example, please see our [knowledge articles](https://docs.quantinuum.com/nexus/trainings/knowledge_articles)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.10.14" } }, "nbformat": 4, "nbformat_minor": 2 }