{"spec_id":"bifurcation-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nbifurcation-basic: Bifurcation Diagram for Dynamical Systems\nLibrary: letsplot 4.10.1 | Python 3.13.14\nQuality: 92/100 | Updated: 2026-06-17\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    coord_cartesian,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    guides,\n    labs,\n    sampling_pick,\n    scale_color_gradient,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\nLetsPlot.setup_html()\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive chrome tokens\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID_COLOR = \"rgba(26, 26, 23, 0.15)\" if THEME == \"light\" else \"rgba(240, 239, 232, 0.15)\"\n\n# Imprint sequential colormap: brand green → blue (single-polarity continuous)\nIMPRINT_SEQ_LOW = \"#009E73\"\nIMPRINT_SEQ_HIGH = \"#4467A3\"\n\n# Data — Logistic map: x(n+1) = r * x(n) * (1 - x(n))\n# Denser sampling in the chaotic regime for richer visualization\nr_stable = np.linspace(2.5, 3.45, 600)\nr_chaotic = np.linspace(3.45, 4.0, 1600)\nr_values = np.concatenate([r_stable, r_chaotic])\ntransient = 250\niterations = 100\n\nr_all = []\nx_all = []\n\nfor r in r_values:\n    x = 0.5\n    for _ in range(transient):\n        x = r * x * (1.0 - x)\n    for _ in range(iterations):\n        x = r * x * (1.0 - x)\n        r_all.append(r)\n        x_all.append(x)\n\ndf = pd.DataFrame({\"r\": np.array(r_all), \"x\": np.array(x_all)})\n\n# Key bifurcation points with dashed guide lines\nbif_r = [3.0, 3.449, 3.544, 3.5699]\nsegments_df = pd.DataFrame({\"r\": bif_r, \"ymin\": [0.0] * 4, \"ymax\": [1.0] * 4})\n\n# Stagger labels at different y positions to avoid overlap\nlabels_df = pd.DataFrame(\n    {\n        \"r\": [3.0, 3.449, 3.58, 3.61],\n        \"x\": [0.93, 0.83, 0.73, 0.63],\n        \"label\": [\"Period-2\", \"Period-4\", \"Period-8\", \"Chaos\"],\n    }\n)\n\n# Feigenbaum constant annotation near onset of chaos\nfeigen_df = pd.DataFrame({\"r\": [3.5699], \"x\": [0.05], \"label\": [\"δ ≈ 4.669 (Feigenbaum)\"]})\n\nplot = (\n    ggplot(df, aes(x=\"r\", y=\"x\", color=\"r\"))\n    + geom_point(size=0.4, alpha=0.35, tooltips=\"none\", show_legend=False, sampling=sampling_pick(n=220000))\n    # Imprint sequential colormap for the continuous r parameter\n    + scale_color_gradient(low=IMPRINT_SEQ_LOW, high=IMPRINT_SEQ_HIGH, guide=\"none\")\n    + geom_segment(\n        aes(x=\"r\", y=\"ymin\", xend=\"r\", yend=\"ymax\"),\n        data=segments_df,\n        color=INK_SOFT,\n        size=0.3,\n        linetype=\"dashed\",\n        inherit_aes=False,\n        tooltips=\"none\",\n    )\n    + geom_text(\n        aes(x=\"r\", y=\"x\", label=\"label\"), data=labels_df, size=4, color=INK_SOFT, hjust=0.5, vjust=0, inherit_aes=False\n    )\n    + geom_text(\n        aes(x=\"r\", y=\"x\", label=\"label\"),\n        data=feigen_df,\n        size=4.5,\n        color=INK_MUTED,\n        hjust=0,\n        vjust=1,\n        fontface=\"italic\",\n        nudge_x=0.02,\n        inherit_aes=False,\n    )\n    + guides(color=\"none\")\n    + labs(\n        x=\"Growth Rate (r)\",\n        y=\"Population (x)\",\n        title=\"bifurcation-basic · python · letsplot · anyplot.ai\",\n        caption=\"Logistic map: x(n+1) = r · x(n) · (1 − x(n))\",\n    )\n    + scale_x_continuous(breaks=[2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0], expand=[0.02, 0], format=\".2f\")\n    + scale_y_continuous(breaks=[0.0, 0.2, 0.4, 0.6, 0.8, 1.0], expand=[0.02, 0], format=\".1f\")\n    + coord_cartesian(ylim=[0, 1])\n    # Canvas: ggsize(800, 450) × scale=4 → 3200×1800 px (landscape)\n    + ggsize(800, 450)\n    + theme_minimal()\n    + theme(\n        axis_text=element_text(size=10, color=INK_SOFT),\n        axis_title=element_text(size=12, color=INK),\n        plot_title=element_text(size=16, color=INK, face=\"bold\"),\n        plot_caption=element_text(size=9, color=INK_MUTED, face=\"italic\"),\n        panel_grid_major_x=element_line(color=GRID_COLOR, size=0.2),\n        panel_grid_major_y=element_line(color=GRID_COLOR, size=0.15),\n        panel_grid_minor=element_blank(),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        axis_ticks=element_line(color=INK_SOFT, size=0.3),\n        axis_line=element_line(color=INK_SOFT, size=0.4),\n        plot_margin=[30, 40, 20, 20],\n    )\n)\n\n# Save PNG (scale=4 → 3200×1800 px) and interactive HTML\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}