Skip to content
Pasqal Documentation

Execute Custom Quantum Program: Tutorial

from qoolqit import DataGraph, Register
#Define a register from a DataGraph
graph=DataGraph.hexagonal(1,1)
reg=Register.from_graph(graph)
#Define a register from positions
reg=Register.from_coordinates(list(graph.coords.values()))
import math
from qoolqit.waveforms import Waveform
class SmoothPulse(Waveform):
"""f(t) = omega_max * sin( (π/2) * sin(π t) )^2, for 0 ≤ t ≤ duration"""
def __init__(self, duration: float , omega_max: float) -> None:
super().__init__(duration, omega_max=omega_max)
def function(self, t: float) -> float:
return self.omega_max * math.sin(0.5 * math.pi * math.sin(math.pi * t/self.duration)) ** 2

3. Combine Register and Drive into a Quantum Program

Section titled “3. Combine Register and Drive into a Quantum Program”

All quantum devices have a hardware limit for the maximum drive strength they can apply Ωmax\Omega_\text{max}.
To make programs device-independent, we take this maximum value as our unit of energy and measure every amplitude as a function of it:

Ωˉ=ΩΩmax \bar{\Omega} = \frac{\Omega}{\Omega_\text{max}} For example:

  • If the physical maximum is $\Omega_\text{max}=4\pi \,\text{rad/µs}$, then $\bar{\Omega}_\text{max}=1$.
  • A drive of $\Omega=2\pi \,\text{rad/µs}$ becomes $\bar{\Omega}=0.5$.
  • A drive of $\Omega=\pi \,\text{rad/µs}$ becomes $\bar{\Omega}=0.25$.

This makes all quantum programs dimensionless and therefore comparable across devices.


Normalizing the drive amplitude automatically sets the scale for time and space as well:

  • Energy units:
    Eˉ=EΩmax \bar{E} = \frac{E}{\Omega_\text{max}} So also the detuning is measured in units of Ωmax\Omega_\text{max}.

  • Time units:
    At zero detuning, the Rabi frequency is equal to the drive,
    ΩR=Ωmax, \Omega_R = \Omega_{\text{max}},
    so the natural unit of time is
    tR=1Ωmax. t_R = \frac{1}{\Omega_\text{max}}.
    All times are expressed in multiples of the Rabi period at maximum drive T=2πΩmax=2πtRT=\frac{2 \pi}{\Omega_{\text{max}}}=2 \pi t_R.
    tˉ=ttR=2πtT \bar{t} = \frac{t}{t_R}= \frac{2 \pi t}{T} So, in a time tˉ=2π\bar{t}=2 \pi one gets a full Rabi period.

  • Space units:
    Interactions follow E(r)=C6/r6E(r)=C_6/r^6.
    The characteristic length is defined by setting E(rB)=ΩmaxE(r_B)=\Omega_\text{max}:
    rB=(C6Ωmax)1/6. r_B = \left(\frac{C_6}{\Omega_\text{max}}\right)^{1/6}.
    Distances are expressed relative to rBr_B, i.e. the Rydberg blockade radius at maximum drive.
    rˉ=rrB. \bar{r} = \frac{r}{r_B}.


  • Energy → normalized by $\Omega_\text{max}$
  • Time → measured in $1/\Omega_\text{max}$ (Rabi oscillations)
  • Space → measured in $r_B=(C_6 / \Omega_\text{max})^{1/6}$ (interaction length)
from qoolqit import Drive, QuantumProgram, Ramp
T1 = 2*math.pi #Duration
amp_1 = SmoothPulse(T1,omega_max=1) #amplitude drive
det_1 = Ramp(T1,-1,1) #detuning drive
drive_1=Drive(amplitude=amp_1,detuning=det_1) #definine the drive
program = QuantumProgram(reg, drive_1) #define the quantum program
program.register.draw() #draw the register
program.draw() #draw the drive
from qoolqit import AnalogDevice
device=AnalogDevice() #Call the device
program.compile_to(device) #Compile to a device
program.compiled_sequence.draw()
program.compiled_sequence.register.draw()
import numpy as np
from qoolqit import BackendName, ResultType
# Number of samples
runs = 1000
evaluation_times = np.linspace(0, 1, num=10).tolist()
# Simulate with EMUMPS backend and output bitstring counts
emumps_bitstrings = program.run(
backend_name=BackendName.EMUMPS,
result_type=ResultType.BITSTRINGS,
evaluation_times=evaluation_times,
runs=runs)
print("Length of the output list:")
print(len(emumps_bitstrings))
print("Final state bitstrings:")
print(emumps_bitstrings[-1])
import matplotlib.pyplot as plt
def plot_distribution(counter):
counter = dict(sorted(counter.items(), key=lambda item: item[1], reverse=True))
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
ax.set_xlabel("Bitstrings")
ax.set_ylabel("Counts")
ax.set_xticks(range(len(counter)))
ax.set_xticklabels(counter.keys(), rotation=90)
ax.bar(counter.keys(), counter.values(), width=0.5)
return fig
fig = plot_distribution(emumps_bitstrings[-1])