codex/row-echelon-affine-solution-line.py

row-echelon-affine-solution-line.py
"""Show the solution line and the single free coordinate of a rank-two system.

Tested with Python 3.14, matplotlib 3.10.7 and NumPy 2.3.5.
Run from _media; the opaque PNG is written to the current directory.
"""
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({'font.family': 'DejaVu Sans', 'font.size': 12,
                         'mathtext.fontset': 'dejavusans'})
    x0 = np.array([-3, 2, 0])
    v = np.array([7, -3, 1])
    t = np.linspace(-0.5, 1.25, 150)
    affine = x0[:, None] + v[:, None]*t
    kernel = v[:, None]*t
    fig = plt.figure(figsize=(10.2, 4.9), facecolor='white')
    fig.suptitle('One free coordinate traces a whole solution line',
                 fontsize=18, fontweight='bold', y=0.98)
    ax = fig.add_subplot(121, projection='3d')
    ax.plot(*affine, color='#176b9c', lw=3,
            label=r'$x_0+t v$: $Ax=(1,4,5,3)^T$')
    ax.plot(*kernel, color='#75818b', lw=2, linestyle='--',
            label=r'$t v$: $Ax=0$')
    for point, label, offset in [(x0, r'$x_0$', (-1, 0.7, 0.08)),
                                 (x0+v, r'$x_0+v$', (-1.8, 0, 0.18))]:
        ax.scatter(*point, color='#176b9c', s=40, depthshade=False)
        ax.text(*(point+np.array(offset)), label, fontsize=11)
    ax.scatter(0, 0, 0, color='#75818b', s=25)
    ax.quiver(0, 0, 0, *x0, color='#b88010', lw=2,
              arrow_length_ratio=0.12)
    ax.text2D(0.04, -0.12, r'Gold arrow: translate by $x_0$',
              transform=ax.transAxes, color='#986900', fontsize=11)
    ax.set(xlabel='$x_1$', ylabel='$x_2$', zlabel='$x_3=t$',
           xlim=(-7, 9), ylim=(-4, 4), zlim=(-0.6, 1.6))
    ax.view_init(elev=21, azim=-61)
    ax.set_box_aspect((1.25, 1, 0.9))
    ax.tick_params(labelsize=9)
    ax.legend(loc='upper left', bbox_to_anchor=(-0.03, 1.05), fontsize=10,
              frameon=False)
    ax2 = fig.add_subplot(122)
    for values, color, label in zip(affine, ['#176b9c', '#b88010', '#6e4aa0'],
                                    [r'$x_1=-3+7t$', r'$x_2=2-3t$', r'$x_3=t$']):
        ax2.plot(t, values, color=color, lw=2.4, label=label)
        for tt in [0, 1]:
            ax2.scatter(tt, np.interp(tt, t, values), color=color, s=30, zorder=3)
    ax2.axvline(0, color='#aeb7be', lw=0.8, zorder=0)
    ax2.axvline(1, color='#aeb7be', lw=0.8, linestyle=':', zorder=0)
    ax2.axhline(0, color='#aeb7be', lw=0.8, zorder=0)
    ax2.set(xlabel='Free choice $t$', ylabel='Coordinate value', xlim=(-0.5, 1.25),
            ylim=(-7, 7), title='Every choice gives exactly one solution')
    ax2.legend(loc='upper left', fontsize=11, frameon=False)
    ax2.grid(alpha=0.16)
    ax2.spines[['top', 'right']].set_visible(False)
    fig.subplots_adjust(left=0.02, right=0.97, bottom=0.2, top=0.8, wspace=0.28)
    fig.text(0.5, 0.05,
             r'$x_0=(-3,2,0)^T,\quad v=(7,-3,1)^T,\quad Ax_0=(1,4,5,3)^T,\quad Av=0$',
             ha='center', fontsize=13)
    fig.savefig(Path(__file__).with_suffix('.png').name, dpi=120,
                facecolor='white', transparent=False)
    plt.close(fig)


if __name__ == '__main__':
    main()