codex/row-echelon-parameter-pivots.py

row-echelon-parameter-pivots.py
"""Draw a rectangular row reduction and its two compatibility cases.

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
from matplotlib.patches import Circle, Rectangle

BLUE = '#176b9c'
GOLD = '#986900'
RED = '#b02e38'


def panel(ax, entries, title, note, pivots=(), contradiction=None):
    ax.set(xlim=(-0.85, 4.3), ylim=(-1.45, 4.45))
    ax.axis('off')
    ax.set_title(title, fontsize=15, fontweight='bold', loc='left', pad=10)
    ax.add_patch(Rectangle((1.6, -0.4), 0.8, 4.0,
                           color='#fff3cf', zorder=0))
    ax.add_patch(Rectangle((2.7, -0.4), 1.1, 4.0,
                           color='#e9f3fa', zorder=0))
    if contradiction is not None:
        ax.add_patch(Rectangle((-0.45, 2.6-contradiction), 4.25, 0.8,
                               color='#fce3e5', zorder=1))
    xs = [0, 1, 2, 3.3]
    for i, row in enumerate(entries):
        for j, entry in enumerate(row):
            color = RED if contradiction == i else '#202830'
            ax.text(xs[j], 3-i, f'${entry}$', ha='center', va='center',
                    fontsize=20, color=color, zorder=3)
    for i, j in pivots:
        ax.add_patch(Circle((xs[j], 3-i), 0.33, fill=False,
                            lw=2.2, edgecolor=RED if j == 3 else BLUE,
                            zorder=4))
    ax.plot([-0.55, -0.68, -0.68, -0.55], [-0.4, -0.4, 3.4, 3.4],
            color='#3d4650', lw=1.5)
    ax.plot([3.9, 4.03, 4.03, 3.9], [-0.4, -0.4, 3.4, 3.4],
            color='#3d4650', lw=1.5)
    ax.plot([2.6, 2.6], [-0.4, 3.4], color='#3d4650', lw=1.4)
    ax.text(2, 4.04, '$x_3$', color=GOLD, ha='center', fontsize=12)
    ax.text(3.3, 4.04, 'RHS', color=BLUE, ha='center', fontsize=11)
    ax.text(-0.65, -0.83, note, fontsize=12, va='top', linespacing=1.5)


def main():
    plt.rcParams.update({'font.family': 'DejaVu Sans', 'mathtext.fontset': 'dejavusans'})
    fig, axes = plt.subplots(2, 2, figsize=(10.8, 7.5), facecolor='white')
    fig.subplots_adjust(left=0.055, right=0.965, bottom=0.06,
                        top=0.86, wspace=0.28, hspace=0.5)
    fig.suptitle('A right-hand-side pivot decides consistency',
                 fontsize=19, fontweight='bold', y=0.98)
    fig.text(0.5, 0.917,
             r'Four equations, three unknowns; coefficient rank $=2$',
             ha='center', fontsize=13)
    panel(axes[0, 0], [['1', '2', '-1', '1'], ['2', '5', '1', '4'],
                       ['3', '7', '0', r'\tau'], ['1', '3', '2', '3']],
          r'1. Original $[A\mid b(\tau)]$',
          r'$a_{3,:}=a_{1,:}+a_{2,:}$'+'\n'+r'$a_{4,:}=a_{2,:}-a_{1,:}$')
    panel(axes[0, 1], [['1', '2', '-1', '1'], ['0', '1', '3', '2'],
                       ['0', '0', '0', r'\tau-5'], ['0', '0', '0', '0']],
          '2. Echelon form (five operations)',
          r'Compatibility requires $0=\tau-5$.', pivots=[(0, 0), (1, 1)])
    panel(axes[1, 0], [['1', '0', '-7', '-3'], ['0', '1', '3', '2'],
                       ['0', '0', '0', '0'], ['0', '0', '0', '0']],
          r'3a. $\tau=5$: full augmented RREF',
          r'$x_3=t$ is free; $x_1=-3+7t$, $x_2=2-3t$.',
          pivots=[(0, 0), (1, 1)])
    panel(axes[1, 1], [['1', '0', '-7', '0'], ['0', '1', '3', '0'],
                       ['0', '0', '0', '1'], ['0', '0', '0', '0']],
          r'3b. $\tau\ne5$: full augmented RREF',
          'The RHS pivot says $0=1$: no solution.',
          pivots=[(0, 0), (1, 1), (2, 3)], contradiction=2)
    fig.savefig(Path(__file__).with_suffix('.png').name, dpi=120,
                facecolor='white', transparent=False)
    plt.close(fig)


if __name__ == '__main__':
    main()