codex/kuramoto-order-parameter.py

kuramoto-order-parameter.py
"""Illustrate phase centroids, including two different zero-order states.

Build: make _media/kuramoto-order-parameter.png
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Writes an opaque PNG with this script's basename to the working directory.
"""

from pathlib import Path

import matplotlib

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


def main():
    cases = [
        ("Aligned", np.full(8, np.pi / 6), "8 at one phase"),
        ("Two opposing clusters", np.array([0.0] * 4 + [np.pi] * 4), "4 at each phase"),
        ("Evenly spaced", np.arange(8) * np.pi / 4, "1 at each phase"),
    ]
    circle = np.linspace(0, 2 * np.pi, 401)
    fig, axes = plt.subplots(1, 3, figsize=(10.2, 4.0), dpi=110, facecolor="white")
    for ax, (title, phases, label) in zip(axes, cases):
        z = np.exp(1j * phases).mean()
        ax.plot(np.cos(circle), np.sin(circle), color="#64748b", lw=1.3)
        ax.axhline(0, color="#cbd5e1", lw=0.8)
        ax.axvline(0, color="#cbd5e1", lw=0.8)
        points, counts = np.unique(np.round(phases, 12), return_counts=True)
        for phase, count in zip(points, counts):
            x, y = np.cos(phase), np.sin(phase)
            ax.plot(x, y, "o", color="#2563a0", ms=6 + 1.2 * count)
        if abs(z) > 1e-12:
            ax.annotate("", xy=(z.real, z.imag), xytext=(0, 0),
                        arrowprops=dict(arrowstyle="->", color="#c13c35", lw=2.5))
        else:
            ax.plot(0, 0, "o", color="#c13c35", ms=7)
        ax.set_title(title, fontsize=12)
        ax.text(0, -1.32, label, ha="center", fontsize=10, color="#2563a0")
        ax.text(0, -1.56, rf"$r={abs(z):.0f}$", ha="center", fontsize=13, color="#c13c35")
        ax.set(xlim=(-1.25, 1.25), ylim=(-1.65, 1.3), aspect="equal")
        ax.axis("off")
    fig.suptitle("Eight phases on the unit circle; red marks their centroid", fontsize=13)
    fig.tight_layout(rect=(0, 0, 1, 0.93))
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()