{ "cells": [ { "cell_type": "markdown", "id": "9cfd1d06", "metadata": {}, "source": [ "# Quickstart: demographic vulnerability from a projection matrix\n", "\n", "This notebook introduces the basic workflow in `demovuln`:\n", "\n", "1. define a matrix population model;\n", "2. simulate one temporally structured perturbation;\n", "3. compute population reduction relative to the unperturbed baseline;\n", "4. evaluate a perturbation grid and compute integrated vulnerability \\(\\Phi\\).\n", "\n", "The example uses a two-stage projection matrix for clarity. Columns are source stages at time \\(t\\), and rows are destination stages at time \\(t+1\\)." ] }, { "cell_type": "code", "execution_count": null, "id": "e2341db8", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:07.517149Z", "iopub.status.busy": "2026-05-19T08:06:07.516811Z", "iopub.status.idle": "2026-05-19T08:06:07.949253Z", "shell.execute_reply": "2026-05-19T08:06:07.947984Z" } }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "from demovuln import (\n", " MatrixPopulationModel,\n", " PerturbationGrid,\n", " simulate_dynamics,\n", " run_grid,\n", ")" ] }, { "cell_type": "markdown", "id": "26034103", "metadata": {}, "source": [ "## Define a matrix population model\n", "\n", "In this example, stage 0 is a juvenile/pre-reproductive stage and stage 1 is an adult/reproductive stage. The fecundity entry is in the first row, second column." ] }, { "cell_type": "code", "execution_count": null, "id": "bf5df38e", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:07.952795Z", "iopub.status.busy": "2026-05-19T08:06:07.952365Z", "iopub.status.idle": "2026-05-19T08:06:07.959206Z", "shell.execute_reply": "2026-05-19T08:06:07.957796Z" } }, "outputs": [], "source": [ "A = np.array([\n", " [0.0, 2.0],\n", " [0.4, 0.7],\n", "])\n", "\n", "model = MatrixPopulationModel(\n", " A,\n", " adult_stages=[1],\n", " juvenile_stages=[0],\n", " name=\"two_stage_example\",\n", ")\n", "\n", "print(f\"Dominant eigenvalue: {model.lambda_:.3f}\")\n", "print(f\"Number of stages: {model.n_stages}\")\n", "print(\"Stable stage distribution:\", model.stable_distribution())" ] }, { "cell_type": "markdown", "id": "51bccc53", "metadata": {}, "source": [ "## Simulate a single perturbation regime\n", "\n", "The following simulation applies a 25% reduction to adult survival for one projection interval every three projection intervals. The final population reduction is computed relative to the unperturbed baseline trajectory at the same final time step." ] }, { "cell_type": "code", "execution_count": null, "id": "b2c5ac89", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:07.961834Z", "iopub.status.busy": "2026-05-19T08:06:07.961569Z", "iopub.status.idle": "2026-05-19T08:06:07.967547Z", "shell.execute_reply": "2026-05-19T08:06:07.966200Z" } }, "outputs": [], "source": [ "sim = simulate_dynamics(\n", " model,\n", " target=\"adult_survival\",\n", " magnitude=0.25,\n", " duration=1,\n", " period=3,\n", " t_max=50,\n", " recovery_steps=10,\n", ")\n", "\n", "print(f\"Population reduction: {sim.reduction:.2f}%\")" ] }, { "cell_type": "code", "execution_count": null, "id": "4a1199e2", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:07.969795Z", "iopub.status.busy": "2026-05-19T08:06:07.969599Z", "iopub.status.idle": "2026-05-19T08:06:08.202774Z", "shell.execute_reply": "2026-05-19T08:06:08.201209Z" } }, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(7, 4))\n", "ax.plot(sim.baseline_abundance, label=\"Unperturbed baseline\")\n", "ax.plot(sim.abundance, label=\"Perturbed dynamics\")\n", "ax.set_xlabel(\"Time step\")\n", "ax.set_ylabel(\"Relative population size\")\n", "ax.set_title(\"Population trajectory under adult-survival perturbations\")\n", "ax.legend()\n", "fig.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "955ef04d", "metadata": {}, "source": [ "## Evaluate a perturbation grid\n", "\n", "We now evaluate multiple combinations of perturbation magnitude, duration, and period. Integrated vulnerability \\(\\Phi\\) is the mean population reduction across the feasible perturbation regimes in the grid." ] }, { "cell_type": "code", "execution_count": null, "id": "6a681218", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:08.205920Z", "iopub.status.busy": "2026-05-19T08:06:08.205639Z", "iopub.status.idle": "2026-05-19T08:06:08.351528Z", "shell.execute_reply": "2026-05-19T08:06:08.349589Z" } }, "outputs": [], "source": [ "grid = PerturbationGrid(\n", " magnitudes=np.linspace(0, 1, 11),\n", " durations=[0, 1, 2, 3],\n", " periods=[1, 2, 3, 5, 10],\n", ")\n", "\n", "out = run_grid(\n", " model,\n", " target=\"adult_survival\",\n", " grid=grid,\n", " t_max=50,\n", " recovery_steps=10,\n", ")\n", "\n", "print(f\"Integrated vulnerability Φ: {out.vulnerability:.2f}%\")\n", "out.table.head()" ] }, { "cell_type": "markdown", "id": "9abc6282", "metadata": {}, "source": [ "## Compare demographic targets\n", "\n", "The same perturbation space can be applied to different demographic targets. This provides a direct comparison of vulnerability to perturbations affecting adult survival, juvenile survival, and fecundity." ] }, { "cell_type": "code", "execution_count": null, "id": "bfd8f350", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:08.354278Z", "iopub.status.busy": "2026-05-19T08:06:08.353988Z", "iopub.status.idle": "2026-05-19T08:06:08.901449Z", "shell.execute_reply": "2026-05-19T08:06:08.900224Z" } }, "outputs": [], "source": [ "summaries = []\n", "\n", "for target in [\"adult_survival\", \"juvenile_survival\", \"fecundity\", \"all\"]:\n", " result = run_grid(\n", " model,\n", " target=target,\n", " grid=grid,\n", " t_max=50,\n", " recovery_steps=10,\n", " )\n", " summaries.append({\"target\": target, \"vulnerability\": result.vulnerability})\n", "\n", "summary = pd.DataFrame(summaries)\n", "summary" ] }, { "cell_type": "code", "execution_count": null, "id": "678a1c88", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:08.904622Z", "iopub.status.busy": "2026-05-19T08:06:08.904416Z", "iopub.status.idle": "2026-05-19T08:06:09.050920Z", "shell.execute_reply": "2026-05-19T08:06:09.049648Z" } }, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(7, 4))\n", "ax.bar(summary[\"target\"], summary[\"vulnerability\"])\n", "ax.set_xlabel(\"Perturbed demographic target\")\n", "ax.set_ylabel(\"Integrated vulnerability Φ (%)\")\n", "ax.set_title(\"Vulnerability across demographic targets\")\n", "ax.tick_params(axis=\"x\", rotation=30)\n", "fig.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "3c0d2fcc", "metadata": {}, "source": [ "## Export grid results\n", "\n", "The long-format table returned by `run_grid` is convenient for downstream analyses and plotting." ] }, { "cell_type": "code", "execution_count": null, "id": "c0511597", "metadata": { "execution": { "iopub.execute_input": "2026-05-19T08:06:09.054528Z", "iopub.status.busy": "2026-05-19T08:06:09.054290Z", "iopub.status.idle": "2026-05-19T08:06:09.063376Z", "shell.execute_reply": "2026-05-19T08:06:09.061631Z" } }, "outputs": [], "source": [ "out.table.to_csv(\"quickstart_adult_survival_grid.csv\", index=False)\n", "print(\"Saved quickstart_adult_survival_grid.csv\")" ] } ], "metadata": { "kernelspec": { "display_name": "PyPI", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.4" } }, "nbformat": 4, "nbformat_minor": 5 }