Submitting quantum hardware experiments with Tangelo and QEMIST Cloud

This short tutorial shows how users can achieve that by simply installing both the Tangelo and QEMIST Cloud’s client library, and access all the backends available in Amazon Braket.

Tangelo users can express quantum circuits in various formats, which can be then submitted to one of the quantum cloud services providers using their account credentials and the API.

This short tutorial shows how users can access all the backends available in Amazon Braket by simply installing both the Tangelo and QEMIST Cloud’s client library, then running quantum hardware experiments using their QEMIST Cloud account credentials and credits.

Installation & environment

In order to succesfully submit an experiment, you will first need to:

  • install Tangelo
  • install qemist-client (QEMIST client library)

Once you have installed the two required packages, set up both environment variables QEMIST_PROJECT_ID and QEMIST_AUTH_TOKEN in your local environment. You may set these variables in your OS / terminal, or provide them in your script using the os module, as below:

import os

os.environ['QEMIST_PROJECT_ID'] = "your_project_id_string"
os.environ['QEMIST_AUTH_TOKEN'] = "your_qemist_authentication_token"

If these two variables are not properly set, you will get errors later on in some import statements related to qemist-client.

Assuming you have access to the QEMIST Cloud dashboard: - You can get a project ID by navigating to Projects using the left-side bar, and selecting a project. - Your API token can be found by clicking the top-right dropdown menu and then API token

A simple example

First, we define a quantum circuit using the API provided by Tangelo. For the purpose of this tutorial, we here explicitly define a short circuit that simply prepares a Bell state. However, this feature works with any quantum circuit expressed in the Tangelo format, including those you may have obtained from a complicated custom workflow expressed with Tangelo.

from tangelo.linq import Gate, Circuit

circuit = Circuit([Gate("H", 0), Gate("CNOT", 1, control=0)])
print(circuit)
Circuit object. Size 2 

H         target : 0   
CNOT      target : 1   control : 0   

In order to submit a quantum job and run this circuit on one of the available devices, we import a few convenience functions from the Tangelo, which rely on the QEMIST client library. We illustrate their usage in the rest of the notebook.

from tangelo.linq.qpu_connection import QEMISTCloudConnection
qcloud_connection = QEMISTCloudConnection()

Job price estimate

Before running one or several quantum experiments, it may be useful to have an estimate of the cost. We provide a convenience function job_estimate, which returns a dictionary featuring the price associated with the different backends available, in QEMIST Cloud credits (currently 1 QEMIST credit = 1 USD). These prices and backends are the ones available to us, using our own subscription to Amazon Braket and other cloud services: we make them available to you through this API.

Some pricing formulas may take into account the specifics of your circuit, while others do not. Simulator backends sometimes charge per-the-minute and are therefore difficult to estimate: we currently choose to only return estimates for actual quantum devices.

Do not hesitate to look directly into the official pricing provided by cloud providers (ex: https://aws.amazon.com/braket/pricing/) for the backends that we do not list or for which the estimate can be difficult to make.

price_estimates = qcloud_connection.job_estimate(circuit, n_shots=1000)
print(price_estimates)
{'braket_ionq': 10.3, 'braket_rigetti': 0.65}

Job submission

The job_submit function can be used to submit a quantum job on the target backends available in Amazon Braket (through their device arn), and the number of shots required. Both parameters are required. It returns a ID, used as a handle to your quantum job. This call is asynchronous / non-blocking, which means that it returns without waiting for the job to complete, allowing you to continue with your script in the meantime.

Feel free to uncomment any of the lines below, if you wish to run the following commands on the backend of your choice. Note that the quantum devices are not guaranteed to be immediately available and that you will require a subscription to QEMIST Cloud.

backend = 'arn:aws:braket:::device/quantum-simulator/amazon/sv1'
# backend = 'arn:aws:braket:::device/qpu/ionq/ionQdevice'
# backend = 'arn:aws:braket:::device/qpu/rigetti/Aspen-9'
# backend = 'arn:aws:braket:::device/quantum-simulator/amazon/dm1'
job_id = qcloud_connection.job_submit(circuit, n_shots=100, backend=backend)
print(f"Job submitted with job id :: {job_id}")
{'shots': 100, 'backend': 'arn:aws:braket:::device/quantum-simulator/amazon/sv1'}
Job submitted with job id :: 60301948057975242

Job status

Once your job has been submitted, you may query his status through the job_status. It may take time for this job to start, as your request has been queued with the desired cloud services: it could be instant or take hours for the job to start.

The return values are: ready, in_progress, complete, cancelled.

print(qcloud_connection.job_status(job_id))
ready

Job cancel

A job that has been submitted and has not yet started may be cancelled through the job_cancel command, which takes the job ID as parameter. You are free to uncomment this line and try, if you’d like.

# print(qcloud_connection.job_cancel(job_id))

Job result

Finally, you can make a blocking call to retrieve your results using job_result. This function returns the histogram of frequencies associated with your quantum circuit, as well as the raw data provided by the quantum services provider, as nested dictionaries.

The histogram of frequencies is returned in a standard format, where the bitstrings read left-to-right: that is, the string 01 refers to the basis state that measured qubit 0 (resp. 1) in state \(|0\rangle\) (resp. state \(|1\rangle\)).

Feel free to investigate and parse the output as you see fit: while the histogram of frequencies ensures you get the essentials in a standard format, the raw data blob is something you can save or dig into to find more about what actually happened on the device, now or later.

freqs, raw_data = qcloud_connection.job_results(job_id)
print(f"Frequencies :: {freqs}")
Frequencies :: {'00': 0.53, '11': 0.47}
print(raw_data)
{'fragment_type': 'QuantumCircuit', 'problem_handle': 60301948057975242, 'result': {'results': {'measurements': [[0, 0], [1, 1], [0, 0], [0, 0], [0, 0], [0, 0], [1, 1], [1, 1], [0, 0], [0, 0], [1, 1], [0, 0], [1, 1], [1, 1], [1, 1], [1, 1], [0, 0], [1, 1], [0, 0], [0, 0], [0, 0], [1, 1], [1, 1], [1, 1], [0, 0], [0, 0], [1, 1], [0, 0], [1, 1], [1, 1], [1, 1], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [1, 1], [0, 0], [1, 1], [0, 0], [0, 0], [0, 0], [1, 1], [1, 1], [1, 1], [0, 0], [1, 1], [1, 1], [0, 0], [0, 0], [1, 1], [0, 0], [0, 0], [0, 0], [1, 1], [1, 1], [0, 0], [0, 0], [1, 1], [1, 1], [0, 0], [1, 1], [1, 1], [1, 1], [0, 0], [0, 0], [0, 0], [1, 1], [0, 0], [1, 1], [1, 1], [0, 0], [1, 1], [0, 0], [0, 0], [1, 1], [1, 1], [0, 0], [0, 0], [0, 0], [0, 0], [1, 1], [1, 1], [1, 1], [0, 0], [0, 0], [1, 1], [1, 1], [0, 0], [0, 0], [0, 0], [0, 0], [1, 1], [0, 0], [1, 1], [1, 1], [0, 0], [0, 0], [1, 1], [1, 1]], 'measured_qubits': [0, 1], 'measurement_counts': {'00': 53, '11': 47}, 'measurement_probabilities': {'00': 0.53, '11': 0.47}}, 'metadata': {'action': {'results': [], 'instructions': [{'type': 'h', 'target': 0}, {'type': 'cnot', 'target': 1, 'control': 0}], 'basis_rotation_instructions': []}, 'rigettiMetadata': None}}, 'solve_time': 777, 'status': 'complete'}

Closing words

Managing multiple quantum cloud service subscriptions and juggling all those login credentials can be cumbersome and tedious. This notebook briefly demonstrates how users with a subscription to QEMIST Cloud can benefit from using our wrappers and submit hardware experiments to a broad range of backends with only one set of credentials, using our subscription.