
In this tutorial, we present an advanced, hands-on tutorial that demonstrates how we use Qrisp to build and execute non-trivial quantum algorithms. We walk through core Qrisp abstractions for quantum data, construct entangled states, and then progressively implement Grover’s search with automatic uncomputation, Quantum Phase Estimation, and a full QAOA workflow for the MaxCut problem. Also, we focus on writing expressive, high-level quantum programs while letting Qrisp manage circuit construction, control logic, and reversibility behind the scenes. Check out the FULL CODES here.
def _pip_install(pkgs):
cmd = [sys.executable, “-m”, “pip”, “install”, “-q”] + pkgs
subprocess.check_call(cmd)
print(“Installing dependencies (qrisp, networkx, matplotlib, sympy)…”)
_pip_install([“qrisp”, “networkx”, “matplotlib”, “sympy”])
print(“✓ Installed\n”)
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
from qrisp import (
QuantumVariable, QuantumFloat, QuantumChar,
h, z, x, cx, p,
control, QFT, multi_measurement,
auto_uncompute
)
from qrisp.qaoa import (
QAOAProblem, RX_mixer,
create_maxcut_cost_operator, create_maxcut_cl_cost_function
)
from qrisp.grover import diffuser
We begin by setting up the execution environment and installing Qrisp along with the minimal scientific stack required to run quantum experiments. We import the core Qrisp primitives that allow us to represent quantum data types, gates, and control flow. We also prepare the optimization and Grover utilities that will later enable variational algorithms and amplitude amplification. Check out the FULL CODES here.
print(“\n” + “=”*90)
print(title)
print(“=”*90)
def topk_probs(prob_dict, k=10):
items = sorted(prob_dict.items(), key=lambda kv: kv[1], reverse=True)[:k]
return items
def print_topk(prob_dict, k=10, label=”Top outcomes”):
items = topk_probs(prob_dict, k=k)
print(label)
for state, prob in items:
print(f” {state}: {prob:.4f}”)
def bitstring_to_partition(bitstring):
left = [i for i, b in enumerate(bitstring) if b == “0”]
right = [i for i, b in enumerate(bitstring) if b == “1”]
return left, right
def classical_maxcut_cost(G, bitstring):
s = set(i for i, b in enumerate(bitstring) if b == “0”)
cost = 0
for u, v in G.edges():
if (u in s) != (v in s):
cost += 1
return cost
banner(“SECTION 1 — Qrisp Core: QuantumVariable, QuantumSession, GHZ State”)
def GHZ(qv):
h(qv[0])
for i in range(1, qv.size):
cx(qv[0], qv[i])
qv = QuantumVariable(5)
GHZ(qv)
print(“Circuit (QuantumSession):”)
print(qv.qs)
print(“\nState distribution (printing QuantumVariable triggers a measurement-like dict view):”)
print(qv)
meas = qv.get_measurement()
print_topk(meas, k=6, label=”\nMeasured outcomes (approx.)”)
qch = QuantumChar()
h(qch[0])
print(“\nQuantumChar measurement sample:”)
print_topk(qch.get_measurement(), k=8)
We define utility functions that help us inspect probability distributions, interpret bitstrings, and evaluate classical costs for comparison with quantum outputs. We then construct a GHZ state to demonstrate how Qrisp handles entanglement and circuit composition through high-level abstractions. We also showcase typed quantum data using QuantumChar, reinforcing how symbolic quantum values can be manipulated and measured. Check out the FULL CODES here.
@auto_uncompute
def sqrt_oracle(qf):
cond = (qf * qf == 0.25)
z(cond)
qf = QuantumFloat(3, -1, signed=True)
n = qf.size
iterations = int(0.25 * math.pi * math.sqrt((2**n) / 2))
print(f”QuantumFloat qubits: {n} | Grover iterations: {iterations}”)
h(qf)
for _ in range(iterations):
sqrt_oracle(qf)
diffuser(qf)
print(“\nGrover result distribution (QuantumFloat prints decoded values):”)
print(qf)
qf_meas = qf.get_measurement()
print_topk(qf_meas, k=10, label=”\nTop measured values (decoded by QuantumFloat):”)
We implement a Grover oracle using automatic uncomputation, allowing us to express reversible logic without manually cleaning up intermediate states. We apply amplitude amplification over a QuantumFloat search space to solve a simple nonlinear equation using quantum search. We finally inspect the resulting measurement distribution to identify the most probable solutions produced by Grover’s algorithm. Check out the FULL CODES here.
def QPE(psi: QuantumVariable, U, precision: int):
res = QuantumFloat(precision, -precision, signed=False)
h(res)
for i in range(precision):
with control(res[i]):
for _ in range(2**i):
U(psi)
QFT(res, inv=True)
return res
def U_example(psi):
phi_1 = 0.5
phi_2 = 0.125
p(phi_1 * 2 * np.pi, psi[0])
p(phi_2 * 2 * np.pi, psi[1])
psi = QuantumVariable(2)
h(psi)
res = QPE(psi, U_example, precision=3)
print(“Joint measurement of (psi, phase_estimate):”)
mm = multi_measurement([psi, res])
items = sorted(mm.items(), key=lambda kv: (-kv[1], str(kv[0])))
for (psi_bits, phase_val), prob in items:
print(f” psi={psi_bits} phase≈{phase_val} prob={prob:.4f}”)
We build a complete Quantum Phase Estimation pipeline by combining controlled unitary applications with an inverse Quantum Fourier Transform. We demonstrate how phase information is encoded into a quantum register with tunable precision using QuantumFloat. We then jointly measure the system and phase registers to interpret the estimated eigenphases. Check out the FULL CODES here.
G = nx.erdos_renyi_graph(6, 0.65, seed=133)
while G.number_of_edges() < 5:
G = nx.erdos_renyi_graph(6, 0.65, seed=random.randint(0, 9999))
print(f”Graph: |V|={G.number_of_nodes()} |E|={G.number_of_edges()}”)
print(“Edges:”, list(G.edges())[:12], “…” if G.number_of_edges() > 12 else “”)
qarg = QuantumVariable(G.number_of_nodes())
qaoa_maxcut = QAOAProblem(
cost_operator=create_maxcut_cost_operator(G),
mixer=RX_mixer,
cl_cost_function=create_maxcut_cl_cost_function(G),
)
depth = 3
max_iter = 25
t0 = time.time()
results = qaoa_maxcut.run(qarg, depth=depth, max_iter=max_iter)
t1 = time.time()
print(f”\nQAOA finished in {t1 – t0:.2f}s (depth={depth}, max_iter={max_iter})”)
print(“Returned measurement distribution size:”, len(results))
cl_cost = create_maxcut_cl_cost_function(G)
print(“\nTop 8 candidate cuts (bitstring, prob, cost):”)
top8 = sorted(results.items(), key=lambda kv: kv[1], reverse=True)[:8]
for bitstr, prob in top8:
cost_val = cl_cost({bitstr: 1})
print(f” {bitstr} prob={prob:.4f} cut_edges≈{cost_val}”)
best_bitstr = top8[0][0]
best_cost = classical_maxcut_cost(G, best_bitstr)
left, right = bitstring_to_partition(best_bitstr)
print(f”\nMost likely solution: {best_bitstr}”)
print(f”Partition 0-side: {left}”)
print(f”Partition 1-side: {right}”)
print(f”Classical crossing edges (verified): {best_cost}”)
pos = nx.spring_layout(G, seed=42)
node_colors = [“#6929C4” if best_bitstr[i] == “0” else “#20306f” for i in G.nodes()]
plt.figure(figsize=(6.5, 5.2))
nx.draw(
G, pos,
with_labels=True,
node_color=node_colors,
node_size=900,
font_color=”white”,
edge_color=”#CCCCCC”,
)
plt.title(f”QAOA MaxCut (best bitstring = {best_bitstr}, cut={best_cost})”)
plt.show()
banner(“DONE — You now have Grover + QPE + QAOA workflows running in Qrisp on Colab ✅”)
print(“Tip: Try increasing QAOA depth, changing the graph, or swapping mixers (RX/RY/XY) to explore behavior.”)
We formulate the MaxCut problem as a QAOA instance using Qrisp’s problem-oriented abstractions and run a hybrid quantum–classical optimization loop. We analyze the returned probability distribution to identify high-quality cut candidates and verify them with a classical cost function. We conclude by visualizing the best cut, connecting abstract quantum results back to an intuitive graph structure.
We conclude by showing how a single, coherent Qrisp workflow allows us to move from low-level quantum state preparation to modern variational algorithms used in near-term quantum computing. By combining automatic uncomputation, controlled operations, and problem-oriented abstractions such as QAOAProblem, we demonstrate how we rapidly prototype and experiment with advanced quantum algorithms. Also, this tutorial establishes a strong foundation for extending our work toward deeper circuits, alternative mixers and cost functions, and more complex quantum-classical hybrid experiments.
Check out the FULL CODES here. Also, feel free to follow us on Twitter and don’t forget to join our 100k+ ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.
