import matplotlib.pyplot as plt
# Subproblem k: position in the tree, and the optimal solution x^k and the
# optimal value z^k of its LP relaxation, found by the greedy method
nodes = {
0: dict(pos=(1.125, 3), x=(1, 1, 0.75, 0), z=23.25),
1: dict(pos=(0, 2), x=(1, 1, 0, 0.75), z=18, note="pruned by bound"),
2: dict(pos=(2.25, 2), x=(1, 0.5, 1, 0), z=23),
3: dict(pos=(1.5, 1), x=(1, 0, 1, 0.25), z=21),
4: dict(pos=(3, 1), x=(0, 1, 1, 0), z=17, note="integer"),
5: dict(pos=(1, 0), x=(1, 0, 1, 0), z=20, note="integer, optimal"),
6: dict(pos=(2, 0)), # infeasible
}
discarded = {1, 6} # gray boxes
optimal = 5 # thick box
# Parent, child, and the variable fixed by branching
edges = [
(0, 1, r"$x_3 = 0$"),
(0, 2, r"$x_3 = 1$"),
(2, 3, r"$x_2 = 0$"),
(2, 4, r"$x_2 = 1$"),
(3, 5, r"$x_4 = 0$"),
(3, 6, r"$x_4 = 1$"),
]
# Computer Modern fonts, as in LaTeX. The last setting avoids a warning about cmr10.
latex_style = {
"font.family": "cmr10",
"mathtext.fontset": "cm",
"axes.formatter.use_mathtext": True,
}
with plt.rc_context(latex_style):
fig, ax = plt.subplots(figsize=(6.5, 4.6))
for parent, child, label in edges:
(px, py), (cx, cy) = nodes[parent]["pos"], nodes[child]["pos"]
ax.plot([px, cx], [py, cy], color="black", linewidth=1, zorder=1)
# Place the label on the outer side of the edge
side = 1 if cx > px else -1
ax.text(
(px + cx) / 2 + 0.07 * side,
(py + cy) / 2,
label,
ha="left" if side > 0 else "right",
va="center",
fontsize=11,
)
for k, node in nodes.items():
if "x" in node:
x = r",\, ".join(f"{v:g}" for v in node["x"])
lines = [
rf"$P_{k}$",
rf"$\mathbf{{x}} = ({x})$",
rf"$z = {node['z']:g}$",
]
else:
lines = [rf"$P_{k}$", "infeasible"]
ax.text(
*node["pos"],
"\n".join(lines),
ha="center",
va="center",
fontsize=11,
linespacing=1.4,
zorder=2,
bbox=dict(
boxstyle="square,pad=0.4",
facecolor="lightgray" if k in discarded else "white",
edgecolor="black",
linewidth=2 if k == optimal else 1,
),
)
if "note" in node:
pos = node["pos"]
ax.text(
pos[0],
pos[1] - 0.4,
node["note"],
ha="center",
va="top",
fontsize=10,
color="dimgray",
)
ax.set(xlim=(-0.5, 3.5), ylim=(-0.7, 3.35))
ax.axis("off")
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
plt.show()