{ "cells": [ { "cell_type": "markdown", "id": "693439e4", "metadata": {}, "source": [ "# Example 8: Model Calibration — Approximate Bayesian Computation (ABC-SMC) \n", "\n", "[(GitHub link)](https://github.com/heberlr/UQ_PhysiCell/tree/main/examples/virus-mac-new/ex8_ABC_Calib.ipynb)\n", "\n", "This notebook calibrates the [virus-mac-new](https://github.com/heberlr/UQ_PhysiCell/tree/main/examples/virus-mac-new) model using **ABC-SMC (Approximate Bayesian Computation — Sequential Monte Carlo)**. Unlike BO (ex7) which finds point estimates, ABC-SMC returns a full **posterior distribution** over parameters, quantifying how uncertain we are about each parameter given the observed data.\n", "\n", "**BO (ex7) vs ABC-SMC (ex8):**\n", "| | Bayesian Optimization (ex7) | ABC-SMC (ex8) |\n", "|---|---|---|\n", "| Output | Pareto front (point estimates) | Posterior distribution |\n", "| Tells you | Best-fit parameter values | Full uncertainty over parameters |\n", "| Requires | Acquisition function + GP | Prior distribution + distance function |\n", "| Best for | Finding optimal parameters | Uncertainty quantification |\n", "\n", "**What you will learn:**\n", "- How to define prior distributions using pyABC's `Distribution` and `RV`\n", "- How distance functions replace the objective function from BO\n", "- How ABC-SMC progressively tightens the tolerance (epsilon) across populations\n", "- How to read the posterior: width reflects identifiability, coverage of the true value reflects accuracy\n", "\n", "**Ground truth (same observed data as ex7):**\n", "- `mac_phag_rate_infected` = 1.0\n", "- `epi2infected_hfm` = 0.4" ] }, { "cell_type": "code", "execution_count": 1, "id": "a3574ae4", "metadata": {}, "outputs": [], "source": [ "import warnings, logging\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "warnings.filterwarnings('ignore')\n", "\n", "from uq_physicell.abc import CalibrationContext, run_abc_calibration\n", "from pyabc import RV, Distribution, visualization\n", "\n", "logging.basicConfig(level=logging.INFO)\n", "logger = logging.getLogger(__name__)\n", "\n", "db_path = \"ex8_ABC_Calib.db\"\n", "obs_data_path = \"ex7_ObsData.csv\"\n", "dic_real_value = {\"mac_phag_rate_infected\": 1.0, \"epi2infected_hfm\": 0.4}\n", "\n", "model_config = {\"ini_path\": \"uq_pc_struc.ini\", \"struc_name\": \"Model_struc_Calib\"}\n", "\n", "# df_cell → cell DataFrame (see ex1 for the full dispatch table)\n", "qoi_functions = {\n", " \"epi_\": lambda df_cell: len(df_cell[df_cell['cell_type'] == 'epithelial']),\n", " \"epi_infected\": lambda df_cell: len(df_cell[df_cell['cell_type'] == 'epithelial_infected']),\n", "}\n", "\n", "obs_data_columns = {\n", " \"time\": \"Time\",\n", " \"epi_\": \"Healthy Epithelial Cells\",\n", " \"epi_infected\": \"Infected Epithelial Cells\",\n", "}" ] }, { "cell_type": "markdown", "id": "69566ab1", "metadata": {}, "source": [ "## Distance functions, prior, and ABC options" ] }, { "cell_type": "code", "execution_count": 2, "id": "f7129ddc", "metadata": {}, "outputs": [], "source": [ "def euclidean_distance_epi(data1, data2):\n", " obs_vals = np.array(data1['epi_'])\n", " sim_vals = np.array(data2['epi_'])\n", " return np.sum((obs_vals - sim_vals) ** 2)\n", "\n", "def euclidean_distance_epi_infected(data1, data2):\n", " obs_vals = np.array(data1['epi_infected'])\n", " sim_vals = np.array(data2['epi_infected'])\n", " return np.sum((obs_vals - sim_vals) ** 2)\n", "\n", "distance_functions = {\n", " \"epi_\": {\"function\": euclidean_distance_epi},\n", " \"epi_infected\": {\"function\": euclidean_distance_epi_infected},\n", "}\n", "\n", "# Uniform priors over the same bounds as ex7 for direct comparison\n", "prior = Distribution(\n", " mac_phag_rate_infected=RV(\"uniform\", 0.7, 0.8), # uniform on [0.7, 1.5]\n", " epi2infected_hfm=RV(\"uniform\", 0.1, 0.4), # uniform on [0.1, 0.5]\n", ")\n", "\n", "abc_options = {\n", " \"max_populations\": 2,\n", " \"max_simulations\": 100,\n", " \"population_strategy\": \"adaptive\",\n", " \"min_population_size\": 10,\n", " \"max_population_size\": 50,\n", " \"adaptive_distance\": True,\n", " \"sampler\": \"multicore\",\n", " \"num_workers\": 6,\n", "}" ] }, { "cell_type": "markdown", "id": "8f6006a0", "metadata": {}, "source": [ "## Create context and run ABC-SMC calibration" ] }, { "cell_type": "code", "execution_count": 3, "id": "fe38de9a", "metadata": {}, "outputs": [], "source": [ "calib_context = CalibrationContext(\n", " db_path=db_path,\n", " obsData=obs_data_path,\n", " obsData_columns=obs_data_columns,\n", " model_config=model_config,\n", " qoi_functions=qoi_functions,\n", " distance_functions=distance_functions,\n", " prior=prior,\n", " abc_options=abc_options,\n", " logger=logger,\n", ")" ] }, { "cell_type": "code", "execution_count": 4, "id": "fd73d229", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "ABC.Sampler INFO: Parallelize sampling on 1 processes.\n", "INFO:ABC.Sampler:Parallelize sampling on 1 processes.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Completed — 1 populations, 158 total simulations\n" ] } ], "source": [ "history = run_abc_calibration(calib_context=calib_context)\n", "print(f\"Completed — {history.n_populations} populations, {history.total_nr_simulations} total simulations\")" ] }, { "cell_type": "markdown", "id": "f7855bf7", "metadata": {}, "source": [ "## Posterior analysis\n", "\n", "The posterior distribution shows how much each parameter value is supported by the observed data. A narrow posterior means the data is informative about that parameter; a wide posterior means it is hard to identify from this QoI alone." ] }, { "cell_type": "code", "execution_count": 5, "id": "b571d9fc", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| name | \n", "epi2infected_hfm | \n", "mac_phag_rate_infected | \n", "
|---|---|---|
| id | \n", "\n", " | \n", " |
| 2 | \n", "0.346153 | \n", "1.159550 | \n", "
| 3 | \n", "0.354043 | \n", "0.920385 | \n", "
| 4 | \n", "0.401899 | \n", "1.447023 | \n", "
| 5 | \n", "0.451707 | \n", "1.355795 | \n", "
| 6 | \n", "0.366163 | \n", "0.875740 | \n", "