Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
309 changes: 309 additions & 0 deletions exercises/module2_register_drive_program_EXERCISES.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "8454d054",
"metadata": {},
"source": [
"# QoolQit Exercises — Module 2\n",
"## Register, Drive and Quantum Programs\n",
"\n",
"In Module 1 we learned how to describe geometry with graphs. This module\n",
"introduces the three objects that define a computation in the Rydberg analog\n",
"model:\n",
"\n",
"- the **`Register`** — *where the atoms are*;\n",
"- the **`Drive`** — *what we do to them over time* (laser amplitude, detuning\n",
" and phase);\n",
"- the **`QuantumProgram`** — the combination of the two.\n",
"\n",
"### In this module you will learn\n",
"- How to build a `Register` from coordinates or from a graph, and inspect its\n",
" distances and interactions\n",
"- The waveform classes (`ConstantWaveform`, `RampWaveform`,\n",
" `PiecewiseLinearWaveform`, ...) and their rules\n",
"- How to compose a `Drive` and what QoolQit validates for you\n",
"- How to assemble a `QuantumProgram`\n",
"\n",
"\n",
"> **How to use this notebook.** \n",
"> - Cells marked **✏️ Exercise** contain gaps\n",
"> indicated by `...` or `# TODO` — replace them with working code following\n",
"> the instructions. \n",
"> - Cells marked **✅ Check** verify your answer: run them\n",
"> after completing the exercise. Everything else is provided and runs as-is.\n",
"> A separate **solution notebook** will be published.\n",
">\n",
"> **API note:** we use qoolqit version 1.4"
]
},
{
"cell_type": "markdown",
"id": "037e35be",
"metadata": {},
"source": [
"## 1. The Register\n",
"\n",
"A `Register` describes the layout of atoms loaded on the device. "
]
},
{
"cell_type": "markdown",
"id": "6af3af8d",
"metadata": {},
"source": [
"### ✏️ Exercise 2.1 — Build and inspect a Register\n",
"\n",
"1. Build `register`, a 3-atom register at coordinates\n",
" `(0, 0)`, `(1, 0)` and `(0.5, 0.9)` (a near-equilateral triangle).\n",
"2. Print `n_qubits` and draw it.\n",
"3. Print the pairwise `distances()` and `interactions()`.\n",
"4. **Sanity check the physics yourself**: for the pair `(0, 1)` at distance\n",
" $r = 1$, verify by hand that the printed interaction equals $1/r^6 = 1$."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aa9b16f7",
"metadata": {},
"outputs": [],
"source": [
"# TODO: build the 3-atom register\n",
"register = ...\n",
"\n",
"print(\"Number of qubits:\", ...)\n",
"register.draw()\n",
"\n",
"print(\"Distances: \", register.distances())\n",
"print(\"Interactions:\", register.interactions())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "576a962c",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert register.n_qubits == 3\n",
"assert abs(register.interactions()[(0, 1)] - 1.0) < 1e-9\n",
"print(\"Register built correctly — J(0,1) = 1/r^6 = 1 as expected.\")"
]
},
{
"cell_type": "markdown",
"id": "7bef6f88",
"metadata": {},
"source": [
"## 2. Waveforms\n",
"\n",
"A `Drive` is built out of **waveforms** — functions of (dimensionless) time.\n",
"The main ones:\n",
"\n",
"| Class | Signature | Shape |\n",
"|-------|-----------|-------|\n",
"| `ConstantWaveform` | `(duration, value)` | flat |\n",
"| `RampWaveform` | `(duration, initial_value, final_value)` | linear ramp |\n",
"| `PiecewiseLinearWaveform` | `(durations, values)` | N connected ramps through N+1 values (**N ≥ 2**) |\n",
"\n",
"Two handy facts:\n",
"- waveforms can be **rescaled** by multiplication: `wf * 2.0`;\n",
"- every waveform has `.duration`, `.max()`, `.min()` and can be inspected\n",
" with `Drive(...).draw()` once inside a drive."
]
},
{
"cell_type": "markdown",
"id": "dc351eb2",
"metadata": {},
"source": [
"### ✏️ Exercise 2.2 — Build the classic annealing waveforms\n",
"\n",
"1. Build `wf_trap`: a `PiecewiseLinearWaveform` with total duration `T = 4`\n",
" that ramps `0 → 1` in the first quarter (`T/4`), stays at `1` for half\n",
" (`T/2`), and ramps back `1 → 0` in the last quarter (`T/4`) — a\n",
" *trapezoid*. Remember: **N durations, N+1 values**.\n",
"2. Build `wf_ramp`: a `RampWaveform` of duration `T` from `-1` to `+1`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "921f4e6c",
"metadata": {},
"outputs": [],
"source": [
"from qoolqit import PiecewiseLinearWaveform, RampWaveform\n",
"\n",
"T = 4\n",
"\n",
"# TODO: trapezoid 0 -> 1 -> 1 -> 0 over [T/4, T/2, T/4]\n",
"wf_trap = PiecewiseLinearWaveform([...], [...])\n",
"\n",
"# TODO: ramp from -1 to +1 over T\n",
"wf_ramp = ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "79198629",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert abs(wf_trap.duration - 4.0) < 1e-9\n",
"assert abs(wf_trap.max() - 1.0) < 1e-9\n",
"assert wf_ramp.min() == -1.0 and wf_ramp.max() == 1.0\n",
"print(\"Waveforms built correctly!\")"
]
},
{
"cell_type": "markdown",
"id": "a66b0685",
"metadata": {},
"source": [
"## 3. The Drive\n",
"\n",
"The `Drive` collects the control parameters of the time-dependent Hamiltonian\n",
"\n",
"$$\n",
"H_{\\mathrm{drive}}(t) = \\sum_i \\frac{\\Omega(t)}{2}\\left(\\cos\\varphi\\,\\hat\\sigma^x_i - \\sin\\varphi\\,\\hat\\sigma^y_i\\right) - \\sum_i \\delta(t)\\, \\hat n_i\n",
"$$\n",
"\n",
"- **amplitude** $\\Omega(t)$ — the Rabi frequency driving the qubits\n",
" (*required*, must be $\\geq 0$ at all times);\n",
"- **detuning** $\\delta(t)$ — the energy offset of the Rydberg state\n",
" (*optional*, defaults to zero);\n",
"- **phase** $\\varphi$ — a global phase (*optional*, defaults to 0).\n",
"\n",
"All arguments are **keyword-only**: `Drive(amplitude=..., detuning=...)`."
]
},
{
"cell_type": "markdown",
"id": "055488be",
"metadata": {},
"source": [
"### ✏️ Exercise 2.3 — Compose a Drive (and let QoolQit catch your mistakes)\n",
"\n",
"1. Build `drive = Drive(amplitude=wf_trap, detuning=wf_ramp)` and `draw()` it.\n",
" Print its `duration`.\n",
"2. **Negative amplitude is unphysical**: in a `try/except ValueError`, try\n",
" `Drive(amplitude=RampWaveform(2.0, 0.5, -0.5))` and print the error.\n",
"3. **Drive compositions**: `Drive`s can be composed with `>>` to concatenate them."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "786ace14",
"metadata": {},
"outputs": [],
"source": [
"from qoolqit import Drive\n",
"\n",
"# TODO 1: define and draw the drive\n",
"drive = ...\n",
"drive.draw()\n",
"print(\"Drive duration:\", drive.duration)\n",
"\n",
"# TODO 2: negative amplitude must be rejected\n",
"try:\n",
" Drive(amplitude=RampWaveform(2.0, 0.5, -0.5))\n",
"except ValueError as err:\n",
" print(\"As expected:\", err)\n",
"\n",
"# TODO 3: create a `Drive` that repeats drive twice and draw it\n",
"double_drive = ...\n",
"double_drive.draw()\n",
"print(\"Drive duration:\", double_drive.duration)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6cf4e95e",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert abs(drive.duration - 4.0) < 1e-9\n",
"assert abs(double_drive.duration - 8.0) < 1e-9\n",
"print(\"Drive composition rules understood!\")"
]
},
{
"cell_type": "markdown",
"id": "1c016719",
"metadata": {},
"source": [
"## 4. The QuantumProgram\n",
"\n",
"A `QuantumProgram` is simply *register + drive*: where the atoms are, and\n",
"what we do to them. It is created **device-agnostic** — in dimensionless\n",
"units, without reference to any hardware. Turning it into something a real\n",
"machine can run is the job of *compilation* (Module 3)."
]
},
{
"cell_type": "markdown",
"id": "efe5b8fd",
"metadata": {},
"source": [
"### ✏️ Exercise 2.4 — Assemble a QuantumProgram\n",
"\n",
"1. Build `program = QuantumProgram(register, drive)` from the register of\n",
" Exercise 2.1 and the drive of Exercise 2.3, and print it.\n",
"2. Print `program.is_compiled` — it should be `False`: no device yet!\n",
"3. Draw the (uncompiled) program with `program.draw()`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2cfc9a6d",
"metadata": {},
"outputs": [],
"source": [
"# TODO: assemble the program\n",
"program = ...\n",
"print(program)\n",
"print(\"Compiled?\", ...)\n",
"program.draw()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8e6d8461",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert not program.is_compiled\n",
"print(\"Program assembled — and correctly not compiled yet.\")"
]
},
{
"cell_type": "markdown",
"id": "4ae9cbb9",
"metadata": {},
"source": [
"### Next module\n",
"In **Module 3** we bring in the hardware: compiling programs to devices with\n",
"real constraints, and executing them on an emulator — including your first\n",
"genuinely quantum experiment, the **Rydberg blockade**."
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
396 changes: 396 additions & 0 deletions solutions/module1_graphs_embedding_SOLUTIONS.ipynb

Large diffs are not rendered by default.