codexgivens-qr-row-elimination.py
givens-qr-row-elimination.py
"""Draw a two-rotation QR example; Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
Run from _media; the PNG is written to the current working directory.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
def draw_matrix(ax, entries, title, active_rows=(), target=None, zeros=()):
for row in active_rows:
ax.add_patch(Rectangle((-0.42, 1.62 - row), 1.84, 0.76,
facecolor="#e7effa", edgecolor="none"))
for row, values in enumerate(entries):
for col, value in enumerate(values):
color = "#1c7254" if (row, col) in zeros else "#172b42"
if (row, col) == target:
ax.add_patch(Rectangle((col - 0.35, 1.66 - row), 0.7, 0.68,
facecolor="#ffe0ba", edgecolor="#d57d24", lw=1.2))
ax.text(col, 2 - row, value, ha="center", va="center", fontsize=19, color=color)
for x, side in [(-0.6, 1), (1.6, -1)]:
ax.plot([x + 0.15 * side, x, x, x + 0.15 * side],
[2.48, 2.48, -0.48, -0.48], color="#172b42", lw=1.8)
ax.set(xlim=(-0.85, 1.85), ylim=(-1.2, 3.4))
ax.text(0.5, 3.04, title, ha="center", va="center", fontsize=15)
ax.axis("off")
def main():
a = np.array([[3., 0.], [4., 5.], [0., 2.]])
g1 = np.array([[0.6, 0.8, 0.], [-0.8, 0.6, 0.], [0., 0., 1.]])
t = np.hypot(3., 2.)
g2 = np.array([[1., 0., 0.], [0., 3/t, 2/t], [0., -2/t, 3/t]])
assert np.allclose(g1 @ a, [[5., 4.], [0., 3.], [0., 2.]])
assert np.allclose(g2 @ g1 @ a, [[5., 4.], [0., t], [0., 0.]])
fig, axes = plt.subplots(1, 3, figsize=(9, 4.3), dpi=120, facecolor="white")
draw_matrix(axes[0], [["3", "0"], ["4", "5"], ["0", "2"]], r"$A$",
active_rows=(0, 1), target=(1, 0), zeros=((2, 0),))
draw_matrix(axes[1], [["5", "4"], ["0", "3"], ["0", "2"]], r"$G_1A$",
active_rows=(1, 2), target=(2, 1), zeros=((1, 0), (2, 0)))
draw_matrix(axes[2], [["5", "4"], ["0", r"$\sqrt{13}$"], ["0", "0"]],
r"$R=G_2G_1A$", zeros=((1, 0), (2, 0), (2, 1)))
notes = ["Mix rows 1 and 2\nEliminate the 4", "Mix rows 2 and 3\nEliminate the 2",
"Zeros below the diagonal\nEarlier zeros survive"]
for ax, note in zip(axes, notes):
ax.text(0.5, -0.85, note, ha="center", va="center", fontsize=11, linespacing=1.5)
fig.suptitle("QR by local row rotations", fontsize=16, y=0.98)
fig.subplots_adjust(top=0.85, bottom=0.2, left=0.02, right=0.98, wspace=0.24)
for x, label in [(0.337, r"$G_1$"), (0.666, r"$G_2$")]:
fig.text(x, 0.53, r"$\longrightarrow$", fontsize=22, ha="center")
fig.text(x, 0.60, label, fontsize=12, ha="center")
fig.text(0.5, 0.055, "Blue: rows mixed next Orange: entry eliminated next Green: zero",
ha="center", fontsize=10, color="#33465b")
fig.savefig(Path.cwd() / f"{Path(__file__).stem}.png", facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()