GBOED method
#11
by odunbar - opened
- .cache/known_methods_snapshot.json +12 -0
- CLAUDE.md +4 -13
- data/gboed-uq_results/boed_l63_ensemble_results_2026-07-27_minimal.nc +3 -0
- data/gboed-uq_results/boed_l63_ensemble_results_2026-07-30_minimal.nc +3 -0
- data/gboed-uq_results/boed_l96_const-force_2026-07-28_minimal.nc +3 -0
- data/gboed-uq_results/boed_l96_const-force_2026-07-30_minimal.nc +3 -0
- data/gboed-uq_results/boed_l96_vec-force_2026-08-06_minimal.nc +3 -0
- src/common/method_registry.py +10 -0
- src/data_store.py +3 -0
- src/pages/MethodDetails.py +5 -0
- src/streamlit_app.py +57 -33
.cache/known_methods_snapshot.json
CHANGED
|
@@ -125,6 +125,18 @@
|
|
| 125 |
"levenberg-marquardt",
|
| 126 |
"gradient_descent"
|
| 127 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
}
|
| 129 |
},
|
| 130 |
"observed_methods": [
|
|
|
|
| 125 |
"levenberg-marquardt",
|
| 126 |
"gradient_descent"
|
| 127 |
]
|
| 128 |
+
},
|
| 129 |
+
"gboed": {
|
| 130 |
+
"abbreviation": "GBOED",
|
| 131 |
+
"Method": "Goal-oriented Bayesian Optimal Experimental Design",
|
| 132 |
+
"parallelism": "parallel-interacting",
|
| 133 |
+
"update_type": "general",
|
| 134 |
+
"method_goal": "uq",
|
| 135 |
+
"emulator_use": "within-optimize",
|
| 136 |
+
"aliases": [
|
| 137 |
+
"gboed",
|
| 138 |
+
"boed"
|
| 139 |
+
]
|
| 140 |
}
|
| 141 |
},
|
| 142 |
"observed_methods": [
|
CLAUDE.md
CHANGED
|
@@ -61,21 +61,11 @@ calibration_benchmark/
|
|
| 61 |
β βββ *.nc Kalman results (TEKI, ETKI, IEKF)
|
| 62 |
β βββ bayesian/ ABC + HM results
|
| 63 |
β βββ UKI_results/ UKI results
|
| 64 |
-
β βββ adam_results/ ADAM gradient optimizer results
|
| 65 |
-
β
|
| 66 |
-
β β benchmark tags: `l63` β L63, `l96_const-force` β L96,
|
| 67 |
-
β β `l96_vec-force` β L96_SPATIAL_FORCING (no L96_NN_FORCING yet)
|
| 68 |
-
β βββ levenberg_marquardt_results/ LM gradient optimizer results
|
| 69 |
-
β β filenames follow `leaderboard_lm_<benchmark-tag>_<date>.nc`;
|
| 70 |
-
β β same benchmark tags as adam_results/ (no L96_NN_FORCING yet)
|
| 71 |
β βββ ces-eki-dmc_results/ CES-EKI-DMC ensemble-results files (UQ source)
|
| 72 |
-
β
|
| 73 |
-
β β benchmark tags: `l63` β L63, `l96` (no suffix) β L96,
|
| 74 |
-
β β `l96_nn_forcing` β L96_NN_FORCING, `l96_spatial_forcing` β L96_SPATIAL_FORCING
|
| 75 |
β βββ gnki-uq_results/ IEKF (a.k.a GNKI) ensemble-results files (UQ source)
|
| 76 |
-
β filenames follow `leaderboard_gnki_<benchmark-tag>_<date>[_minimal].nc`;
|
| 77 |
-
β benchmark tags: `l63` β L63, `l96_const-force` β L96,
|
| 78 |
-
β `l96_flux-force` β L96_NN_FORCING, `l96_vec-force` β L96_SPATIAL_FORCING
|
| 79 |
βββ examples/ lorenz_demo.ipynb + lorenz.py (reference, not app code)
|
| 80 |
βββ From_Rob_6-10/ Raw collaborator data drop β NOT read by the app
|
| 81 |
βββ .cache/ known_methods_snapshot.json (auto-generated, safe to delete)
|
|
@@ -191,6 +181,7 @@ A quick taxonomy tree is rendered on the home page (`src/streamlit_app.py`).
|
|
| 191 |
| `ces-eki-dmc` | CES-EKI-DMC | parallel-interacting | kalman | uq | after-optimize | β |
|
| 192 |
| `adam` | ADAM | serial | gradient | optimization | none | β |
|
| 193 |
| `lm` | LM | serial | gradient | optimization | none | gradient_descent |
|
|
|
|
| 194 |
|
| 195 |
## How to extend
|
| 196 |
|
|
|
|
| 61 |
β βββ *.nc Kalman results (TEKI, ETKI, IEKF)
|
| 62 |
β βββ bayesian/ ABC + HM results
|
| 63 |
β βββ UKI_results/ UKI results
|
| 64 |
+
β βββ adam_results/ ADAM gradient optimizer results (per-benchmark files)
|
| 65 |
+
β βββ levenberg_marquardt_results/ LM gradient optimizer results (per-benchmark files)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
β βββ ces-eki-dmc_results/ CES-EKI-DMC ensemble-results files (UQ source)
|
| 67 |
+
β βββ gboed-uq_results/ GBOED ensemble-results files (UQ source)
|
|
|
|
|
|
|
| 68 |
β βββ gnki-uq_results/ IEKF (a.k.a GNKI) ensemble-results files (UQ source)
|
|
|
|
|
|
|
|
|
|
| 69 |
βββ examples/ lorenz_demo.ipynb + lorenz.py (reference, not app code)
|
| 70 |
βββ From_Rob_6-10/ Raw collaborator data drop β NOT read by the app
|
| 71 |
βββ .cache/ known_methods_snapshot.json (auto-generated, safe to delete)
|
|
|
|
| 181 |
| `ces-eki-dmc` | CES-EKI-DMC | parallel-interacting | kalman | uq | after-optimize | β |
|
| 182 |
| `adam` | ADAM | serial | gradient | optimization | none | β |
|
| 183 |
| `lm` | LM | serial | gradient | optimization | none | gradient_descent |
|
| 184 |
+
| `gboed` | GBOED | parallel-interacting | general | uq | within-optimize | boed |
|
| 185 |
|
| 186 |
## How to extend
|
| 187 |
|
data/gboed-uq_results/boed_l63_ensemble_results_2026-07-27_minimal.nc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ca32e5609af36cfd99cd14ec73ae1f2254f24da96bbf65a16dce023b5a803f76
|
| 3 |
+
size 673057
|
data/gboed-uq_results/boed_l63_ensemble_results_2026-07-30_minimal.nc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:52616c824f068f6de1eb9ccf67f56a1000b56d1cd54c911daa817df4b600e26b
|
| 3 |
+
size 673057
|
data/gboed-uq_results/boed_l96_const-force_2026-07-28_minimal.nc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:50c1939536077245886497fab8b74ffe8382d95b62a55ac7d7ccd66c0815703c
|
| 3 |
+
size 809857
|
data/gboed-uq_results/boed_l96_const-force_2026-07-30_minimal.nc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:84062bc4afda563eba840efcf06c70143037660e99f258d6ecba84538a308b78
|
| 3 |
+
size 809857
|
data/gboed-uq_results/boed_l96_vec-force_2026-08-06_minimal.nc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8cd67af4f7a347aa156718ba4a759b2ed11a6047813ed6446cef6ed6922df851
|
| 3 |
+
size 536257
|
src/common/method_registry.py
CHANGED
|
@@ -134,6 +134,15 @@ KNOWN_METHODS = {
|
|
| 134 |
"emulator_use": "none",
|
| 135 |
"aliases": ["lm", "levenberg_marquardt", "levenberg-marquardt", "gradient_descent"],
|
| 136 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
|
|
@@ -142,6 +151,7 @@ KNOWN_METHODS = {
|
|
| 142 |
_METHOD_PALETTE = [
|
| 143 |
"#4c78a8", "#f58518", "#e45756", "#72b7b2", "#54a24b",
|
| 144 |
"#eeca3b", "#b279a2", "#ff9da6", "#9d755d", "#bab0ac",
|
|
|
|
| 145 |
]
|
| 146 |
|
| 147 |
# Stable abbreviation β hex color mapping. Import this wherever Altair charts are built
|
|
|
|
| 134 |
"emulator_use": "none",
|
| 135 |
"aliases": ["lm", "levenberg_marquardt", "levenberg-marquardt", "gradient_descent"],
|
| 136 |
},
|
| 137 |
+
"gboed": {
|
| 138 |
+
"abbreviation": "GBOED",
|
| 139 |
+
"Method": "Goal-oriented Bayesian Optimal Experimental Design",
|
| 140 |
+
"parallelism": "parallel-interacting",
|
| 141 |
+
"update_type": "general",
|
| 142 |
+
"method_goal": "uq",
|
| 143 |
+
"emulator_use": "within-optimize",
|
| 144 |
+
"aliases": ["gboed", "boed"],
|
| 145 |
+
},
|
| 146 |
}
|
| 147 |
|
| 148 |
|
|
|
|
| 151 |
_METHOD_PALETTE = [
|
| 152 |
"#4c78a8", "#f58518", "#e45756", "#72b7b2", "#54a24b",
|
| 153 |
"#eeca3b", "#b279a2", "#ff9da6", "#9d755d", "#bab0ac",
|
| 154 |
+
"#5254a3", "#8ca252",
|
| 155 |
]
|
| 156 |
|
| 157 |
# Stable abbreviation β hex color mapping. Import this wherever Altair charts are built
|
src/data_store.py
CHANGED
|
@@ -93,6 +93,7 @@ UQ_BUDGET_FILES: dict[str, list[tuple[str, str]]] = {
|
|
| 93 |
("ces-eki-const", "ces-eki-const_results/ces-eki-const_l63_ensemble_results_2026-07-09_minimal.nc"),
|
| 94 |
("ces-iekf-const", "ces-iekf-const_results/ces-iekf-const_l63_ensemble_results_2026-07-17_minimal.nc"),
|
| 95 |
("hm", "history-matching-uq_results/history-matching_l63_ensemble_results_2026-07-20_minimal.nc"),
|
|
|
|
| 96 |
],
|
| 97 |
"L96": [
|
| 98 |
("ces-eki-dmc", "ces-eki-dmc_results/ces-eki-dmc_l96_ensemble_results_2026-06-15_minimal.nc"),
|
|
@@ -100,6 +101,7 @@ UQ_BUDGET_FILES: dict[str, list[tuple[str, str]]] = {
|
|
| 100 |
("ces-eki-const", "ces-eki-const_results/ces-eki-const_l96_ensemble_results_2026-07-09_minimal.nc"),
|
| 101 |
("ces-iekf-const", "ces-iekf-const_results/ces-iekf-const_l96_ensemble_results_2026-07-17_minimal.nc"),
|
| 102 |
("hm", "history-matching-uq_results/history-matching_l96_const-force_2026-07-20_minimal.nc"),
|
|
|
|
| 103 |
],
|
| 104 |
"L96_NN_FORCING": [
|
| 105 |
("ces-eki-dmc", "ces-eki-dmc_results/ces-eki-dmc_l96_nn_forcing_ensemble_results_2026-06-15_minimal.nc"),
|
|
@@ -114,6 +116,7 @@ UQ_BUDGET_FILES: dict[str, list[tuple[str, str]]] = {
|
|
| 114 |
("ces-eki-const", "ces-eki-const_results/ces-eki-const_l96_spatial_forcing_ensemble_results_2026-07-09_minimal.nc"),
|
| 115 |
("ces-iekf-const", "ces-iekf-const_results/ces-iekf-const_l96_spatial_forcing_ensemble_results_2026-07-17_minimal.nc"),
|
| 116 |
("hm", "history-matching-uq_results/history-matching_l96_vec-force_2026-07-22_minimal.nc"),
|
|
|
|
| 117 |
],
|
| 118 |
}
|
| 119 |
|
|
|
|
| 93 |
("ces-eki-const", "ces-eki-const_results/ces-eki-const_l63_ensemble_results_2026-07-09_minimal.nc"),
|
| 94 |
("ces-iekf-const", "ces-iekf-const_results/ces-iekf-const_l63_ensemble_results_2026-07-17_minimal.nc"),
|
| 95 |
("hm", "history-matching-uq_results/history-matching_l63_ensemble_results_2026-07-20_minimal.nc"),
|
| 96 |
+
("gboed", "gboed-uq_results/boed_l63_ensemble_results_2026-07-30_minimal.nc"),
|
| 97 |
],
|
| 98 |
"L96": [
|
| 99 |
("ces-eki-dmc", "ces-eki-dmc_results/ces-eki-dmc_l96_ensemble_results_2026-06-15_minimal.nc"),
|
|
|
|
| 101 |
("ces-eki-const", "ces-eki-const_results/ces-eki-const_l96_ensemble_results_2026-07-09_minimal.nc"),
|
| 102 |
("ces-iekf-const", "ces-iekf-const_results/ces-iekf-const_l96_ensemble_results_2026-07-17_minimal.nc"),
|
| 103 |
("hm", "history-matching-uq_results/history-matching_l96_const-force_2026-07-20_minimal.nc"),
|
| 104 |
+
("gboed", "gboed-uq_results/boed_l96_const-force_2026-07-30_minimal.nc"),
|
| 105 |
],
|
| 106 |
"L96_NN_FORCING": [
|
| 107 |
("ces-eki-dmc", "ces-eki-dmc_results/ces-eki-dmc_l96_nn_forcing_ensemble_results_2026-06-15_minimal.nc"),
|
|
|
|
| 116 |
("ces-eki-const", "ces-eki-const_results/ces-eki-const_l96_spatial_forcing_ensemble_results_2026-07-09_minimal.nc"),
|
| 117 |
("ces-iekf-const", "ces-iekf-const_results/ces-iekf-const_l96_spatial_forcing_ensemble_results_2026-07-17_minimal.nc"),
|
| 118 |
("hm", "history-matching-uq_results/history-matching_l96_vec-force_2026-07-22_minimal.nc"),
|
| 119 |
+
("gboed", "gboed-uq_results/boed_l96_vec-force_2026-08-06_minimal.nc"),
|
| 120 |
],
|
| 121 |
}
|
| 122 |
|
src/pages/MethodDetails.py
CHANGED
|
@@ -94,6 +94,11 @@ method_meta = {
|
|
| 94 |
"url": "https://doi.org/10.1090/qam/10666",
|
| 95 |
"summary": "Levenberg-Marquardt β damped least-squares algorithm that interpolates between gradient descent and Gauss-Newton steps for efficient nonlinear least-squares minimization.",
|
| 96 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
}
|
| 98 |
|
| 99 |
# Selection UI (defaults to query param if valid)
|
|
|
|
| 94 |
"url": "https://doi.org/10.1090/qam/10666",
|
| 95 |
"summary": "Levenberg-Marquardt β damped least-squares algorithm that interpolates between gradient descent and Gauss-Newton steps for efficient nonlinear least-squares minimization.",
|
| 96 |
},
|
| 97 |
+
"GBOED": {
|
| 98 |
+
"citation": "Holthuijzen et al. 2026",
|
| 99 |
+
"url": "https://arxiv.org/abs/2508.13071",
|
| 100 |
+
"summary": "Goal-oriented Bayesian Optimal Experimental Design β selects each batch of forward-model evaluations by maximizing an expected-information-gain criterion targeted at the quantity of interest, refitting the surrogate used by the criterion at every iteration.",
|
| 101 |
+
},
|
| 102 |
}
|
| 103 |
|
| 104 |
# Selection UI (defaults to query param if valid)
|
src/streamlit_app.py
CHANGED
|
@@ -1,6 +1,17 @@
|
|
| 1 |
-
import
|
| 2 |
from pathlib import Path
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
st.set_page_config(page_title="Calibration Benchmark", page_icon="π ", layout="wide")
|
| 5 |
|
| 6 |
# Sidebar navigation
|
|
@@ -88,38 +99,51 @@ st.markdown(
|
|
| 88 |
"a surrogate model. See the **π Methods** page for citations and per-method "
|
| 89 |
"performance charts."
|
| 90 |
)
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
)
|
| 124 |
st.caption(
|
| 125 |
"Note: Kalman methods are Bayesian in spirit too (they're approximate Gaussian "
|
|
|
|
| 1 |
+
import sys
|
| 2 |
from pathlib import Path
|
| 3 |
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import streamlit as st
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
from common.method_registry import KNOWN_METHODS
|
| 9 |
+
from common.leaderboard import _UPDATE_TYPE_ORDER
|
| 10 |
+
except ModuleNotFoundError:
|
| 11 |
+
sys.path.append(str(Path(__file__).resolve().parent))
|
| 12 |
+
from common.method_registry import KNOWN_METHODS
|
| 13 |
+
from common.leaderboard import _UPDATE_TYPE_ORDER
|
| 14 |
+
|
| 15 |
st.set_page_config(page_title="Calibration Benchmark", page_icon="π ", layout="wide")
|
| 16 |
|
| 17 |
# Sidebar navigation
|
|
|
|
| 99 |
"a surrogate model. See the **π Methods** page for citations and per-method "
|
| 100 |
"performance charts."
|
| 101 |
)
|
| 102 |
+
_taxonomy_rows = [
|
| 103 |
+
{
|
| 104 |
+
"Algorithm": info["abbreviation"],
|
| 105 |
+
"Method Goal": info["method_goal"],
|
| 106 |
+
"Update Type": info["update_type"],
|
| 107 |
+
"Parallelism": info["parallelism"],
|
| 108 |
+
"Emulator Use": info["emulator_use"],
|
| 109 |
+
"_update_type_order": _UPDATE_TYPE_ORDER.get(info["update_type"], 99),
|
| 110 |
+
}
|
| 111 |
+
for info in KNOWN_METHODS.values()
|
| 112 |
+
]
|
| 113 |
+
_taxonomy_df = (
|
| 114 |
+
pd.DataFrame(_taxonomy_rows)
|
| 115 |
+
.sort_values(["_update_type_order", "Algorithm"])
|
| 116 |
+
.drop(columns="_update_type_order")
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
_UPDATE_TYPE_ROW_COLOR = {
|
| 120 |
+
"kalman": "#d9f2e6", # pastel green
|
| 121 |
+
"gradient": "#dbe9fa", # pastel blue
|
| 122 |
+
"general": "#fde2c8", # pastel orange
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def _style_taxonomy_rows(df: pd.DataFrame) -> pd.DataFrame:
|
| 127 |
+
result = pd.DataFrame("", index=df.index, columns=df.columns)
|
| 128 |
+
for row in df.index:
|
| 129 |
+
bg = _UPDATE_TYPE_ROW_COLOR.get(df.loc[row, "Update Type"], "")
|
| 130 |
+
if bg:
|
| 131 |
+
result.loc[row, :] = f"background-color: {bg}; color: #212529"
|
| 132 |
+
return result
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
st.dataframe(
|
| 136 |
+
_taxonomy_df.style.apply(_style_taxonomy_rows, axis=None),
|
| 137 |
+
hide_index=True,
|
| 138 |
+
use_container_width=True,
|
| 139 |
+
height=int((len(_taxonomy_df) + 1) * 35.2 + 3),
|
| 140 |
+
column_config={
|
| 141 |
+
"Algorithm": st.column_config.TextColumn("Algorithm"),
|
| 142 |
+
"Method Goal": st.column_config.TextColumn("Method Goal"),
|
| 143 |
+
"Update Type": st.column_config.TextColumn("Update Type"),
|
| 144 |
+
"Parallelism": st.column_config.TextColumn("Parallelism"),
|
| 145 |
+
"Emulator Use": st.column_config.TextColumn("Emulator Use"),
|
| 146 |
+
},
|
| 147 |
)
|
| 148 |
st.caption(
|
| 149 |
"Note: Kalman methods are Bayesian in spirit too (they're approximate Gaussian "
|