codex/reduction-modulo-one-sawtooth.py

reduction-modulo-one-sawtooth.py
"""Plot fractional-part representatives; write the PNG into the current directory.

Build with ``make _media/reduction-modulo-one-sawtooth.png``.
The curve is an exact mathematical example, not measured data.
"""
from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt


def main():
    lower, upper = -2.4, 2.4
    blue, orange = "#176b91", "#a64b16"
    fig, ax = plt.subplots(figsize=(7.8, 3.1), layout="constrained")
    fig.patch.set_facecolor("white")
    ax.set_facecolor("white")
    for integer in range(-3, 3):
        left, right = max(integer, lower), min(integer + 1, upper)
        if left < right:
            ax.plot([left, right], [left - integer, right - integer],
                    color=blue, lw=2)
        if lower < integer < upper:
            ax.axvline(integer, color="0.85", ls="--", lw=0.8, zorder=0)
            ax.plot(integer, 0, "o", color=blue, ms=6, zorder=3)
        if lower < integer + 1 < upper:
            ax.plot(integer + 1, 1, "o", color=blue, markerfacecolor="white",
                    markeredgewidth=1.5, ms=6, zorder=3)
    ax.scatter([-1.2], [0.8], color=orange, s=42, zorder=4)
    ax.annotate(r"$-1.2-(-2)=0.8$", (-1.2, 0.8), (-2.25, 1.08),
                color=orange, fontsize=11,
                arrowprops={"arrowstyle": "->", "color": orange})
    ax.set(xlim=(lower, upper), ylim=(-0.08, 1.22),
           xlabel=r"Real input $x$", ylabel=r"$x-\lfloor x\rfloor$",
           title="Reduction modulo one: filled points belong; open points do not")
    ax.set_xticks(range(-2, 3))
    ax.set_yticks([0, 0.5, 1])
    ax.tick_params(labelsize=10)
    ax.grid(axis="y", alpha=0.2)
    fig.savefig(Path(__file__).with_suffix(".png").name, dpi=100,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()