{"spec_id":"phase-diagram","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nphase-diagram: Phase Diagram (State Space Plot)\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette (first series always #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data: Damped harmonic oscillator phase trajectories\nnp.random.seed(42)\n\n# System parameters for damped oscillator: d²x/dt² + 2*zeta*omega*dx/dt + omega²*x = 0\nomega = 2 * np.pi\nzeta = 0.15\n\n# Generate multiple trajectories from different initial conditions\nt = np.linspace(0, 5, 500)\ntrajectories = []\ninitial_conditions = [(2.0, 0.0), (0.0, 8.0), (-1.5, -5.0), (1.0, 4.0)]\n\nfor x0, v0 in initial_conditions:\n    # Analytical solution for underdamped oscillator\n    omega_d = omega * np.sqrt(1 - zeta**2)\n    A = np.sqrt(x0**2 + ((zeta * omega * x0 + v0) / omega_d) ** 2)\n    phi = np.arctan2(omega_d * x0, zeta * omega * x0 + v0)\n\n    # Position and velocity (derivative)\n    x = A * np.exp(-zeta * omega * t) * np.sin(omega_d * t + phi)\n    dx_dt = (\n        A\n        * np.exp(-zeta * omega * t)\n        * (-zeta * omega * np.sin(omega_d * t + phi) + omega_d * np.cos(omega_d * t + phi))\n    )\n\n    trajectories.append((x, dx_dt, f\"({x0}, {v0})\"))\n\n# Create DataFrame for seaborn\ndata = []\nfor x, dx_dt, label in trajectories:\n    for i in range(len(x)):\n        data.append({\"Position (x)\": x[i], \"Velocity (dx/dt)\": dx_dt[i], \"Initial Condition\": label, \"Time\": t[i]})\ndf = pd.DataFrame(data)\n\n# Configure seaborn theme\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9))\n\n# Plot trajectories with gradient alpha to reduce density\nsns.lineplot(\n    data=df,\n    x=\"Position (x)\",\n    y=\"Velocity (dx/dt)\",\n    hue=\"Initial Condition\",\n    palette=IMPRINT,\n    linewidth=3,\n    alpha=0.8,\n    legend=True,\n    ax=ax,\n    sort=False,\n)\n\n# Add starting points as larger markers\nfor i, (x, dx_dt, _label) in enumerate(trajectories):\n    ax.scatter(x[0], dx_dt[0], s=300, color=IMPRINT[i], zorder=5, edgecolor=PAGE_BG, linewidth=2)\n\n# Add fixed point (equilibrium at origin)\nax.scatter(0, 0, s=400, color=INK, marker=\"x\", linewidth=3.5, zorder=6, label=\"Equilibrium\")\n\n# Add direction arrows on trajectories\nfor i, (x, dx_dt, _label) in enumerate(trajectories):\n    arrow_indices = [50, 150, 300]\n    for idx in arrow_indices:\n        if idx < len(x) - 1:\n            dx = x[idx + 1] - x[idx]\n            dy = dx_dt[idx + 1] - dx_dt[idx]\n            ax.annotate(\n                \"\",\n                xy=(x[idx] + dx * 0.5, dx_dt[idx] + dy * 0.5),\n                xytext=(x[idx], dx_dt[idx]),\n                arrowprops={\"arrowstyle\": \"->\", \"color\": IMPRINT[i], \"lw\": 2},\n            )\n\n# Styling\nax.set_xlabel(\"Position (x)\", fontsize=20, color=INK)\nax.set_ylabel(\"Velocity (dx/dt)\", fontsize=20, color=INK)\nax.set_title(\"phase-diagram · seaborn · anyplot.ai\", fontsize=24, color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\n\n# Remove top and right spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\n\n# Add zero lines for reference\nax.axhline(y=0, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.4)\nax.axvline(x=0, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.4)\n\n# Adjust legend\nlegend = ax.legend(fontsize=14, loc=\"upper right\", title=\"Initial Condition\", title_fontsize=16)\nlegend.get_frame().set_facecolor(ELEVATED_BG)\nlegend.get_frame().set_edgecolor(INK_SOFT)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}