Executing Custom Quantum Programs
This tutorial guides you through using LunaQ to execute custom quantum programs on quantum hardware. It's designed for individuals with unique quantum algorithms who are looking for a one-stop-shop to access various quantum hardware vendors in a unified way. This step-by-step guide will show you how to deploy these algorithms to quantum hardware providers like IBM Quantum.
Before we begin, ensure you have installed the Luna Python SDK along with these additional dependencies:
pip install qiskit matplotlib
Upon completing this tutorial, you will be able to:
- Learn to prepare your quantum circuits.
- Understand how to execute your algorithm on quantum hardware via LunaQ, specifically with IBM Quantum and Q-CTRL's FireOpal.
- Discover how to retrieve and analyze the results from your quantum computation.
1. Quantum Circuit Preparation
To begin, your quantum algorithm must be correctly formatted. Using LunaQ, you can express your algorithm in OpenQASM 2.0, a powerful quantum assembly language for specifying quantum circuits.
We're going to explore this through an example using Qiskit to implement the Bernstein-Vazirani algorithm. This algorithm is particularly interesting because it showcases the power of quantum computing over classical approaches in specific problem-solving scenarios. The problem it addresses involves identifying a hidden binary string that's embedded within a quantum oracle, allowing to solve certain types of problems where the solution involves discerning a specific piece of hidden information based on limited input. In our example, we'll focus on uncovering the secret string '10101'.
This circuit sets up a series of qubits in a superposition, applies a series of controlled-NOT gates based on the '10101' secret string, and then uses Hadamard gates to interfere the qubits. The final state of the qubits, when measured, collapses to the secret string, demonstrating a quantum algorithm's ability to solve problems in fewer steps than classical counterparts.
from qiskit import QuantumCircuit
# Define a quantum circuit that represents the Bernstein-Vazirani algorithm
circuit_qasm = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[6];
creg c[5];
h q[0];
h q[1];
h q[2];
h q[3];
h q[4];
x q[5];
h q[5];
cx q[0],q[5];
cx q[2],q[5];
cx q[4],q[5];
h q[0];
h q[1];
h q[2];
h q[3];
h q[4];
barrier q[0],q[1],q[2],q[3],q[4],q[5];
measure q[0] -> c[0];
measure q[1] -> c[1];
measure q[2] -> c[2];
measure q[3] -> c[3];
measure q[4] -> c[4];
"""
# Visualize the defined quantum circuit
print(QuantumCircuit.from_qasm_str(circuit_qasm))
Output:
┌───┝ ┌───┝ ░ ┌─┝
q_0: ┤ H ├───────■──┤ H ├───────────░─┤M├────────────
├───┤┌───┝ │ └───┘ ░ └╥┘┌─┝
q_1: ┤ H ├┤ H ├──┼──────────────────░──╫─┤M├─────────
├───┤└───┘ │ ┌───┝ ░ ║ └╥┘┌─┝
q_2: ┤ H ├───────┼────■──┤ H ├──────░──╫──╫─┤M├──────
├───┤┌───┝ │ │ └───┘ ░ ║ ║ └╥┘┌─┝
q_3: ┤ H ├┤ H ├──┼────┼─────────────░──╫──╫──╫─┤M├───
├───┤└───┘ │ │ ┌───┝ ░ ║ ║ ║ └╥┘┌─┝
q_4: ┤ H ├───────┼────┼────■──┤ H ├─░──╫──╫──╫──╫─┤M├
├───┤┌───┝┌─┴─┝┌─┴─┝┌─┴─┝└───┘ ░ ║ ║ ║ ║ └╥┘
q_5: ┤ X ├┤ H ├┤ X ├┤ X ├┤ X ├──────░──╫──╫──╫──╫──╫─
└───┘└───┘└───┘└───┘└───┘ ░ ║ ║ ║ ║ ║
c: 5/╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╝╩╝╝╩╝╝╩╝╝╩╝╝╩╝
0 1 2 3 4
2. Executing the Quantum Circuit on Quantum Hardware
After preparing your quantum circuit, the next step is to execute it on actual quantum hardware. LunaQ simplifies this process, offering streamlined access to various quantum hardware vendors, including IBM Quantum and Q-CTRL's FireOpal.
First, you need to initialize LunaQ with your credentials. This setup enables communication between your local environment and the Luna platform, facilitating the execution of your quantum circuit on the chosen quantum hardware.
from luna_sdk.controllers.luna_q import LunaQ
# Initialize LunaQ with your credentials
lq = LunaQ(email="YOUREMAIL", password="YOURPASSWORD")
Before sending your circuit for execution, you must configure access to the quantum hardware by setting your Quantum Processing Unit (QPU) token. This token is essential for authenticating your access to the hardware providers like IBM Quantum.
Note: The name of your token has to be unique. You might refer to it later again, so make sure to select a name you can remember.
# Set your QPU token for IBM Quantum
lq.qpu_token.create(
name="My IBM Token",
provider="ibm",
token="<TOKEN>",
token_type="personal"
)
With your LunaQ client configured and QPU tokens set, you're ready to submit your quantum circuit for execution. You can choose the quantum hardware provider where you want to run your circuit. This tutorial covers execution on IBM Quantum, but the process is similar for other providers.
from luna_sdk.schemas.qpu_token import QpuToken, TokenProvider
# Submit your quantum circuit for execution on IBM Quantum
job = lq.circuit.create(
circuit=circuit_qasm,
provider="ibm",
qpu_tokens=TokenProvider(
ibm=QpuToken(
source="personal",
name="My IBM Token"
)
),
params={
"backend_name": "ibm_kyoto", # Choose the IBM backend of your choice
"shots": 4096 # Leave empty to use primitive's default
}
)
Since execution on quantum hardware may require some time for completion, we have designed the process to be asynchronous. When you request a circuit run, Luna will manage all the necessary steps in the background. You're free to continue with other tasks while the computation is underway, returning at your convenience when it's finished.
circuit_result = lq.circuit.get(job)
print(circuit_result)
Output:
status=<CircuitStatusEnum.DONE: 'DONE'>
result = {
'counts': {
'10101': 3697,
'10000': 9,
'10001': 104,
'11101': 14,
'00101': 39,
'10100': 183,
'00000': 18,
'00100': 6,
'00001': 16,
'10111': 5,
'11001': 5
},
'metadata': {
'execution': {
'execution_spans': {
'__type__': 'ExecutionSpanCollection',
'__value__': {
'spans': [{
'__type__': 'ExecutionSpan',
'__value__': {
'start': '2024-09-15T07:54:43.599900Z',
'stop': '2024-09-15T07:55:03.985031Z',
'data_slices': {
'0': [[4096], 0, 4096]
}
}
}]
}
}
},
'version': 2
}
}
error_message=None
3. Q-CTRL's Fire Opal for Automated Error Suppression
In addition to using IBM's hardware, you can also access IBM via Fire Opal. Fire Opal is a service from Q-CTRL that allows you to easily run your most valuable quantum applications by abstracting hardware, automatically reducing error, and boosting algorithmic success on quantum computers in the NISQ era and beyond.
LunaQ offers a built-in function for using Fire Opal. You only need to specify the necessary parameters to access Fire Opal and the rest of the workflow remains the same.
# Set your access token for Q-CTRL's Fire Opal
lq.qpu_token.create(
name="My Q-CTRL Token",
provider="qctrl",
token="<TOKEN>",
token_type="personal"
)
params={
"backend_name": "ibm_kyoto", # Choose the IBM backend of your choice
# If you are a member of multiple organizations at Q-CTRL, specify your organization here
"organization_slug": "<SLUG>"
}
# Run the program from the OpenQASM string on IBM's quantum hardware via Fire Opal
job_fireopal = lq.circuit.create(
provider="qctrl",
circuit=circuit_qasm,
qpu_tokens=TokenProvider(
qctrl=QpuToken(
source="personal",
name="My Q-CTRL Token"
),
ibm=QpuToken(
source="personal",
name="My IBM Token"
)
),
params=params
)
backend_name
parameter, you can pass either the actual name of the backend you want to use, e.g., "ibm_kyoto"
or the string "least_busy"
to use the least busy backend. The latter causes an overhead when creating the job.4. Execution via Amazon Braket
LunaQ offers multiple quantum providers, another one of them being Amazon Braket. Amazon Braket is a fully managed quantum computing service that provides access to quantum hardware from leading providers, making it useful for running and experimenting with quantum algorithms in a scalable and accessible environment.
LunaQ offers a built-in function for using Braket. You only need to specify the necessary parameters to access Braket and the rest of the workflow remains the same.
For Amazon's Services, Luna currently offers OpenQASM 3.0 as input format.
# Defining a quantum circuit to prepara a GHZ state in OpenQASM 3.0 format:
qasm3_str = """// ghz.qasm
// Prepare a GHZ state
OPENQASM 3;
qubit[3] q;
bit[3] c;
h q[0];
cnot q[0], q[1];
cnot q[1], q[2];
c = measure q;
"""
You also need to create your access tokens for Amazon Braket.
# Set your QPU tokens for Amazon Braket
lq.qpu_token.create(
name="My AWS Access Key",
provider="aws",
token="<AWS Access Key>",
token_type="personal"
)
lq.qpu_token.create(
name="My AWS Secret Access Key",
provider="aws",
token="<AWS Secret Access Key>",
token_type="personal"
)
Once you have prepared your quantum circuit, you need to set the parameters that also define the Amazon Braket device on which you want to run the circuit. You can find more details about the available hardware here. You can then send your circuit for execution.
# Set the necessary parameters. In this case, we use a simulator provided by Amazon.
params = {
"aws_provider": "Amazon",
"aws_device": "SV1",
"shots": 2048,
}
job_braket = lq.circuit.create(
circuit=qasm3_str,
provider="aws",
params=params,
qpu_tokens=TokenProvider(
aws_access_key=QpuToken(
source="inline",
token="<Your AWS access key>",
),
aws_secret_access_key=QpuToken(
source="inline",
token="<Your AWS secret access key>",
),
),
)
Alternatively, you can use environment variables for inline QPU tokens. For detailed instructions, please refer to the QPU tokens documentation.
5. Retrieving Your Results
Given the nature of quantum hardware operations, which can vary in execution time, we've optimized our process to be asynchronous. This means that after you submit your circuit for execution, Luna takes care of everything behind the scenes. This setup allows you to focus on other work or projects, while Luna is efficiently handling your quantum circuit execution. You can check back at a time that suits you to see the results.
Results Fire Opal
from qiskit.visualization import plot_histogram
# Retrieve the output of your quantum program
results_fireopal = lq.circuit.get(job_fireopal)
# Retrieve the results of your circuits, ignoring any metadata
circuit_results_fireopal = results_fireopal.result['results']
# Plot your results from FireOpal
plot_histogram(circuit_results_fireopal, title="My Circuit Results (FireOpal)")
Output:
Results Amazon Braket
from qiskit.visualization import plot_histogram
# Retrieve the output of your quantum program
results_braket = lq.circuit.get(job_braket)
# Retrieve the results of your circuits, ignoring any metadata
circuit_results_brakets = results_braket.result['measurement_counts']
# Plot your results from Amazon Braket
plot_histogram(circuit_results_brakets, title="My Circuit Results (Amazon Braket)")
Output: