codexmatrix-rank-column-plane.py
matrix-rank-column-plane.py
"""Draw original column-space geometry; tested with Python 3.14 and Matplotlib 3.10."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.lines import Line2D
from matplotlib.patches import Patch
def main():
plt.rcParams.update({"font.size": 12, "font.family": "DejaVu Sans"})
fig = plt.figure(figsize=(10, 6.4), dpi=100, facecolor="white")
ax = fig.add_axes((0.02, 0.09, 0.69, 0.80), projection="3d")
fig.suptitle("Three columns, two independent output directions", fontsize=19, y=0.97)
u, v = np.meshgrid(np.linspace(-0.25, 3.35, 2), np.linspace(-0.4, 2.4, 2))
ax.plot_surface(u, 2 * u, v, color="#85b9d6", alpha=0.24, shade=False)
columns = np.array([[1, 2, 1], [2, 4, 1], [3, 6, 2]], dtype=float)
colors = ["#176b9b", "#ba6e16", "#6b4196"]
offsets = [(0.02, -0.2, 0.15), (0.08, 0.05, -0.2), (-0.04, 0.10, 0.12)]
for index, (column, color, offset) in enumerate(zip(columns, colors, offsets), 1):
ax.quiver(0, 0, 0, *column, color=color, linewidth=2.8, arrow_length_ratio=0.08)
ax.text(*(column + offset), rf"$c_{index}$", color=color, fontsize=16)
for start in columns[:2]:
ax.plot(*np.stack([start, columns[2]]).T, color="#6b4196", linestyle="--", linewidth=1.4)
b = np.array([1.0, 2.0, 0.0])
impossible = np.array([1.0, 3.0, 0.0])
foot = np.array([1.4, 2.8, 0.0])
ax.scatter(*b, color="#238452", s=70, depthshade=False)
ax.scatter(*impossible, color="#c53636", marker="x", linewidths=2.5, s=80, depthshade=False)
ax.plot(*np.stack([foot, impossible]).T, color="#c53636", linestyle=":", linewidth=2)
ax.text(0.86, 1.75, -0.23, r"$b$", color="#238452", fontsize=15)
ax.text(0.94, 3.30, 0.0, r"$b'$", color="#c53636", fontsize=15)
ax.scatter(0, 0, 0, color="#333333", s=18, depthshade=False)
ax.set(xlim=(-0.4, 3.6), ylim=(-0.8, 7.2), zlim=(-0.5, 2.6))
ax.set_xlabel(r"$b_1$", labelpad=8)
ax.set_ylabel(r"$b_2$", labelpad=8)
ax.set_zlabel(r"$b_3$", labelpad=7)
ax.set_xticks([0, 1, 2, 3])
ax.set_yticks([0, 2, 4, 6])
ax.set_zticks([0, 1, 2])
ax.view_init(elev=24, azim=-54)
ax.set_box_aspect((4, 6, 3.2))
for axis in (ax.xaxis, ax.yaxis, ax.zaxis):
axis.pane.set_facecolor("white")
fig.text(0.73, 0.80, r"$c_1=(1,2,1)^T$", color=colors[0], fontsize=15)
fig.text(0.73, 0.74, r"$c_2=(2,4,1)^T$", color=colors[1], fontsize=15)
fig.text(0.73, 0.68, r"$c_3=c_1+c_2$", color=colors[2], fontsize=16)
fig.text(0.73, 0.55, r"Image plane: $b_2=2b_1$", fontsize=14)
fig.text(0.73, 0.48, "Rank = 2", fontsize=17)
fig.text(0.73, 0.36, r"$b=(1,2,0)^T$", color="#238452", fontsize=15)
fig.text(0.73, 0.31, r"$b=-c_1+c_2$", color="#238452", fontsize=14)
fig.text(0.73, 0.20, r"$b'=(1,3,0)^T$", color="#c53636", fontsize=15)
fig.text(0.73, 0.15, r"$3\ne 2\cdot1$: unreachable", color="#c53636", fontsize=12)
fig.legend(handles=[Patch(facecolor="#85b9d6", alpha=0.5, label="Column space"),
Line2D([0], [0], color="#6b4196", linestyle="--", label="Column addition")],
loc="lower left", bbox_to_anchor=(0.10, 0.015), frameon=False, ncol=2)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()