codexgraph-laplacian-edge-energy.py
graph-laplacian-edge-energy.py
"""Plot path edge energy and eigenvectors; write an opaque PNG in the CWD."""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
incidence = np.array([[1, -1, 0], [0, 1, -1]])
laplacian = incidence.T @ incidence
values = np.array([2, 0, -1])
energies = (incidence @ values) ** 2
modes = np.array([[1, 1, 1], [1, 0, -1], [1, -2, 1]])
eigenvalues = np.array([0, 1, 3])
assert np.array_equal(energies, [4, 1])
assert values @ laplacian @ values == energies.sum() == 5
assert np.array_equal(laplacian @ modes.T, modes.T * eigenvalues)
colors = ["#2868a6", "#d07826", "#4b8651"]
fig = plt.figure(figsize=(9.6, 5.6), dpi=110, facecolor="white")
grid = fig.add_gridspec(2, 3, height_ratios=[1.1, 1], hspace=0.55,
left=0.075, right=0.97, bottom=0.12, top=0.88,
wspace=0.37)
graph = fig.add_subplot(grid[0, :])
for i, energy in enumerate(energies):
graph.plot([i, i + 1], [0, 0], color=colors[i], lw=4, zorder=1)
graph.text(i + 0.5, -0.35,
rf"$({values[i]} - ({values[i+1]}))^2 = {energy}$",
ha="center", fontsize=13, color=colors[i])
graph.scatter(range(3), [0] * 3, s=900, facecolor="white",
edgecolor="#34495e", linewidth=2, zorder=3)
for i, label in enumerate("ABC"):
graph.text(i, 0, label, ha="center", va="center", fontsize=15,
zorder=4)
graph.text(i, 0.36, f"Value {values[i]}", ha="center", fontsize=13)
graph.text(1, -0.67, r"Total edge energy: $x^T L x = 4 + 1 = 5$",
ha="center", fontsize=14)
graph.set_xlim(-0.35, 2.35)
graph.set_ylim(-0.85, 0.65)
graph.axis("off")
for k in range(3):
ax = fig.add_subplot(grid[1, k])
ax.axhline(0, color="#888888", lw=0.8)
ax.plot(range(3), modes[k], "o-", lw=2.2, markersize=7,
color=colors[k])
ax.set_title(rf"Eigenvalue $\lambda={eigenvalues[k]}$", fontsize=13)
ax.set_xticks(range(3), list("ABC"))
ax.set_yticks([-2, -1, 0, 1])
ax.set_ylim(-2.4, 1.65)
ax.set_xlim(-0.15, 2.15)
ax.grid(axis="y", alpha=0.2)
if k == 0:
ax.set_ylabel("Eigenvector entry", fontsize=11)
for i, value in enumerate(modes[k]):
ax.annotate(str(value), (i, value), xytext=(0, 8),
textcoords="offset points", ha="center", fontsize=11)
fig.suptitle("Three-vertex path: edge disagreement and Laplacian modes",
fontsize=15, y=0.97)
fig.text(0.5, 0.035, "Each edge is counted once. The eigenvectors below are not normalized.",
ha="center", fontsize=11)
fig.savefig(Path.cwd() / "graph-laplacian-edge-energy.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()