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:

  1. Learn to prepare your quantum circuits.
  2. Understand how to execute your algorithm on quantum hardware via LunaQ, specifically with IBM Quantum and Q-CTRL's FireOpal.
  3. 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": "least_busy"}  # Chooses the least busy backend
)

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={
    'quasi_dists':
        [
            {
                '0': 0.0067790060087083185,
                '1': 0.004285616799561392,
                '4': 0.00030223713284139355,
                '5': 0.006663017084471091,
                '16': 0.0002779077288583342,
                '17': 0.006900962495015426,
                '20': 0.006632800307242497,
                '21': 1.0043188239727503,
                '23': -0.0016830659496742202,
                '29': -0.03477025876287272,
                '31': 0.00029295318309822705
            }
        ],
        'metadata':
            [
                {
                    'shots': 4000,
                    'circuit_metadata': {},
                    'readout_mitigation_overhead': 1.3115744780798482,
                    'readout_mitigation_time': 0.10430949833244085,
                    'warning': 'Optimization level clipped from 3 to 1'
                }
            ]
    }
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": "least_busy",
    # 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
)

4. 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.

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:

luna-q-ctrl-results

Was this page helpful?