diff --git a/docs/notebooks/hawkdovemulti-adjust/hdm_convergence_runlength.html b/docs/notebooks/hawkdovemulti-adjust/hdm_convergence_runlength.html new file mode 100644 index 0000000..ea812d3 --- /dev/null +++ b/docs/notebooks/hawkdovemulti-adjust/hdm_convergence_runlength.html @@ -0,0 +1,9138 @@ + + + + + +hdm_convergence_runlength + + + + + + + + + + + + +
+
+ +
+ +
+ + +
+
+ +
+ + +
+ + +
+ +
+ + +
+
+ +
+ + +
+ + +
+
+ +
+ + +
+ + +
+ + +
+ + +
+
+ +
+ + +
+ + +
+ + +
+ + +
+
+ +
+ +
+
+ +
+ + +
+
+ +
+ + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + diff --git a/docs/notebooks/hawkdovemulti-adjust/index.html b/docs/notebooks/hawkdovemulti-adjust/index.html index fab45de..fbca97a 100644 --- a/docs/notebooks/hawkdovemulti-adjust/index.html +++ b/docs/notebooks/hawkdovemulti-adjust/index.html @@ -164,7 +164,7 @@

📊 Available Notebook Snapshots

📊Initial Risk Attitude - Distributions & Population Outcomes + Distributions & Population Outcomes
Analysis of final population category and simulation run @@ -177,7 +177,7 @@

📊 Available Notebook Snapshots

- ⏱️Convergence & run length + ⏱️Convergence & run length
Investigation of simulation run lengths and convergence. @@ -187,6 +187,20 @@

📊 Available Notebook Snapshots

>
+
+
+ ⏱️Convergence & risk adjustment +
+
+ Analysis of simulation run length, convergence, and risk adjustment strategy. +
+ View Notebook +
+ + +
🔬Parameter Analysis diff --git a/notebooks/new_convergence/hdm_convergence_runlength.ipynb b/notebooks/new_convergence/hdm_convergence_runlength.ipynb index 0701bc6..448fb91 100644 --- a/notebooks/new_convergence/hdm_convergence_runlength.ipynb +++ b/notebooks/new_convergence/hdm_convergence_runlength.ipynb @@ -9,24 +9,34 @@ "\n", "- how long does hawk/dove multi risk attitude take to converge with the new logic?\n", " - how many do not converge ?\n", - "- what difference does it make if we use adapt or average risk adjustment strategy?" + "- what difference does it make if we use adapt or average risk adjustment strategy?\n", + "\n", + "\n", + "--- \n", + "Most recent analysis based on data generated with this command:\n", + "\n", + "```sh\n", + "./simulatingrisk/hawkdovemulti/batch_run.py --params risk_adjust\n", + "```\n", + "\n", + "Ran some tests with more iterations; when 200 iterations were specified, the percent of simulations that converged were closer to 85%, but all batches tested were in the 80% range." ] }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 218, "id": "4edfc5bc-ab4c-435b-8c0c-593d560a1445", "metadata": {}, "outputs": [], "source": [ "import polars as pl\n", "\n", - "df = pl.scan_csv(\"../../data/hawkdovemulti/riskadjust/dist-uniform/*.csv\").collect()" + "df = pl.read_csv(\"../../data/hawkdovemulti/riskadjust/dist-uniform/2025-08-05T153548_956082_model.csv\")" ] }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 219, "id": "cc794dde-f8d5-4bc6-9eb5-059741196600", "metadata": {}, "outputs": [ @@ -34,7 +44,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Analyzing 1000 runs\n" + "Analyzing 600 runs\n" ] } ], @@ -56,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 220, "id": "eaa053a9-2567-41e7-9c8a-0bc76c4c05c2", "metadata": {}, "outputs": [ @@ -70,28 +80,28 @@ " white-space: pre-wrap;\n", "}\n", "\n", - "shape: (9, 2)
statisticvalue
strf64
"count"1000.0
"null_count"0.0
"mean"2904.7
"std"2131.600767
"min"60.0
"25%"750.0
"50%"3500.0
"75%"5500.0
"max"5500.0
" + "shape: (9, 2)
statisticvalue
strf64
"count"600.0
"null_count"0.0
"mean"278.398333
"std"363.777872
"min"50.0
"25%"61.0
"50%"101.0
"75%"200.0
"max"1000.0
" ], "text/plain": [ "shape: (9, 2)\n", - "┌────────────┬─────────────┐\n", - "│ statistic ┆ value │\n", - "│ --- ┆ --- │\n", - "│ str ┆ f64 │\n", - "╞════════════╪═════════════╡\n", - "│ count ┆ 1000.0 │\n", - "│ null_count ┆ 0.0 │\n", - "│ mean ┆ 2904.7 │\n", - "│ std ┆ 2131.600767 │\n", - "│ min ┆ 60.0 │\n", - "│ 25% ┆ 750.0 │\n", - "│ 50% ┆ 3500.0 │\n", - "│ 75% ┆ 5500.0 │\n", - "│ max ┆ 5500.0 │\n", - "└────────────┴─────────────┘" + "┌────────────┬────────────┐\n", + "│ statistic ┆ value │\n", + "│ --- ┆ --- │\n", + "│ str ┆ f64 │\n", + "╞════════════╪════════════╡\n", + "│ count ┆ 600.0 │\n", + "│ null_count ┆ 0.0 │\n", + "│ mean ┆ 278.398333 │\n", + "│ std ┆ 363.777872 │\n", + "│ min ┆ 50.0 │\n", + "│ 25% ┆ 61.0 │\n", + "│ 50% ┆ 101.0 │\n", + "│ 75% ┆ 200.0 │\n", + "│ max ┆ 1000.0 │\n", + "└────────────┴────────────┘" ] }, - "execution_count": 145, + "execution_count": 220, "metadata": {}, "output_type": "execute_result" } @@ -102,95 +112,88 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 221, "id": "31aa7cd7-a9ab-408a-9266-218238c79e01", "metadata": {}, "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, { "data": { - "application/vnd.holoviews_exec.v0+json": "", "text/html": [ - "
\n", - "
\n", - "
\n", - "" + "\n", + "
\n", + "" ], "text/plain": [ - ":Histogram [Step] (Step_count)" + "alt.Chart(...)" ] }, - "execution_count": 146, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p2751" - } - }, + "execution_count": 221, + "metadata": {}, "output_type": "execute_result" } ], @@ -200,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 222, "id": "97a6df2a-d8dc-4f61-b356-a0e52571d93f", "metadata": {}, "outputs": [], @@ -212,95 +215,88 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 223, "id": "9371939e-2223-40b3-9989-4172a69741f6", "metadata": {}, "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, { "data": { - "application/vnd.holoviews_exec.v0+json": "", "text/html": [ - "
\n", - "
\n", - "
\n", - "" + "\n", + "
\n", + "" ], "text/plain": [ - ":Histogram [Step] (Step_count)" + "alt.Chart(...)" ] }, - "execution_count": 148, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p2817" - } - }, + "execution_count": 223, + "metadata": {}, "output_type": "execute_result" } ], @@ -318,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 224, "id": "2a819ce2-eb8b-40bf-8a0c-7509989b4401", "metadata": {}, "outputs": [ @@ -332,7 +328,7 @@ " white-space: pre-wrap;\n", "}\n", "\n", - "shape: (2, 2)
statuscount
stru32
"running"858
"converged"142
" + "shape: (2, 2)
statuscount
stru32
"converged"481
"running"119
" ], "text/plain": [ "shape: (2, 2)\n", @@ -341,12 +337,12 @@ "│ --- ┆ --- │\n", "│ str ┆ u32 │\n", "╞═══════════╪═══════╡\n", - "│ running ┆ 858 │\n", - "│ converged ┆ 142 │\n", + "│ converged ┆ 481 │\n", + "│ running ┆ 119 │\n", "└───────────┴───────┘" ] }, - "execution_count": 149, + "execution_count": 224, "metadata": {}, "output_type": "execute_result" } @@ -358,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 225, "id": "0c121e06-1d76-4a26-8bd4-50ede7c558e3", "metadata": {}, "outputs": [ @@ -366,7 +362,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "142 runs out of 1000; 14.20% complete\n" + "481 runs out of 600; 80.17% complete\n" ] } ], @@ -388,7 +384,7 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 226, "id": "1dfbf4b2-1525-4f00-9a83-93a49b72e085", "metadata": {}, "outputs": [ @@ -396,8 +392,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "adopt: 140 rows\n", - "average: 2 rows\n" + "adopt: 251 rows\n", + "average: 230 rows\n" ] } ], @@ -435,17 +431,17 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 246, "id": "484c7785-b067-4253-8a33-2c66c86a05ff", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "TtestResult(statistic=array([0.58333333]), pvalue=array([0.66381736]), df=array([1]))" + "TtestResult(statistic=array([0.30599364]), pvalue=array([0.75988749]), df=array([229]))" ] }, - "execution_count": 152, + "execution_count": 246, "metadata": {}, "output_type": "execute_result" } @@ -459,105 +455,101 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 243, "id": "3f420203-f683-40bf-8ffa-9767d6cbdc09", "metadata": {}, "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, { "data": { - "application/vnd.holoviews_exec.v0+json": "", "text/html": [ - "
\n", - "
\n", - "
\n", - "" + "\n", + "
\n", + "" ], "text/plain": [ - ":BoxWhisker [risk_adjustment] (Step)" + "alt.Chart(...)" ] }, - "execution_count": 153, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p2883" - } - }, + "execution_count": 243, + "metadata": {}, "output_type": "execute_result" } ], "source": [ - "df_riskadjust.plot.box(\"Step\", by='risk_adjustment')" + "import altair as alt\n", + "\n", + "alt.Chart(df_riskadjust).mark_boxplot().encode(\n", + " x=alt.X('pct_risk_inclined', title='% risk inclined'), y=alt.Y('risk_adjustment', title=\"Adjustment\"))" ] }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 229, "id": "2872f0c6-85aa-476a-8297-7a664266c42b", "metadata": {}, "outputs": [ @@ -566,28 +558,29 @@ "text/html": [ "\n", "\n", - "
\n", + "
\n", "" ], "text/plain": [ "alt.HConcatChart(...)" ] }, - "execution_count": 154, + "execution_count": 229, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from simulatingrisk.hawkdovemulti import analysis_utils\n", - "import importlib\n", - "importlib.reload(analysis_utils)\n", "\n", "# df_adopt, df_average\n", "\n", @@ -659,169 +651,915 @@ ] }, { - "cell_type": "code", - "execution_count": 155, - "id": "2242207f-0d44-492c-83ba-3c4f6beb892b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(858, 23)" - ] - }, - "execution_count": 155, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "not_converged = df.filter(pl.col(\"status\") == \"running\")\n", - "not_converged.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 158, - "id": "c7937841-63b8-484d-9061-da3462d49c18", + "cell_type": "markdown", + "id": "3db0444d-cb97-452f-8170-cb3ed807d06a", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "shape: (9, 2)
statisticvalue
strf64
"count"858.0
"null_count"0.0
"mean"10.850816
"std"7.569892
"min"1.0
"25%"4.0
"50%"10.0
"75%"16.0
"max"35.0
" - ], - "text/plain": [ - "shape: (9, 2)\n", - "┌────────────┬───────────┐\n", - "│ statistic ┆ value │\n", - "│ --- ┆ --- │\n", - "│ str ┆ f64 │\n", - "╞════════════╪═══════════╡\n", - "│ count ┆ 858.0 │\n", - "│ null_count ┆ 0.0 │\n", - "│ mean ┆ 10.850816 │\n", - "│ std ┆ 7.569892 │\n", - "│ min ┆ 1.0 │\n", - "│ 25% ┆ 4.0 │\n", - "│ 50% ┆ 10.0 │\n", - "│ 75% ┆ 16.0 │\n", - "│ max ┆ 35.0 │\n", - "└────────────┴───────────┘" - ] - }, - "execution_count": 158, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "not_converged[\"num_agents_risk_changed\"].describe()" + "## Simulations that converged" ] }, { "cell_type": "code", - "execution_count": 160, - "id": "9dff6e0a-54f8-460a-bb7e-859d67017e04", + "execution_count": 230, + "id": "c2e98e75-6699-408b-a835-c127ad83c271", "metadata": {}, "outputs": [ - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, { "data": { - "application/vnd.holoviews_exec.v0+json": "", "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":BoxWhisker (num_agents_risk_changed)" - ] - }, - "execution_count": 160, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "p3032" - } - }, - "output_type": "execute_result" - } - ], - "source": [ - "not_converged.plot.box(\"num_agents_risk_changed\")" + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.FacetChart(...)" + ] + }, + "execution_count": 230, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# filter to status = running\n", + "converged = converged.with_columns(\n", + " pct_agents_risk_changed=pl.col(\"num_agents_risk_changed\").truediv(pl.col(\"total_agents\")),\n", + " seven_pct_pop=pl.col(\"total_agents\").mul(0.07)\n", + ")\n", + "\n", + "alt.Chart(converged).mark_boxplot().encode(\n", + " x=alt.X('num_agents_risk_changed', title='# Agents that adjusted risk attitude'), \n", + " y=alt.Y('risk_adjustment', title='Adjustment')).facet('grid_size')" + ] + }, + { + "cell_type": "code", + "execution_count": 231, + "id": "5e49a7c6-6e54-457e-a087-481364051f32", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.FacetChart(...)" + ] + }, + "execution_count": 231, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "alt.Chart(converged).mark_boxplot().encode(\n", + " x=alt.X('pct_agents_risk_changed', title='% of Agents that adjusted risk attitude'), \n", + " y=alt.Y('risk_adjustment', title='Adjustment')).facet('grid_size')" + ] + }, + { + "cell_type": "code", + "execution_count": 232, + "id": "b7d48b2f-1365-4132-9641-09c0998af4e3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.FacetChart(...)" + ] + }, + "execution_count": 232, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "converg_pop_boxplot = alt.Chart(converged).mark_boxplot().encode(\n", + " x=alt.X('sum_risk_level_changes', title='Total risk attitude changes'), \n", + " y=alt.Y('risk_adjustment', title='Adjustment'))\n", + "\n", + "converg_pop_boxplot.facet('grid_size')" + ] + }, + { + "cell_type": "code", + "execution_count": 233, + "id": "d12da2d9-d673-416a-bdee-9a6c9ccfdf65", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.FacetChart(...)" + ] + }, + "execution_count": 233, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "converg_threshold = alt.Chart(converged).mark_point(color=\"orange\").encode(\n", + " x=\"seven_pct_pop\",\n", + " y=alt.Y('risk_adjustment', title='Adjustment')\n", + ")\n", + "(converg_pop_boxplot + converg_threshold).facet('grid_size')" + ] + }, + { + "cell_type": "markdown", + "id": "8e15e456-cc28-45d4-8c6e-b9ef41e9c9bb", + "metadata": {}, + "source": [ + "## Simulations that did not converge" + ] + }, + { + "cell_type": "code", + "execution_count": 234, + "id": "2242207f-0d44-492c-83ba-3c4f6beb892b", + "metadata": {}, + "outputs": [], + "source": [ + "# filter to status = running\n", + "not_converged = df.filter(pl.col(\"status\") == \"running\").with_columns(\n", + " pct_agents_risk_changed=pl.col(\"num_agents_risk_changed\").truediv(pl.col(\"total_agents\")),\n", + " seven_pct_pop=pl.col(\"total_agents\").mul(0.07)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1ce0aed4-4c32-454b-b0b8-6d3b02d034cb", + "metadata": {}, + "source": [ + "How many simulations with each adjustment type did not converge?\n" + ] + }, + { + "cell_type": "code", + "execution_count": 235, + "id": "1d2b6726-e1b6-45a9-ab09-24cba63365e5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.Chart(...)" + ] + }, + "execution_count": 235, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "alt.Chart(not_converged).mark_bar().encode(\n", + " y=alt.Y('risk_adjustment', title=\"Adjustment\"), x='count(RunId)')" + ] + }, + { + "cell_type": "markdown", + "id": "381b999a-e8c7-46c9-8daf-2586f056d932", + "metadata": {}, + "source": [ + "How many agents adjusted on the last adjustment round?" + ] + }, + { + "cell_type": "code", + "execution_count": 236, + "id": "9dff6e0a-54f8-460a-bb7e-859d67017e04", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.Chart(...)" + ] + }, + "execution_count": 236, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "alt.Chart(not_converged).mark_boxplot().encode(\n", + " x=alt.X('num_agents_risk_changed', title='# Agents that adjusted risk attitude'), \n", + " y=alt.Y('risk_adjustment', title='Adjustment'))" + ] + }, + { + "cell_type": "code", + "execution_count": 237, + "id": "e5fb0fe1-90c8-4558-b647-d142ecb5b8d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.Chart(...)" + ] + }, + "execution_count": 237, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "alt.Chart(not_converged).mark_boxplot().encode(\n", + " x=alt.X('pct_agents_risk_changed', title='% of Agents that adjusted risk attitude'), \n", + " y=alt.Y('risk_adjustment', title='Adjustment'))" + ] + }, + { + "cell_type": "markdown", + "id": "85be9ac6-0e11-4866-882a-bb60a1afe566", + "metadata": {}, + "source": [ + "What about total population adjustments?\n" + ] + }, + { + "cell_type": "code", + "execution_count": 238, + "id": "c1cb6d78-b5df-40a3-89e3-23376bff3e32", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.Chart(...)" + ] + }, + "execution_count": 238, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nonconverg_pop_boxplot = alt.Chart(not_converged).mark_boxplot().encode(\n", + " x=alt.X('sum_risk_level_changes', title='Total risk attitude changes'), \n", + " y=alt.Y('risk_adjustment', title='Adjustment'))\n", + "\n", + "nonconverg_pop_boxplot" + ] + }, + { + "cell_type": "markdown", + "id": "200fee55-2d84-4fc9-aed9-d1599176e332", + "metadata": {}, + "source": [ + "Facet by grid size, and add a marker (orange circle) to indicate the threshold (based on 7% of total population size)." + ] + }, + { + "cell_type": "code", + "execution_count": 239, + "id": "60667865-a6e1-46c5-8ed2-dcf58fcfd129", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.FacetChart(...)" + ] + }, + "execution_count": 239, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "converg_threshold = alt.Chart(not_converged).mark_point(color=\"orange\").encode(\n", + " x=\"seven_pct_pop\",\n", + " y=alt.Y('risk_adjustment', title='Adjustment')\n", + ")\n", + "(nonconverg_pop_boxplot + converg_threshold).facet('grid_size')" ] } ], @@ -841,7 +1579,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.1" + "version": "3.12.7" } }, "nbformat": 4, diff --git a/simulatingrisk/hawkdovemulti/README.md b/simulatingrisk/hawkdovemulti/README.md index a6bac8f..4115c83 100644 --- a/simulatingrisk/hawkdovemulti/README.md +++ b/simulatingrisk/hawkdovemulti/README.md @@ -23,16 +23,31 @@ instead of choosing based on the rules of the game. ## Convergence The model is configured to stop automatically when it has stabilized. -Convergence is reached when an adjustment round occurs and zero agents -adjust their risk attitude. + +Model and agent data collection reports on whether agents updated their +risk level in the last adjustment round, and model data collection +includes a status of "running" or "converged". + +### With Adjustment + +When adjustment is enabled (adopt / average), convergence is checked +after the simulation has run for a minimum of 50 rounds. The simulation +is considered stable when individual agents are no longer adjusting risk attitudes, +or when the number of agents in each risk attitude are relatively stable +(e.g., agents are swapping risk attitudes but the overall total for each +category is stable). + +Convergence is reached when an adjustment round occurs and _either_: + +- _zero_ agents adjust their risk attitude +- the total changes of agents per risk attitudes is less than 7% of the population size + +### Without Adjustment If adjustment is not enabled, convergence logic falls back to the -implementation of the hawk/dove single-risk attitude simulation, which is -based on a stable rolling % average of agents playing hawk. +implementation of the hawk/dove single-risk attitude simulation. In this case, +convergence is based on a stable rolling % average of agents playing hawk. -Model and agent data collection also includes reports on whether agents -updated their risk level in the last adjustment round, and model data collection -includes a status of "running" or "converged". ## Batch running diff --git a/simulatingrisk/hawkdovemulti/analysis_utils.py b/simulatingrisk/hawkdovemulti/analysis_utils.py index fafe12e..618be1e 100644 --- a/simulatingrisk/hawkdovemulti/analysis_utils.py +++ b/simulatingrisk/hawkdovemulti/analysis_utils.py @@ -1,6 +1,7 @@ """ utility methods for analyzing data collected generated by this model """ + import altair as alt import polars as pl @@ -25,7 +26,9 @@ def groupby_population_risk_category(df): poprisk_grouped = poprisk_grouped.with_columns( pl.Series( name="type", - values=poprisk_grouped["risk_category"].map_elements(RiskState.category), + values=poprisk_grouped["risk_category"].map_elements( + RiskState.category, return_type=pl.datatypes.String + ), ) ) return poprisk_grouped diff --git a/simulatingrisk/hawkdovemulti/batch_run.py b/simulatingrisk/hawkdovemulti/batch_run.py index 6ae0a46..0082b29 100755 --- a/simulatingrisk/hawkdovemulti/batch_run.py +++ b/simulatingrisk/hawkdovemulti/batch_run.py @@ -2,17 +2,15 @@ import argparse import csv -from datetime import datetime import multiprocessing import os +from datetime import datetime +from mesa.batchrunner import _collect_data, _make_model_kwargs from tqdm.auto import tqdm -from mesa.batchrunner import _make_model_kwargs, _collect_data - from simulatingrisk.hawkdovemulti.model import HawkDoveMultipleRiskModel - neighborhood_sizes = list(HawkDoveMultipleRiskModel.neighborhood_sizes) # NOTE: it's better to be explicit about even parameters @@ -40,7 +38,7 @@ "risk_adjustment": ["adopt", "average"], "risk_distribution": "uniform", # use model defaults; grid size must be specified - "grid_size": 10, # 25, + "grid_size": [5, 10, 25], }, "payoff": { "adjust_payoff": HawkDoveMultipleRiskModel.supported_adjust_payoffs, diff --git a/simulatingrisk/hawkdovemulti/model.py b/simulatingrisk/hawkdovemulti/model.py index 83ae5ec..3003704 100644 --- a/simulatingrisk/hawkdovemulti/model.py +++ b/simulatingrisk/hawkdovemulti/model.py @@ -3,8 +3,7 @@ from enum import IntEnum from functools import cached_property - -from simulatingrisk.hawkdove.model import HawkDoveModel, HawkDoveAgent +from simulatingrisk.hawkdove.model import HawkDoveAgent, HawkDoveModel class HawkDoveMultipleRiskAgent(HawkDoveAgent): @@ -193,7 +192,7 @@ def __init__( if risk_distribution not in self.risk_distribution_options: raise ValueError( f"Unsupported risk distribution '{risk_distribution}'; " - + f"must be one of { ', '.join(self.risk_distribution_options) }" + + f"must be one of {', '.join(self.risk_distribution_options)}" ) # make sure risk adjustment is valid @@ -350,10 +349,10 @@ def converged(self): # this simulation typically takes around 1000 rounds to converge, # so don't even bother checking until at least 50 rounds - return ( - self.schedule.steps > max(self.adjust_round_n, 50) - # TODO: determine value (% of population?) and/or make configurable - and (self.num_agents_risk_changed == 0 or self.sum_risk_level_changes == 6) + return self.schedule.steps > max(self.adjust_round_n, 50) and ( + self.num_agents_risk_changed == 0 + # NOTE: could adjust the threshold here + or self.sum_risk_level_changes <= len(self.schedule.agents) * 0.07 ) @cached_property @@ -376,8 +375,8 @@ def sum_risk_level_changes(self): # for each risk level, calculate the absolute difference for rlevel, total in a.items(): changes[rlevel] = abs(total - b[rlevel]) + return sum([val for val in changes.values()]) - # return sum([abs(a[rlevel] - b[rlevel]) for rlevel in a.keys()]) def __getattr__(self, attr): # support dynamic properties for data collection on total by risk level