"""Illustrate basis pairings, vector evaluation, and lost alternating data. Save bilinear-form-pairings.png to CWD on an opaque white background. Tested with Python 3.14.4, NumPy 2.3.5, and Matplotlib 3.10.7. """ from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.patches import Rectangle import numpy as np def main(): a = np.array([[1, 2], [0, 1]]) v, w = np.array([2, 1]), np.array([-1, 3]) e1, e2 = np.eye(2, dtype=int) s, k = (a + a.T) / 2, (a - a.T) / 2 assert e1 @ a @ e2 == 2 and e2 @ a @ e1 == 0 assert v @ a @ w == 13 and w @ a @ v == -1 assert np.array_equal(s + k, a) and np.array_equal(k.T, -k) for x in (e1, e2, v, w, np.array([1, -1])): assert x @ a @ x == x @ s @ x == (x[0] + x[1]) ** 2 assert x @ k @ x == 0 blue, orange, green = "#235f9b", "#bb4e1d", "#267567" plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"}) fig, axes = plt.subplots(1, 2, figsize=(10, 6.3), facecolor="white") fig.subplots_adjust(left=0.055, right=0.975, top=0.83, bottom=0.06, wspace=0.19) for ax in axes: ax.set_xlim(0, 1) ax.set_ylim(0, 1) ax.axis("off") fig.suptitle("A bilinear form pairs two vectors", fontsize=18, y=0.97) fig.text(0.5, 0.9, r"$A_{ij}=B(e_i,e_j)$ and $B(v,w)=v^T A w$", ha="center", fontsize=15) left, right = axes left.text(0.5, 0.99, "1. Read the basis pairings", ha="center", va="top", weight="bold") x0, y0, width, height = 0.24, 0.58, 0.25, 0.125 for i in range(2): for j in range(2): x, y = x0 + j * width, y0 + (1 - i) * height active = (i, j) == (0, 1) left.add_patch(Rectangle((x, y), width, height, facecolor="#e2eef9" if active else "#f4f4f4", edgecolor="#aaaaaa", linewidth=1)) left.text(x + width / 2, y + height / 2, str(a[i, j]), ha="center", va="center", fontsize=21, color=blue if active else "#333333") for j in range(2): left.text(x0 + (j + 0.5) * width, 0.87, rf"$w=e_{j+1}$", ha="center") for i in range(2): left.text(0.205, y0 + (1.5 - i) * height, rf"$v=e_{i+1}$", ha="right", va="center") left.annotate(r"$B(e_1,e_2)=2$", xy=(0.615, 0.755), xytext=(0.5, 0.485), ha="center", color=blue, arrowprops={"arrowstyle": "->", "color": blue, "lw": 1.5}) left.text(0.5, 0.395, "2. Expand arbitrary vectors", ha="center", weight="bold") left.text(0.5, 0.315, r"$v=(2,1)^T,\quad w=(-1,3)^T$", ha="center") left.text(0.5, 0.225, r"$Aw=(5,3)^T$", ha="center", color=green, fontsize=15) left.annotate("", (0.5, 0.155), (0.5, 0.2), arrowprops={"arrowstyle": "->", "color": green}) left.text(0.5, 0.11, r"$v^T(Aw)=2\cdot5+1\cdot3=13$", ha="center", fontsize=14) left.text(0.5, 0.015, "Each coefficient weights one basis pairing.", ha="center", fontsize=11) right.text(0.5, 0.99, "3. Order matters", ha="center", va="top", weight="bold") right.text(0.5, 0.875, r"$B(e_1,e_2)=2$", ha="center", color=blue, fontsize=16) right.text(0.5, 0.78, r"$B(e_2,e_1)=0$", ha="center", color=orange, fontsize=16) right.text(0.5, 0.685, r"$B(w,v)=-1\quad\ne\quad B(v,w)=13$", ha="center", fontsize=13) right.plot([0.04, 0.96], [0.62, 0.62], color="#bbbbbb", lw=1) right.text(0.5, 0.565, "4. Self-pairings lose information", ha="center", weight="bold") right.text(0.5, 0.465, "A = S + K", ha="center", fontsize=16) right.text(0.25, 0.33, "S = [1 1]\n [1 1]", ha="center", fontfamily="monospace", fontsize=13, color=green) right.text(0.76, 0.33, "K = [ 0 1]\n [-1 0]", ha="center", fontfamily="monospace", fontsize=13, color=orange) right.text(0.5, 0.19, r"$x^T A x=x^T S x=(x_1+x_2)^2$", ha="center", fontsize=14) right.text(0.5, 0.1, r"$x^T K x=0$ for every $x$", ha="center", color=orange, fontsize=14) right.text(0.5, 0.015, "The alternating part is invisible on the diagonal.", ha="center", fontsize=11) fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=100, facecolor="white", transparent=False) plt.close(fig) if __name__ == "__main__": main()