Skip to content
Merged
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
298 changes: 298 additions & 0 deletions exercises/module1_graphs_embedding_EXERCISES.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "713028f0",
"metadata": {},
"source": [
"# QoolQit Exercises — Module 1\n",
"## Graphs and Embedding\n",
"\n",
"Welcome to this hands-on introduction to **QoolQit**, the Python library for\n",
"algorithm development in the Rydberg analog model. Across four self-contained\n",
"modules you will learn all the building blocks of a QoolQit application, and\n",
"assemble them in a quantum application in the final module:\n",
"\n",
"| Module | Topic |\n",
"|--------|-------|\n",
"| 1 | Graphs and Embedding |\n",
"| 2 | Register, Drive and Quantum Programs |\n",
"| 3 | Compilation and Execution |\n",
"| 4 | Putting it all together: solving a QUBO |\n",
"\n",
"### In this module you will learn\n",
"- How to create graphs with the `DataGraph` class (pre-defined layouts,\n",
" random graphs, graphs from raw data)\n",
"- The difference between *abstract* graphs and graphs *with coordinates*\n",
"- How to give coordinates to an abstract graph.\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": "6226deb9",
"metadata": {},
"source": [
"## 1. The `DataGraph` class\n",
"\n",
"In QoolQit, problems and atom layouts are described by graphs. The\n",
"`DataGraph` class (a subclass of `networkx.Graph`) is the central data\n",
"structure: it can hold **connectivity** (edges), **coordinates** (positions\n",
"of the nodes in the plane), and node and edge **weights**."
]
},
{
"cell_type": "markdown",
"id": "f89ebcdc",
"metadata": {},
"source": [
"### ✏️ Exercise 1.1 — Pre-defined graph layouts\n",
"\n",
"Create and draw three graphs:\n",
"1. `g_line`: a **line** graph with 5 nodes;\n",
"2. `g_circle`: a **circle** graph with 6 nodes and `spacing=1.0`;\n",
"3. `g_square`: a **square** grid graph with `m=3` rows and `n=3` columns.\n",
"\n",
"Use each graph's `.draw()` method to visualize it."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af9b7821",
"metadata": {},
"outputs": [],
"source": [
"# TODO: create the three graphs\n",
"g_line = ...\n",
"g_circle = ...\n",
"g_square = ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e506aeb8",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4))\n",
"\n",
"# TODO: print the graphs on the subplots\n",
"# ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eca26aea",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert g_line.number_of_nodes() == 5\n",
"assert g_circle.number_of_nodes() == 6\n",
"assert g_square.number_of_nodes() == 9\n",
"print(\"All three graphs look right!\")"
]
},
{
"cell_type": "markdown",
"id": "a9b0848c",
"metadata": {},
"source": [
"## 2. Abstract graphs vs. graphs with coordinates\n",
"\n",
"Not every graph has coordinates. What is required is only their connectivity: edges and nodes. \n",
"A **random Erdős–Rényi graph**, for\n",
"instance, is purely *abstract*: it defines which nodes are connected, but\n",
"says nothing about where the nodes sit in the plane. Since neutral atoms live\n",
"in real space, sooner or later every graph needs coordinates. That is the\n",
"job of *embedding* (Section 4).\n",
"\n",
"Useful properties to interrogate a graph: `has_coords`, `has_edges`,\n",
"`has_node_weights`, `has_edge_weights`."
]
},
{
"cell_type": "markdown",
"id": "e3ffa662",
"metadata": {},
"source": [
"### ✏️ Exercise 1.2 — An abstract random graph\n",
"\n",
"1. Create `g_er`, an Erdős–Rényi random graph with `n=8` nodes, edge\n",
" probability `p=0.4` and `seed=3` (for reproducibility).\n",
"2. Print whether it has coordinates (`has_coords`) and how many edges it has.\n",
"3. Try to compute `g_er.min_distance()` inside a `try/except Exception` block\n",
" and print the error — distances make no sense without coordinates!"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "61d92e10",
"metadata": {},
"outputs": [],
"source": [
"# TODO: create the random ER graph\n",
"g_er = ...\n",
"\n",
"print(\"Has coordinates:\", ...)\n",
"print(\"Number of edges:\", g_er.number_of_edges())\n",
"\n",
"try:\n",
" g_er.min_distance()\n",
"except AttributeError as err:\n",
" print(\"As expected, this fails:\", err)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "724d4d0a",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert g_er.number_of_nodes() == 8\n",
"assert not g_er.has_coords, \"An ER graph should be abstract (no coordinates)\"\n",
"print(\"Correct — g_er is an abstract graph.\")"
]
},
{
"cell_type": "markdown",
"id": "2a2264fd",
"metadata": {},
"source": [
"## 3. Embedding: assign coordinates to an abstract graph or interaction matrix\n",
"\n",
"**Embedding** is the process of assigning coordinates to a graph. QoolQit can take an\n",
" abstract graph and return the same graph *with coordinates* or take a symmetric matrix of *desired interactions* and return a graph whose node positions physically realize them (next section).\n"
]
},
{
"cell_type": "markdown",
"id": "d7a6df28",
"metadata": {},
"source": [
"### ✏️ Exercise 1.4 — Spring-layout embedding of the random graph\n",
"\n",
"1. Import `SpringLayoutEmbedder` from `qoolqit.embedding` and instantiate it.\n",
"2. Embed the abstract graph `g_er` from Exercise 1.2 into `g_er_embedded`.\n",
"3. Verify the embedded graph now has coordinates, print its `min_distance()`,\n",
" and draw it."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b0314e34",
"metadata": {},
"outputs": [],
"source": [
"# TODO: instantiate the embedder and embed g_er\n",
"embedder = ...\n",
"g_er_embedded = ...\n",
"\n",
"print(\"Has coordinates:\", g_er_embedded.has_coords)\n",
"print(\"Minimum distance:\", g_er_embedded.min_distance())\n",
"g_er_embedded.draw()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0f8d3fcb",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check\n",
"assert g_er_embedded.has_coords, \"The embedded graph should have coordinates\"\n",
"print(\"Spring-layout embedding successful!\")"
]
},
{
"cell_type": "markdown",
"id": "de059158",
"metadata": {},
"source": [
"### ✏️ Exercise 1.5 — Embed a target interaction matrix\n",
"\n",
"1. Define the symmetric 3×3 target matrix\n",
" `M = [[0, 1, 0.3], [1, 0, 0.5], [0.3, 0.5, 0]]` as a NumPy array.\n",
"2. Instantiate an `InteractionEmbedder` (from `qoolqit.embedding`) and embed\n",
" `M` into `g_int`.\n",
"3. Draw `g_int`, then print its `interactions()` dictionary side by side with\n",
" the corresponding entries of `M`. How close did the embedder get?\n",
"\n",
"> 💡 The `interactions()` method computes $1/r^6$ for each pair of nodes from\n",
"> the coordinates."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6b2d9b52",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"# TODO: define the target interaction matrix\n",
"M = np.array(...)\n",
"\n",
"# TODO: embed it\n",
"g_int = ...\n",
"\n",
"g_int.draw()\n",
"\n",
"for (i, j), J in g_int.interactions().items():\n",
" print(f\"pair ({i},{j}): J = {J:.4f} target M = {M[i, j]:.4f}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "881fa9d1",
"metadata": {},
"outputs": [],
"source": [
"# ✅ Check — every realized interaction within 5% of its target\n",
"for (i, j), J in g_int.interactions().items():\n",
" assert abs(J - M[i, j]) < 0.05, f\"pair ({i},{j}) is off: {J} vs {M[i, j]}\"\n",
"print(\"Interaction embedding matches the target matrix!\")"
]
},
{
"cell_type": "markdown",
"id": "f8f2abe6",
"metadata": {},
"source": [
"### Next module\n",
"Graphs describe *data* and *geometry*. In **Module 2** we turn geometry into\n",
"physics: the `Register` of atoms, the time-dependent `Drive`, and the\n",
"`QuantumProgram` that combines them."
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}