Skip to content

Results are limited to the current section : Qoolqit

Quantum Program

In this page, you will learn how to build a quantum program, from its building blocks:

  • Create a Register from a set of coordinates,
  • Define Waveforms selecting amplitude and detuning,
  • Build a Drive from waveform components,
  • Instantiate a QuantumProgram from a Register and a Drive,
  • Check whether a program has already been compiled.

The QuantumProgram defines the protocol used to solve your problem within the adimensional framework of the Rydberg Analog model.

In practice, this involves specifying both the interaction and the driving Hamiltonian. In QoolQit, these are set up by creating a Register, which determines the positions of the qubits, and a Drive object, which describes how laser fields control the qubits over time.

To run the program on real quantum hardware, the abstract QuantumProgram must first be compiled into a form compatible with a specific QPU. This compilation process will be covered later in the Compilation page.

A Register defines the qubit resources to be used by a quantum program.

from qoolqit import Register
qubits = {
0: (-0.5, -0.5),
1: (-0.5, 0.5),
2: (0.5, -0.5),
3: (0.5, 0.5),
}
register = Register(qubits)
print(register)
coords = [(-0.5, -0.5), (-0.5, 0.5), (0.5, -0.5), (0.5, 0.5)]
register = Register.from_coordinates(coords)
register.draw()
register.distances()
register.distances()
register.min_distance()
register.interactions()

An essential part of writing programs in the Rydberg analog model is to write the time-dependent functions representing the amplitude and detuning terms in the drive Hamiltonian. For that, QoolQit implements a set of waveforms that can be used directly and/or composed together.

A full list of the available waveforms can be found in the API reference page of this documentation.

from qoolqit import Constant, Delay, Ramp
# An empty waveform
wf1 = Delay(1.0)
print(wf1)
# A waveform with a constant value
wf2 = Constant(1.0, 2.0)
print(wf2)
# A waveform that ramps linearly between two values
wf3 = Ramp(1.0, -1.0, 1.0)
print(wf3)
wf1(t = 0.0)
wf2(t = 0.5)
wf3(t = 1.0)
print("wf1(t = 0.0) =", wf1(t = 0.0))
print("wf2(t = 0.5) =", wf2(t = 0.5))
print("wf3(t = 1.0) =", wf3(t = 1.0))
import numpy as np
t_array = np.linspace(0.0, 2.0, 9)
wf3(t_array)
fig = wf3.draw()

Special waveform to fit a set given values with a smooth function. For the full set of available options please refer to the API reference section of this documentation.

from qoolqit import Interpolated
values = np.sin(np.linspace(0,2*np.pi, 10))
wf_interpolated = Interpolated(100, values)
wf_interpolated.draw()

The most straightforward way to arbitrarily compose waveforms is to use the >> operator. This will create a CompositeWaveform representing the waveforms in the order provided.

wf_comp = wf1 >> wf2 >> wf3
print("Total duration :", wf_comp.duration)
# List of durations of the individual waveforms
print("List of durations :", wf_comp.durations)
# List of times where each individual waveform starts / ends
print("List of times :", wf_comp.times)
from qoolqit import PiecewiseLinear
durations = [1.0, 1.0, 2.0]
values = [0.0, 1.0, 0.5, 0.5]
wf_pwl = PiecewiseLinear(durations, values)
wf_pwl.draw()

Built-in waveforms cover the most common shapes, but any differentiable (or piecewise-smooth) profile can be realized by subclassing Waveform. For a full walkthrough — including concrete examples and how to use custom waveforms inside a Drive — see Defining custom waveforms.

The Drive is a collection of amplitude and detuning waveforms, plus an optional phase, fully specifying the drive Hamiltonian described in the QoolQit model page. Here is an example on how to create a drive:

from qoolqit import Constant, Drive, Ramp
# Defining two waveforms
amplitude = Constant(duration=5.0, value=1.0) >> Ramp(1.0, 0.0, 0.5)
detuning = Ramp(2.0, -1.0, 1.0) >> Constant(1.0, 1.0)
# Defining the drive
drive = Drive(
amplitude = amplitude,
detuning = detuning
)
# Expanding the drive through composition
drive = drive >> drive
print(drive)
drive.draw()

To understand the role of time and the duration of a drive in the Rydberg Analog model, please have a look at the Time regimes page. Alternatively, duration can also be overwritten at compilation time, as relative to the maximum duration allowed by a specific hardware device. Such feature, is useful, for example, when working on adiabatic protocols. For more details, please have a look at the Device and compilation page of the documentation, specifically at the Special compilation flags section.

Finally, at the compilation stage, the duration set by the user might be higher than what the selected QPU device allows. Compilation will thus trigger an informative error about the hardware limitations and how to comply with those.

A QuantumProgram combines a Register and a Drive and serves as the main interface for compilation and execution.

from qoolqit import Drive, PiecewiseLinear, QuantumProgram, Register
# Defining the Drive
wf0 = PiecewiseLinear([1.0, 2.0, 1.0], [0.0, 0.5, 0.5, 0.0])
wf1 = PiecewiseLinear([1.0, 2.0, 1.0], [-1.0, -1.0, 1.0, 1.0])
drive = Drive(amplitude = wf0, detuning = wf1)
# Defining the Register
coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 0.0), (1.0, 1.0)]
register = Register.from_coordinates(coords)
# Creating the Program
program = QuantumProgram(register, drive)
print(program)
program.is_compiled