yx21e commited on
Commit
80ef3b2
·
verified ·
1 Parent(s): 4d9bc8c

Initial FireWx-FM artifact release

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +6 -0
  2. LICENSE +9 -0
  3. README.md +146 -0
  4. artifacts/manifests/paper_outputs.sha256 +20 -0
  5. artifacts/manifests/paper_outputs.yml +60 -0
  6. artifacts/results/fireprone_contract_progression_summary.json +0 -0
  7. artifacts/results/fireprone_contract_progression_table.generated.tex +69 -0
  8. artifacts/results/selection_regret_all_backbones_20260504.csv +25 -0
  9. artifacts/results/selection_regret_all_backbones_20260504.json +0 -0
  10. artifacts/results/selection_regret_full_head_table.generated.tex +2 -0
  11. artifacts/results/selection_regret_head_metrics.csv +241 -0
  12. artifacts/results/selection_regret_main_table.generated.tex +24 -0
  13. artifacts/results/selection_regret_per_seed.csv +121 -0
  14. artifacts/results/selection_regret_rq2_figure_values.csv +12 -0
  15. artifacts/results/selection_regret_scope_sweep_20260505.csv +45 -0
  16. artifacts/results/selection_regret_scope_sweep_20260505.generated.tex +24 -0
  17. artifacts/results/selection_regret_scope_sweep_20260505.json +0 -0
  18. artifacts/results/selection_regret_summary.csv +25 -0
  19. artifacts/results/selection_regret_tolerance_family_table.generated.tex +2 -0
  20. data_sources/DATA_SOURCES.md +27 -0
  21. docs/artifact_map.md +56 -0
  22. docs/huggingface_release_design.md +16 -0
  23. experiments/README.md +25 -0
  24. experiments/raw_reference/run_selection_regret_scope_sweep_20260505.py +335 -0
  25. experiments/raw_reference/task_scripts/run_all_backbone_selection_regret_20260504.py +656 -0
  26. experiments/raw_reference/task_scripts/run_analog_extended_retrieval_sweep_seeded.py +333 -0
  27. experiments/raw_reference/task_scripts/run_event_analog_taskmodel_seeded.py +350 -0
  28. experiments/raw_reference/task_scripts/run_extreme_heat_alphaearth_suite_seeded.py +344 -0
  29. experiments/raw_reference/task_scripts/run_final_area_taskmodel_seeded.py +353 -0
  30. experiments/raw_reference/task_scripts/run_smoke_pm25_alphaearth_suite_seeded.py +306 -0
  31. experiments/raw_reference/task_scripts/run_smoke_pm25_attached_fm_suite_seeded.py +231 -0
  32. experiments/raw_reference/task_scripts/summarize_forced_meanstd_20260429.py +232 -0
  33. experiments/slurm/submit_template.sbatch +13 -0
  34. paper_outputs/figures/fig_fireprone_contract_progression_compact.pdf +262 -0
  35. paper_outputs/figures/fig_selection_regret_rq2.tikz +120 -0
  36. paper_outputs/figures/fig_task_contract_tiles.pdf +0 -0
  37. paper_outputs/figures/fig_task_rank_map.pdf +348 -0
  38. paper_outputs/figures/matching.pdf +0 -0
  39. paper_outputs/tables/tab_app_analog_rank_depth.tex +24 -0
  40. paper_outputs/tables/tab_app_burned_area_median_acre.tex +24 -0
  41. paper_outputs/tables/tab_app_contract_params_full.tex +22 -0
  42. paper_outputs/tables/tab_app_head_architectures.tex +36 -0
  43. paper_outputs/tables/tab_app_heat_event_pr.tex +24 -0
  44. paper_outputs/tables/tab_app_matching_rule_params.tex +17 -0
  45. paper_outputs/tables/tab_app_occupancy_ppr_scope.tex +27 -0
  46. paper_outputs/tables/tab_app_scope_params.tex +19 -0
  47. paper_outputs/tables/tab_app_seed_robustness.tex +36 -0
  48. paper_outputs/tables/tab_app_smoke_high_event.tex +24 -0
  49. paper_outputs/tables/tab_app_spread_ap_by_scope.tex +24 -0
  50. paper_outputs/tables/tab_appendix_selection_regret_tolerance.tex +37 -0
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ .DS_Store
4
+ .ipynb_checkpoints/
5
+ *.log
6
+ tmp/
LICENSE ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ This release is provided for scholarly reproducibility of the associated paper.
2
+
3
+ Code files are released under the MIT License.
4
+ Paper-output tables, figures, and bundled summary artifacts are released for
5
+ non-commercial research and review use with attribution.
6
+
7
+ Raw data from NOAA, NASA, LANDFIRE, Wildfire Risk to Communities, LandScan,
8
+ WFIGS, MTBS, and model providers are not redistributed here. Users must obtain
9
+ those data from the original providers and comply with their terms.
README.md ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - wildfire
5
+ - geospatial
6
+ - earth-observation
7
+ - foundation-models
8
+ - evaluation
9
+ - reproducibility
10
+ - paper-artifact
11
+ pretty_name: FireWx-FM and Wildfire Evaluation Contracts
12
+ ---
13
+
14
+ # FireWx-FM and Wildfire Evaluation Contracts
15
+
16
+ This repository contains the public code and lightweight artifacts for the
17
+ paper *Does Your Wildfire Prediction Model Actually Work, or Just Score Well?*
18
+
19
+ The release has two parts:
20
+
21
+ - **FireWx-FM reference backbone artifacts.** FireWx-FM is a wildfire-specialized
22
+ reference model used as an in-domain comparator in the paper.
23
+ - **Fixed-contract evaluation artifacts.** The paper evaluates wildfire transfer
24
+ under fixed task, metric, matching-rule, scope, and head-family choices.
25
+
26
+ This is a paper-artifact repository. It includes scripts, compact summary files,
27
+ paper tables, and paper figures needed to inspect and reproduce reported
28
+ outputs. It does **not** redistribute raw weather, fire, fuel, exposure,
29
+ incident, perimeter, feature-cache, or private model files.
30
+
31
+ ## Key Results in the Release
32
+
33
+ The bundled paper outputs reproduce the main results reported in the manuscript.
34
+
35
+ | Check | Paper artifact | What it shows |
36
+ |---|---|---|
37
+ | Matching-rule sensitivity (RQ1) | `paper_outputs/figures/fig_fireprone_contract_progression_compact.pdf` | The same occupancy outputs can move sharply from exact to tolerated to union \(F_1\), especially under fire-prone scopes. |
38
+ | Fixed-feature head selection (RQ2) | `paper_outputs/figures/fig_selection_regret_rq2.tikz` | Selecting a head by ranking evidence can lose decision performance relative to direct decision selection. |
39
+ | Same-contract transfer matrix (RQ3) | `paper_outputs/tables/tab_primary_results.tex` | FireWx-FM, Prithvi-WxC, Aurora, ClimaX, StormCast, DLWP, FCN, FengWu, FuXi, Pangu-Weather, and AlphaEarth are compared under fixed occupancy and spread contracts. |
40
+ | Supporting task forms (RQ4) | `paper_outputs/tables/tab_supporting_results.tex` and `paper_outputs/figures/fig_task_rank_map.pdf` | Backbone ranking changes across burned area, analog retrieval, smoke PM2.5, and extreme heat task forms. |
41
+
42
+ Selected displayed values from the paper:
43
+
44
+ - FireWx-FM reference occupancy union \(F_1\): `59.0656 ± 2.7372`.
45
+ - ClimaX occupancy union \(F_1\): `60.1506 ± 7.5865`.
46
+ - FireWx-FM fire-spread AP: `30.0900 ± 1.2500`.
47
+ - FireWx-FM smoke PM2.5 RMSE: `4.4646 ± 0.0060`.
48
+ - AlphaEarth smoke PM2.5 RMSE: `4.4403 ± 0.0488`.
49
+
50
+ Values are stored as TeX table cells under `paper_outputs/tables/`. The
51
+ corresponding compact CSV/JSON summaries are under `artifacts/results/`.
52
+
53
+ ## Repository Layout
54
+
55
+ ```text
56
+ artifacts/
57
+ manifests/ table/figure provenance metadata and SHA-256 hashes
58
+ results/ compact CSV/JSON summaries used by paper outputs
59
+ data_sources/ source list and download notes for raw data
60
+ docs/ artifact map and Hugging Face release notes
61
+ experiments/ sanitized raw-rerun scripts and Slurm templates
62
+ paper_outputs/
63
+ figures/ PDF/TikZ figures used by the manuscript
64
+ tables/ TeX table blocks used by the manuscript
65
+ scripts/ release rebuild and audit scripts
66
+ ```
67
+
68
+ ## Quick Reproduction
69
+
70
+ The paper-output path uses only the Python standard library.
71
+
72
+ ```bash
73
+ python3 scripts/reproduce_paper_outputs.py
74
+ ```
75
+
76
+ This command:
77
+
78
+ - rebuilds the RQ1 fire-prone progression figure from summary JSON;
79
+ - rebuilds the RQ2 selection-regret TikZ figure from CSV;
80
+ - rebuilds the RQ4 rank-map PDF from the released main tables;
81
+ - checks SHA-256 hashes for all final paper outputs;
82
+ - audits that stale labels, local paths, and incomplete placeholders are absent.
83
+
84
+ Expected terminal tail:
85
+
86
+ ```text
87
+ Paper-output checksum check passed.
88
+ Release audit passed.
89
+ Rebuilt reproducible outputs and passed release audit.
90
+ ```
91
+
92
+ ## What Is Included
93
+
94
+ - Final paper table TeX files under `paper_outputs/tables/`.
95
+ - Final paper figures under `paper_outputs/figures/`.
96
+ - Small released CSV/JSON summary artifacts under `artifacts/results/`.
97
+ - Builder scripts for reproducible paper-output figures under `scripts/`.
98
+ - Sanitized raw-rerun reference scripts under `experiments/raw_reference/`.
99
+ - Data-source documentation under `data_sources/DATA_SOURCES.md`.
100
+ - A table/figure provenance map under `docs/artifact_map.md`.
101
+
102
+ ## What Is Not Included
103
+
104
+ Raw data are not bundled. The paper uses public or provider-hosted resources,
105
+ including NOAA HRRR, NASA FIRMS, LANDFIRE, Wildfire Risk to Communities,
106
+ LandScan, WFIGS, MTBS, and external Earth-FM/backbone sources.
107
+
108
+ See `data_sources/DATA_SOURCES.md` for the role of each source and public access
109
+ entry points. Full raw-data reruns require users to obtain those sources
110
+ independently and rebuild local feature caches.
111
+
112
+ ## Reproducibility Scope
113
+
114
+ There are two levels of reproducibility:
115
+
116
+ 1. **Paper-output reproduction from bundled artifacts.** This is lightweight and
117
+ does not require raw data, GPUs, or Slurm. It verifies the exact files used by
118
+ the manuscript.
119
+ 2. **Raw-data reruns.** These require separately downloaded source data, local
120
+ preprocessing, model dependencies, and compute resources. The repository
121
+ provides sanitized scripts and Slurm templates, but not the raw inputs.
122
+
123
+ ## Intended Use
124
+
125
+ Use this repository to inspect paper values, reproduce released figures from
126
+ summary artifacts, audit table/figure provenance, or adapt the fixed-contract
127
+ evaluation workflow for wildfire transfer studies.
128
+
129
+ Do not use this repository as a raw dataset mirror. Do not treat the included
130
+ summary artifacts as a substitute for the original data sources.
131
+
132
+ ## Citation
133
+
134
+ If you use this release, please cite:
135
+
136
+ ```bibtex
137
+ @misc{wildfire_fm_evaluation_contracts_2026,
138
+ title = {Does Your Wildfire Prediction Model Actually Work, or Just Score Well?},
139
+ author = {Anonymous},
140
+ year = {2026},
141
+ note = {FireWx-FM and fixed-contract wildfire evaluation code and artifacts}
142
+ }
143
+ ```
144
+
145
+ The BibTeX entry will be updated with arXiv metadata after the preprint is
146
+ public.
artifacts/manifests/paper_outputs.sha256 ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ b369d13e0419fa8272ccdc994b6642f3b141248a879c030218e387c583537eb2 paper_outputs/figures/fig_fireprone_contract_progression_compact.pdf
2
+ b2e56403e2774c457dd12c4685e2dc7492e22e32df46fcc5c37b3087110f2439 paper_outputs/figures/fig_selection_regret_rq2.tikz
3
+ bc4d35ad9cb4c1f9ba8f31c7c340d9684c9dd2d55f5a2e60604a2b58b90cbe40 paper_outputs/figures/fig_task_contract_tiles.pdf
4
+ c382f5d69f25cc2f5db174601a33d0fd0928b44910a2a4b1c131954bd42113d9 paper_outputs/figures/fig_task_rank_map.pdf
5
+ 015ab951b0af5c130e4894092a5dd0bb0fd62e710467163a9df8246d8cf369f4 paper_outputs/figures/matching.pdf
6
+ e8abbd2668517f5cae14933ed943fe103e74132886b0ff48ecd1685978549504 paper_outputs/tables/tab_app_analog_rank_depth.tex
7
+ 81db28aace3366625f1cfd5935892eb5af672d5ecd8327e6dcba00b7b04e2b3c paper_outputs/tables/tab_app_burned_area_median_acre.tex
8
+ 4a93401ef355c02eb0cc6b2e9a1506f9ed9d912301ec6829581247e40991bdfb paper_outputs/tables/tab_app_contract_params_full.tex
9
+ 3c5398c28e6243b1784b27d2e9eab1a5c60e6e6d2cfd14a79aa6fd1e0499b871 paper_outputs/tables/tab_app_head_architectures.tex
10
+ f740b8f076490e852efa88fa8180ca08bb6b12901ff3ec3687c7e5c0b236da4e paper_outputs/tables/tab_app_heat_event_pr.tex
11
+ 86e97a394ceae8cc6eafd6d1021b44d13a117378ead87bfee662cc90a1e0e54b paper_outputs/tables/tab_app_matching_rule_params.tex
12
+ 0b1ad4587dd440fdabf771000b1c971daa9222e946a3404c9beae10dd7ea67c6 paper_outputs/tables/tab_app_occupancy_ppr_scope.tex
13
+ 4e79672c28a938cd9ba1bc0e423e7169eca389251a22357aff6fe84d3cbfa889 paper_outputs/tables/tab_app_scope_params.tex
14
+ 6850ee131e203f66392c79f17f59214672b362274f42285b252b83ac0ede1eb3 paper_outputs/tables/tab_app_seed_robustness.tex
15
+ 1ca91ca451f846e59cb62ea64a616780c698b9dee80918a05467bd6c40df2dd5 paper_outputs/tables/tab_app_smoke_high_event.tex
16
+ cd65372622e8dd388adb1122a3e93b22d2090fba836405b08a078d5159b182de paper_outputs/tables/tab_app_spread_ap_by_scope.tex
17
+ a31d4a4e0f2f1c7f90a5610acea77aef5a48e63c754ab2159a42473dce2c3b94 paper_outputs/tables/tab_appendix_selection_regret_tolerance.tex
18
+ 22614e90568cc562c023c540bdfdec14c0923ecf55d432fffa2619625b856092 paper_outputs/tables/tab_fireprone_contract_progression.tex
19
+ 6672c62a150d83a351f4fa23ac04537d9aaae01af6056f689437d9b7d8bcee40 paper_outputs/tables/tab_primary_results.tex
20
+ 717555b2584658c936aa8fc27b63f1068dc5f796a297bcef0576cf020b3ddaf8 paper_outputs/tables/tab_supporting_results.tex
artifacts/manifests/paper_outputs.yml ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ figures:
2
+ fig:toy_occupancy_contract:
3
+ output: paper_outputs/figures/matching.pdf
4
+ type: static_vector
5
+ fig:task_contract_tiles:
6
+ output: paper_outputs/figures/fig_task_contract_tiles.pdf
7
+ type: static_vector
8
+ fig:selection_regret_diagnostic:
9
+ output: paper_outputs/figures/fig_selection_regret_rq2.tikz
10
+ builder: scripts/build_selection_regret_rq2_figure.py
11
+ inputs:
12
+ - artifacts/results/selection_regret_scope_sweep_20260505.csv
13
+ fig:fireprone_contract_progression:
14
+ output: paper_outputs/figures/fig_fireprone_contract_progression_compact.pdf
15
+ builder: scripts/build_fireprone_contract_progression_figure.py
16
+ inputs:
17
+ - artifacts/results/fireprone_contract_progression_summary.json
18
+ fig:task_comparator_normalized_map:
19
+ output: paper_outputs/figures/fig_task_rank_map.pdf
20
+ builder: scripts/build_task_rank_map.py
21
+ inputs:
22
+ - paper_outputs/tables/tab_primary_results.tex
23
+ - paper_outputs/tables/tab_supporting_results.tex
24
+ tables:
25
+ tab:primary_results:
26
+ output: paper_outputs/tables/tab_primary_results.tex
27
+ tab:supporting_results:
28
+ output: paper_outputs/tables/tab_supporting_results.tex
29
+ tab:app_matching_rule_params:
30
+ output: paper_outputs/tables/tab_app_matching_rule_params.tex
31
+ tab:app_contract_params_full:
32
+ output: paper_outputs/tables/tab_app_contract_params_full.tex
33
+ tab:app_scope_params:
34
+ output: paper_outputs/tables/tab_app_scope_params.tex
35
+ tab:fireprone_contract_progression:
36
+ output: paper_outputs/tables/tab_fireprone_contract_progression.tex
37
+ inputs:
38
+ - artifacts/results/fireprone_contract_progression_summary.json
39
+ tab:appendix_selection_regret_tolerance:
40
+ output: paper_outputs/tables/tab_appendix_selection_regret_tolerance.tex
41
+ inputs:
42
+ - artifacts/results/selection_regret_all_backbones_20260504.csv
43
+ tab:app_occupancy_ppr_scope:
44
+ output: paper_outputs/tables/tab_app_occupancy_ppr_scope.tex
45
+ inputs:
46
+ - artifacts/results/fireprone_contract_progression_summary.json
47
+ tab:app_spread_ap_by_scope:
48
+ output: paper_outputs/tables/tab_app_spread_ap_by_scope.tex
49
+ tab:app_burned_area_median_acre:
50
+ output: paper_outputs/tables/tab_app_burned_area_median_acre.tex
51
+ tab:app_analog_rank_depth:
52
+ output: paper_outputs/tables/tab_app_analog_rank_depth.tex
53
+ tab:app_smoke_high_event:
54
+ output: paper_outputs/tables/tab_app_smoke_high_event.tex
55
+ tab:app_heat_event_pr:
56
+ output: paper_outputs/tables/tab_app_heat_event_pr.tex
57
+ tab:app_seed_robustness:
58
+ output: paper_outputs/tables/tab_app_seed_robustness.tex
59
+ tab:app_head_architectures:
60
+ output: paper_outputs/tables/tab_app_head_architectures.tex
artifacts/results/fireprone_contract_progression_summary.json ADDED
The diff for this file is too large to render. See raw diff
 
artifacts/results/fireprone_contract_progression_table.generated.tex ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{4pt}
5
+ \caption{Occupancy scores across global and fire-prone scopes. Global uses the full validation/test domain; top-\(k\) rows use train-defined fire-prone masks from historical fire frequency. Values are \(F_1\) percentages from the same validation-selected strict threshold. Tolerance is spatial-only; union adds temporal and spatial matching. Difference is union minus strict. Rows report five-seed mean with small std. Values use four decimals.}
6
+ \label{tab:fireprone_contract_progression}
7
+ \begin{adjustbox}{max width=\textwidth}
8
+ \begin{tabular}{@{}llcccc@{}}
9
+ \toprule
10
+ Backbone & Scope & Strict \(F_1\uparrow\) & Tolerance \(F_1\uparrow\) & Union \(F_1\uparrow\) & Difference \(\uparrow\) \\
11
+ \midrule
12
+ \textcolor{blue}{FireWx-FM ref.} & global & \ms{0.4550}{0.1410} & \ms{29.7480}{1.2870} & \ms{59.0660}{2.7370} & \ms{58.6110}{2.6950} \\
13
+ & top 5\% & \ms{3.5600}{0.8810} & \ms{39.2620}{1.4010} & \ms{72.8280}{2.5780} & \ms{69.2680}{1.9960} \\
14
+ & top 10\% & \ms{3.5580}{0.8800} & \ms{39.1660}{1.3910} & \ms{72.5200}{2.5670} & \ms{68.9630}{1.9890} \\
15
+ & top 20\% & \ms{3.5300}{0.8700} & \ms{38.2850}{1.2950} & \ms{69.7230}{2.4660} & \ms{66.1930}{1.9270} \\
16
+ \addlinespace[1pt]
17
+ Prithvi-WxC & global & \ms{0.0550}{0.0040} & \ms{7.1600}{0.6600} & \ms{20.1900}{1.8300} & \ms{20.1300}{1.8300} \\
18
+ & top 5\% & \ms{1.4100}{1.1600} & \ms{19.2600}{4.5000} & \ms{42.5800}{4.5500} & \ms{41.1700}{3.4800} \\
19
+ & top 10\% & \ms{1.2400}{1.3200} & \ms{14.8800}{8.4400} & \ms{32.6900}{13.2100} & \ms{31.4500}{11.9100} \\
20
+ & top 20\% & \ms{1.1500}{1.3800} & \ms{13.1500}{9.4600} & \ms{28.1300}{15.2900} & \ms{26.9800}{13.9200} \\
21
+ \addlinespace[1pt]
22
+ Aurora & global & \ms{0.0700}{0.0100} & \ms{8.5000}{1.9600} & \ms{23.1000}{4.9400} & \ms{23.0400}{4.9300} \\
23
+ & top 5\% & \ms{0.9900}{0.9300} & \ms{15.1300}{6.0800} & \ms{35.4800}{11.0200} & \ms{34.5000}{10.3700} \\
24
+ & top 10\% & \ms{0.7800}{1.0500} & \ms{12.7400}{6.5600} & \ms{30.5300}{10.8800} & \ms{29.7500}{9.8700} \\
25
+ & top 20\% & \ms{0.6700}{1.1000} & \ms{10.5300}{7.4300} & \ms{24.9400}{12.5800} & \ms{24.2800}{11.4900} \\
26
+ \addlinespace[1pt]
27
+ ClimaX & global & \ms{0.3500}{0.0800} & \ms{29.7500}{3.6100} & \ms{60.1500}{7.5900} & \ms{59.8000}{7.5500} \\
28
+ & top 5\% & \ms{1.2900}{0.1100} & \ms{34.5800}{2.3800} & \ms{69.2200}{5.7200} & \ms{67.9200}{5.7300} \\
29
+ & top 10\% & \ms{1.2500}{0.1600} & \ms{34.3300}{2.2900} & \ms{68.5700}{5.5400} & \ms{67.3200}{5.5500} \\
30
+ & top 20\% & \ms{1.0300}{0.2700} & \ms{30.2100}{4.2900} & \ms{60.0600}{7.5700} & \ms{59.0400}{7.5900} \\
31
+ \addlinespace[1pt]
32
+ StormCast & global & \ms{0.0560}{0.0110} & \ms{8.2000}{2.1900} & \ms{22.3800}{5.4300} & \ms{22.3200}{5.4200} \\
33
+ & top 5\% & \ms{0.9600}{0.8000} & \ms{15.3200}{5.5300} & \ms{36.1900}{9.7300} & \ms{35.2300}{9.1800} \\
34
+ & top 10\% & \ms{0.7300}{0.9300} & \ms{12.6700}{6.3300} & \ms{30.4700}{10.6500} & \ms{29.7500}{9.7500} \\
35
+ & top 20\% & \ms{0.5800}{0.9100} & \ms{10.4200}{7.3400} & \ms{24.6600}{12.4000} & \ms{24.0800}{11.5000} \\
36
+ \addlinespace[1pt]
37
+ AlphaEarth & global & \ms{2.0600}{0.4400} & \ms{29.4500}{6.0100} & \ms{37.4300}{9.9500} & \ms{35.3700}{10.0300} \\
38
+ & top 5\% & \ms{6.9100}{0.8500} & \ms{42.8800}{4.6100} & \ms{51.7400}{8.7300} & \ms{44.8300}{9.0800} \\
39
+ & top 10\% & \ms{6.6400}{0.9900} & \ms{41.9000}{5.9500} & \ms{50.5700}{10.0100} & \ms{43.9300}{9.9200} \\
40
+ & top 20\% & \ms{6.1900}{1.1300} & \ms{38.8300}{7.5000} & \ms{46.3800}{12.1700} & \ms{40.1900}{11.6800} \\
41
+ \addlinespace[1pt]
42
+ DLWP & global & \ms{0.1700}{0.0400} & \ms{14.9100}{3.2400} & \ms{28.1900}{6.9700} & \ms{28.0200}{6.9300} \\
43
+ & top 5\% & \ms{1.8100}{0.4800} & \ms{31.7200}{3.2900} & \ms{55.4600}{5.2900} & \ms{53.6500}{5.4800} \\
44
+ & top 10\% & \ms{1.6100}{0.6000} & \ms{27.6600}{5.9200} & \ms{47.1300}{8.0100} & \ms{45.5200}{7.7900} \\
45
+ & top 20\% & \ms{1.5200}{0.9000} & \ms{20.9400}{4.8000} & \ms{34.9300}{7.8500} & \ms{33.4100}{7.8800} \\
46
+ \addlinespace[1pt]
47
+ FCN & global & \ms{0.2800}{0.0800} & \ms{19.5100}{3.3400} & \ms{40.0600}{9.3700} & \ms{39.7800}{9.3400} \\
48
+ & top 5\% & \ms{1.6200}{0.5100} & \ms{29.3800}{2.7600} & \ms{54.3000}{7.4100} & \ms{52.6800}{7.4400} \\
49
+ & top 10\% & \ms{1.1800}{0.5100} & \ms{22.4200}{3.9800} & \ms{43.4500}{9.2500} & \ms{42.2700}{9.0300} \\
50
+ & top 20\% & \ms{1.0000}{0.4300} & \ms{16.9800}{3.9400} & \ms{34.0900}{8.2600} & \ms{33.0900}{7.9300} \\
51
+ \addlinespace[1pt]
52
+ FengWu & global & \ms{0.2600}{0.0800} & \ms{12.0000}{6.0200} & \ms{24.1000}{13.6300} & \ms{23.8400}{13.5700} \\
53
+ & top 5\% & \ms{1.5700}{0.3600} & \ms{16.2800}{3.7000} & \ms{30.1100}{5.0100} & \ms{28.5400}{4.7700} \\
54
+ & top 10\% & \ms{1.2400}{0.5300} & \ms{12.9500}{5.6100} & \ms{24.1900}{8.6900} & \ms{22.9400}{8.1900} \\
55
+ & top 20\% & \ms{1.1200}{0.5000} & \ms{11.9500}{5.0700} & \ms{22.7900}{7.9100} & \ms{21.6700}{7.4400} \\
56
+ \addlinespace[1pt]
57
+ FuXi & global & \ms{0.3800}{0.1200} & \ms{21.0300}{4.8200} & \ms{37.2900}{9.4500} & \ms{36.9100}{9.4300} \\
58
+ & top 5\% & \ms{2.0300}{0.6800} & \ms{31.8900}{4.7300} & \ms{53.9300}{8.3800} & \ms{51.9000}{8.6900} \\
59
+ & top 10\% & \ms{1.6500}{0.7300} & \ms{24.0100}{5.7800} & \ms{40.2100}{9.9300} & \ms{38.5600}{9.7700} \\
60
+ & top 20\% & \ms{1.3600}{0.6800} & \ms{21.9500}{5.8600} & \ms{36.7300}{10.0300} & \ms{35.3700}{9.9200} \\
61
+ \addlinespace[1pt]
62
+ Pangu-Weather & global & \ms{0.2800}{0.1100} & \ms{17.0900}{4.0500} & \ms{35.6400}{9.0300} & \ms{35.3600}{9.0800} \\
63
+ & top 5\% & \ms{1.3700}{0.3100} & \ms{22.2200}{6.8600} & \ms{43.4200}{13.2400} & \ms{42.0600}{13.0600} \\
64
+ & top 10\% & \ms{1.0900}{0.3500} & \ms{18.9300}{5.9300} & \ms{38.5300}{11.7200} & \ms{37.4400}{11.5300} \\
65
+ & top 20\% & \ms{0.8800}{0.3600} & \ms{17.0200}{5.4900} & \ms{34.5700}{10.2900} & \ms{33.6800}{10.1300} \\
66
+ \bottomrule
67
+ \end{tabular}
68
+ \end{adjustbox}
69
+ \end{table*}
artifacts/results/selection_regret_all_backbones_20260504.csv ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_tag,label,scope,n,seeds,exact_regret_mean,exact_regret_std,tolerated_regret_mean,tolerated_regret_std,union_regret_mean,union_regret_std
2
+ reference,Reference,global,5,1 7 42 99 123,0.0,0.0,0.08783024981138902,0.09670495645481135,0.08783024981138902,0.09670495645481135
3
+ reference,Reference,fire_prone,5,1 7 42 99 123,0.0,0.0,0.03402707057672223,0.032044658643147844,0.03402707057672223,0.032044658643147844
4
+ prithvi_wxc,Prithvi-WxC,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
5
+ prithvi_wxc,Prithvi-WxC,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
6
+ aurora,Aurora,global,5,1 7 42 99 123,0.00020004882767231798,0.00026703384456332115,0.09851983041506818,0.1298781980037557,0.09851983041506818,0.1298781980037557
7
+ aurora,Aurora,fire_prone,5,1 7 42 99 123,0.008202508825959588,0.01834136732088763,0.14391889430974364,0.32121904665016227,0.14391889430974364,0.32121904665016227
8
+ climax,ClimaX,global,5,1 7 42 99 123,3.0287686240700486e-06,4.147312242167625e-06,0.0012959969982639485,0.0017746169760203706,0.0012959969982639485,0.0017746169760203706
9
+ climax,ClimaX,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
10
+ stormcast,StormCast,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
11
+ stormcast,StormCast,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
12
+ pangu_weather,Pangu-Weather,global,5,1 7 42 99 123,0.00013033979247265275,0.0002685372203690466,0.048806713097574374,0.10733308684741971,0.048806713097574374,0.10733308684741971
13
+ pangu_weather,Pangu-Weather,fire_prone,5,1 7 42 99 123,0.027875386332505546,0.02348779386900393,0.43111948243387105,0.39355644251497235,0.43111948243387105,0.39355644251497235
14
+ dlwp,DLWP,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
15
+ dlwp,DLWP,fire_prone,5,1 7 42 99 123,0.0007702319787454587,0.0010995336594539604,0.043265915053601556,0.04332331365579739,0.043265915053601556,0.04332331365579739
16
+ fcn,FCN,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
17
+ fcn,FCN,fire_prone,5,1 7 42 99 123,5.960229415004348e-06,1.3327478133443526e-05,0.011679805987441694,0.019872372458657642,0.011679805987441694,0.019872372458657642
18
+ fengwu,FengWu,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
19
+ fengwu,FengWu,fire_prone,5,1 7 42 99 123,0.0006908222234409067,0.0011910586589384115,0.005222389249812243,0.0062394095558402415,0.005222389249812243,0.0062394095558402415
20
+ fuxi,FuXi,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
21
+ fuxi,FuXi,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0010839188523199318,0.0017288780545672386,0.0010839188523199318,0.0017288780545672386
22
+ pangu6,Pangu-Weather,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
23
+ pangu6,Pangu-Weather,fire_prone,5,1 7 42 99 123,0.0007280423771922354,0.001178746460551365,0.0018491271881979853,0.0032630386057089294,0.0018491271881979853,0.0032630386057089294
24
+ alphaearth,AlphaEarth,global,5,1 7 42 99 123,0.0,0.0,0.1722171037486726,0.08849214830495522,0.1722171037486726,0.08849214830495522
25
+ alphaearth,AlphaEarth,fire_prone,5,1 7 42 99 123,0.0,0.0,0.038803552655092256,0.0594825313313219,0.038803552655092256,0.0594825313313219
artifacts/results/selection_regret_all_backbones_20260504.json ADDED
The diff for this file is too large to render. See raw diff
 
artifacts/results/selection_regret_full_head_table.generated.tex ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ % Full per-head rows are kept in the supplementary CSV files.
2
+ % The manuscript uses the all-backbone selection-regret summaries instead.
artifacts/results/selection_regret_head_metrics.csv ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ family,model_tag,scope,seed,selected_by,head_label,test_pr_auc,exact_f1,tolerated_f1,union_f1
2
+ AlphaEarth,alphaearth,fire_prone,1,PR-AUC,shallow spatial adapter,0.04390614036305261,0.09715762273901808,0.7863702028280513,0.7863702028280513
3
+ AlphaEarth,alphaearth,fire_prone,1,decision,shallow spatial adapter,,,,0.7863702028280513
4
+ AlphaEarth,alphaearth,fire_prone,7,PR-AUC,shallow spatial adapter,0.05955397140893137,0.12514898688915374,0.8294499693616161,0.8294499693616161
5
+ AlphaEarth,alphaearth,fire_prone,7,decision,shallow spatial adapter,,,,0.8294499693616161
6
+ AlphaEarth,alphaearth,fire_prone,42,PR-AUC,shallow spatial adapter,0.038083070948941686,0.08702469619756958,0.7112901458230849,0.7112901458230849
7
+ AlphaEarth,alphaearth,fire_prone,42,decision,pixel MLP head,,,,0.8461131676361712
8
+ AlphaEarth,alphaearth,fire_prone,99,PR-AUC,shallow spatial adapter,0.0458102699699856,0.10365251727541955,0.7758298037709835,0.7758298037709835
9
+ AlphaEarth,alphaearth,fire_prone,99,decision,pixel MLP head,,,,0.8350245452333586
10
+ AlphaEarth,alphaearth,fire_prone,123,PR-AUC,shallow spatial adapter,0.045809049876129763,0.10531544957774468,0.7789089693560928,0.7789089693560928
11
+ AlphaEarth,alphaearth,fire_prone,123,decision,shallow spatial adapter,,,,0.7789089693560928
12
+ AlphaEarth,alphaearth,global,1,PR-AUC,shallow spatial adapter,0.0006549130347299629,0.004193290734824281,0.40561891947698747,0.40561891947698747
13
+ AlphaEarth,alphaearth,global,1,decision,pixel MLP head,,,,0.6337627266658229
14
+ AlphaEarth,alphaearth,global,7,PR-AUC,shallow spatial adapter,0.001005722733868245,0.010460251046025104,0.6184842128568402,0.6184842128568402
15
+ AlphaEarth,alphaearth,global,7,decision,pixel MLP head,,,,0.6691395427484861
16
+ AlphaEarth,alphaearth,global,42,PR-AUC,shallow spatial adapter,0.0005634701573991865,0.004809747755451047,0.4087444681515033,0.4087444681515033
17
+ AlphaEarth,alphaearth,global,42,decision,pixel MLP head,,,,0.6812131506751973
18
+ AlphaEarth,alphaearth,global,99,PR-AUC,shallow spatial adapter,0.0006577120081349608,0.006780481898534931,0.3921547570095426,0.3921547570095426
19
+ AlphaEarth,alphaearth,global,99,decision,pixel MLP head,,,,0.5842714676652996
20
+ AlphaEarth,alphaearth,global,123,PR-AUC,shallow spatial adapter,0.0007047712457371991,0.006959088991986505,0.4427625907752311,0.4427625907752311
21
+ AlphaEarth,alphaearth,global,123,decision,pixel MLP head,,,,0.5604635792586619
22
+ Aurora,aurora,fire_prone,1,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
23
+ Aurora,aurora,fire_prone,1,decision,linear probe,,,,0.7185324707231184
24
+ Aurora,aurora,fire_prone,7,PR-AUC,linear probe,0.024802820904513342,0.0413500618483831,0.7184857293868923,0.7184857293868923
25
+ Aurora,aurora,fire_prone,7,decision,shallow spatial adapter,,,,0.7185324707231184
26
+ Aurora,aurora,fire_prone,42,PR-AUC,linear probe,0.02613792907867929,0.04101254412979794,0.7185324707231184,0.7185324707231184
27
+ Aurora,aurora,fire_prone,42,decision,linear probe,,,,0.7185324707231184
28
+ Aurora,aurora,fire_prone,99,PR-AUC,linear probe,0.02093558282208589,0.0,0.0,0.0
29
+ Aurora,aurora,fire_prone,99,decision,shallow spatial adapter,,,,0.7185324707231184
30
+ Aurora,aurora,fire_prone,123,PR-AUC,pixel MLP head,0.03014151567817997,0.04541038665445361,0.7175172112337449,0.7175172112337449
31
+ Aurora,aurora,fire_prone,123,decision,linear probe,,,,0.7185324707231184
32
+ Aurora,aurora,global,1,PR-AUC,linear probe,0.00024254792826221397,0.00048497822606044473,0.23755358049655212,0.23755358049655212
33
+ Aurora,aurora,global,1,decision,shallow spatial adapter,,,,0.240793572992212
34
+ Aurora,aurora,global,7,PR-AUC,linear probe,0.00027660331739269843,0.0,0.23734400297937533,0.23734400297937533
35
+ Aurora,aurora,global,7,decision,shallow spatial adapter,,,,0.240793572992212
36
+ Aurora,aurora,global,42,PR-AUC,linear probe,0.0002876372030063385,0.00048497822606044473,0.0,0.0
37
+ Aurora,aurora,global,42,decision,shallow spatial adapter,,,,0.240793572992212
38
+ Aurora,aurora,global,99,PR-AUC,linear probe,0.00024254792826221397,0.0,0.0,0.0
39
+ Aurora,aurora,global,99,decision,shallow spatial adapter,,,,0.240793572992212
40
+ Aurora,aurora,global,123,PR-AUC,pixel MLP head,0.00031683315961488916,0.0005311562430574265,0.23647112940979162,0.23647112940979162
41
+ Aurora,aurora,global,123,decision,shallow spatial adapter,,,,0.240793572992212
42
+ ClimaX,climax,fire_prone,1,PR-AUC,linear probe,0.02281272244151735,0.04101254412979794,0.7185324707231184,0.7185324707231184
43
+ ClimaX,climax,fire_prone,1,decision,linear probe,,,,0.7185324707231184
44
+ ClimaX,climax,fire_prone,7,PR-AUC,linear probe,0.021317405351800135,0.04101254412979794,0.7185324707231184,0.7185324707231184
45
+ ClimaX,climax,fire_prone,7,decision,linear probe,,,,0.7185324707231184
46
+ ClimaX,climax,fire_prone,42,PR-AUC,linear probe,0.021516770872035896,0.04101254412979794,0.7185324707231184,0.7185324707231184
47
+ ClimaX,climax,fire_prone,42,decision,linear probe,,,,0.7185324707231184
48
+ ClimaX,climax,fire_prone,99,PR-AUC,shallow spatial adapter,0.02099123219536693,0.04101254412979794,0.7185324707231184,0.7185324707231184
49
+ ClimaX,climax,fire_prone,99,decision,shallow spatial adapter,,,,0.7185324707231184
50
+ ClimaX,climax,fire_prone,123,PR-AUC,shallow spatial adapter,0.024930358757410707,0.04101254412979794,0.7185324707231184,0.7185324707231184
51
+ ClimaX,climax,fire_prone,123,decision,shallow spatial adapter,,,,0.7185324707231184
52
+ ClimaX,climax,global,1,PR-AUC,linear probe,0.0002543464414550104,0.00048497822606044473,0.23755358049655212,0.23755358049655212
53
+ ClimaX,climax,global,1,decision,shallow spatial adapter,,,,0.240793572992212
54
+ ClimaX,climax,global,7,PR-AUC,pixel MLP head,0.00025423546642937565,0.00048497822606044473,0.23755358049655212,0.23755358049655212
55
+ ClimaX,climax,global,7,decision,shallow spatial adapter,,,,0.240793572992212
56
+ ClimaX,climax,global,42,PR-AUC,shallow spatial adapter,0.00023723426605001756,0.0004925501476206198,0.240793572992212,0.240793572992212
57
+ ClimaX,climax,global,42,decision,shallow spatial adapter,,,,0.240793572992212
58
+ ClimaX,climax,global,99,PR-AUC,shallow spatial adapter,0.0002340075376021003,0.00048696535779102983,0.2384111045733381,0.2384111045733381
59
+ ClimaX,climax,global,99,decision,shallow spatial adapter,,,,0.2384111045733381
60
+ ClimaX,climax,global,123,PR-AUC,shallow spatial adapter,0.00025952340213823634,0.0004925501476206198,0.240793572992212,0.240793572992212
61
+ ClimaX,climax,global,123,decision,shallow spatial adapter,,,,0.240793572992212
62
+ DLWP,dlwp,fire_prone,1,PR-AUC,linear probe,0.019747545663020845,0.043506471331489265,0.7280364139105968,0.7280364139105968
63
+ DLWP,dlwp,fire_prone,1,decision,linear probe,,,,0.7280364139105968
64
+ DLWP,dlwp,fire_prone,7,PR-AUC,pixel MLP head,0.018519739310339497,0.04101254412979794,0.7185324707231184,0.7185324707231184
65
+ DLWP,dlwp,fire_prone,7,decision,shallow spatial adapter,,,,0.762536895087842
66
+ DLWP,dlwp,fire_prone,42,PR-AUC,pixel MLP head,0.020762153794205103,0.04637177602565815,0.6606900017471591,0.6606900017471591
67
+ DLWP,dlwp,fire_prone,42,decision,shallow spatial adapter,,,,0.7728400679088107
68
+ DLWP,dlwp,fire_prone,99,PR-AUC,linear probe,0.02136633936888583,0.04819843096725701,0.7187730423241409,0.7187730423241409
69
+ DLWP,dlwp,fire_prone,99,decision,shallow spatial adapter,,,,0.7653346239087763
70
+ DLWP,dlwp,fire_prone,123,PR-AUC,pixel MLP head,0.021118517500793188,0.04101254412979794,0.7185324707231184,0.7185324707231184
71
+ DLWP,dlwp,fire_prone,123,decision,linear probe,,,,0.7321459738801157
72
+ DLWP,dlwp,global,1,PR-AUC,shallow spatial adapter,0.0006257446338466172,0.00487022180273714,0.38023285660836226,0.38023285660836226
73
+ DLWP,dlwp,global,1,decision,shallow spatial adapter,,,,0.38023285660836226
74
+ DLWP,dlwp,global,7,PR-AUC,shallow spatial adapter,0.0005264872646085452,0.0,0.3432315705541329,0.3432315705541329
75
+ DLWP,dlwp,global,7,decision,shallow spatial adapter,,,,0.3432315705541329
76
+ DLWP,dlwp,global,42,PR-AUC,shallow spatial adapter,0.0006203713852571992,0.0,0.3405125814370199,0.3405125814370199
77
+ DLWP,dlwp,global,42,decision,shallow spatial adapter,,,,0.3405125814370199
78
+ DLWP,dlwp,global,99,PR-AUC,shallow spatial adapter,0.0007477128447471452,0.0,0.3979559626836394,0.3979559626836394
79
+ DLWP,dlwp,global,99,decision,shallow spatial adapter,,,,0.3979559626836394
80
+ DLWP,dlwp,global,123,PR-AUC,shallow spatial adapter,0.0007129763973023342,0.0,0.3797689460796109,0.3797689460796109
81
+ DLWP,dlwp,global,123,decision,shallow spatial adapter,,,,0.3797689460796109
82
+ FCN,fcn,fire_prone,1,PR-AUC,shallow spatial adapter,0.01844011667219625,0.042068766252528166,0.7185324707231184,0.7185324707231184
83
+ FCN,fcn,fire_prone,1,decision,linear probe,,,,0.7182175622542595
84
+ FCN,fcn,fire_prone,7,PR-AUC,pixel MLP head,0.02050876208409485,0.04101254412979794,0.7185324707231184,0.7185324707231184
85
+ FCN,fcn,fire_prone,7,decision,linear probe,,,,0.7644129739607127
86
+ FCN,fcn,fire_prone,42,PR-AUC,shallow spatial adapter,0.018030815062946615,0.0414596444738876,0.7197180735022655,0.7197180735022655
87
+ FCN,fcn,fire_prone,42,decision,shallow spatial adapter,,,,0.7197180735022655
88
+ FCN,fcn,fire_prone,99,PR-AUC,linear probe,0.029098665712304895,0.042822140550172624,0.726408418760773,0.726408418760773
89
+ FCN,fcn,fire_prone,99,decision,linear probe,,,,0.726408418760773
90
+ FCN,fcn,fire_prone,123,PR-AUC,pixel MLP head,0.019943278646881796,0.04101254412979794,0.7185324707231184,0.7185324707231184
91
+ FCN,fcn,fire_prone,123,decision,linear probe,,,,0.7310509974227326
92
+ FCN,fcn,global,1,PR-AUC,shallow spatial adapter,0.00037256097806901117,0.0009319664492078285,0.31167484413093016,0.31167484413093016
93
+ FCN,fcn,global,1,decision,shallow spatial adapter,,,,0.31167484413093016
94
+ FCN,fcn,global,7,PR-AUC,shallow spatial adapter,0.0003268363416054406,0.001086071137659517,0.3051941376005135,0.3051941376005135
95
+ FCN,fcn,global,7,decision,shallow spatial adapter,,,,0.3051941376005135
96
+ FCN,fcn,global,42,PR-AUC,shallow spatial adapter,0.00041063897933390575,0.0007027406886858749,0.31987973649439366,0.31987973649439366
97
+ FCN,fcn,global,42,decision,pixel MLP head,,,,0.2870596305028149
98
+ FCN,fcn,global,99,PR-AUC,shallow spatial adapter,0.00038120453362995967,0.0018159806295399514,0.3054145960271247,0.3054145960271247
99
+ FCN,fcn,global,99,decision,shallow spatial adapter,,,,0.3054145960271247
100
+ FCN,fcn,global,123,PR-AUC,shallow spatial adapter,0.000387412312932838,0.0006535947712418301,0.3096850885545486,0.3096850885545486
101
+ FCN,fcn,global,123,decision,shallow spatial adapter,,,,0.3096850885545486
102
+ FengWu,fengwu,fire_prone,1,PR-AUC,shallow spatial adapter,0.022736128443885992,0.04452825597664091,0.7269980510116651,0.7269980510116651
103
+ FengWu,fengwu,fire_prone,1,decision,shallow spatial adapter,,,,0.7269980510116651
104
+ FengWu,fengwu,fire_prone,7,PR-AUC,pixel MLP head,0.016801706970978273,0.04101254412979794,0.7185324707231184,0.7185324707231184
105
+ FengWu,fengwu,fire_prone,7,decision,shallow spatial adapter,,,,0.722901134194411
106
+ FengWu,fengwu,fire_prone,42,PR-AUC,shallow spatial adapter,0.021165185967854567,0.04574838388861263,0.7265705731122933,0.7265705731122933
107
+ FengWu,fengwu,fire_prone,42,decision,shallow spatial adapter,,,,0.7265705731122933
108
+ FengWu,fengwu,fire_prone,99,PR-AUC,linear probe,0.02199779031689959,0.0,0.7185324707231184,0.7185324707231184
109
+ FengWu,fengwu,fire_prone,99,decision,shallow spatial adapter,,,,0.7336829717908426
110
+ FengWu,fengwu,fire_prone,123,PR-AUC,pixel MLP head,0.020495144103834257,0.04101254412979794,0.7185324707231184,0.7185324707231184
111
+ FengWu,fengwu,fire_prone,123,decision,shallow spatial adapter,,,,0.7251252524331628
112
+ FengWu,fengwu,global,1,PR-AUC,shallow spatial adapter,0.000398077435365184,0.0,0.31071390711162444,0.31071390711162444
113
+ FengWu,fengwu,global,1,decision,shallow spatial adapter,,,,0.31071390711162444
114
+ FengWu,fengwu,global,7,PR-AUC,shallow spatial adapter,0.00036963872610239895,0.0,0.30641904273669174,0.30641904273669174
115
+ FengWu,fengwu,global,7,decision,shallow spatial adapter,,,,0.30641904273669174
116
+ FengWu,fengwu,global,42,PR-AUC,shallow spatial adapter,0.00036987379624400454,0.0,0.31219058559732665,0.31219058559732665
117
+ FengWu,fengwu,global,42,decision,shallow spatial adapter,,,,0.31219058559732665
118
+ FengWu,fengwu,global,99,PR-AUC,shallow spatial adapter,0.00042782651526874734,0.0,0.3111214819309595,0.3111214819309595
119
+ FengWu,fengwu,global,99,decision,shallow spatial adapter,,,,0.3111214819309595
120
+ FengWu,fengwu,global,123,PR-AUC,shallow spatial adapter,0.0004116035724473925,0.0,0.3145618361221859,0.3145618361221859
121
+ FengWu,fengwu,global,123,decision,shallow spatial adapter,,,,0.3145618361221859
122
+ FuXi,fuxi,fire_prone,1,PR-AUC,shallow spatial adapter,0.01904942261850253,0.043196160341303,0.7246636456247585,0.7246636456247585
123
+ FuXi,fuxi,fire_prone,1,decision,linear probe,,,,0.7261195534617713
124
+ FuXi,fuxi,fire_prone,7,PR-AUC,shallow spatial adapter,0.018717347905238046,0.044572342126298965,0.7235687421646468,0.7235687421646468
125
+ FuXi,fuxi,fire_prone,7,decision,shallow spatial adapter,,,,0.7235687421646468
126
+ FuXi,fuxi,fire_prone,42,PR-AUC,shallow spatial adapter,0.020363596550511454,0.04603830266616599,0.7054843984273774,0.7054843984273774
127
+ FuXi,fuxi,fire_prone,42,decision,shallow spatial adapter,,,,0.7054843984273774
128
+ FuXi,fuxi,fire_prone,99,PR-AUC,pixel MLP head,0.02225455497934009,0.030284377692970574,0.7203102915557309,0.7203102915557309
129
+ FuXi,fuxi,fire_prone,99,decision,shallow spatial adapter,,,,0.7183426482806338
130
+ FuXi,fuxi,fire_prone,123,PR-AUC,pixel MLP head,0.021045021724179047,0.018543768748295608,0.7185324707231184,0.7185324707231184
131
+ FuXi,fuxi,fire_prone,123,decision,linear probe,,,,0.7224961571477053
132
+ FuXi,fuxi,global,1,PR-AUC,shallow spatial adapter,0.0003596048414560045,0.0006866311182961118,0.3091927111996169,0.3091927111996169
133
+ FuXi,fuxi,global,1,decision,linear probe,,,,0.21516044416153002
134
+ FuXi,fuxi,global,7,PR-AUC,shallow spatial adapter,0.00037118462783325537,0.0008450613619815959,0.3062654921252336,0.3062654921252336
135
+ FuXi,fuxi,global,7,decision,shallow spatial adapter,,,,0.3062654921252336
136
+ FuXi,fuxi,global,42,PR-AUC,shallow spatial adapter,0.0003830459807690152,0.0003188165529554294,0.316668570748256,0.316668570748256
137
+ FuXi,fuxi,global,42,decision,shallow spatial adapter,,,,0.316668570748256
138
+ FuXi,fuxi,global,99,PR-AUC,shallow spatial adapter,0.0003758107890486513,0.0007771032641515431,0.3108110703043797,0.3108110703043797
139
+ FuXi,fuxi,global,99,decision,shallow spatial adapter,,,,0.3108110703043797
140
+ FuXi,fuxi,global,123,PR-AUC,shallow spatial adapter,0.00039999784430104664,0.0009888839378897987,0.3096924811079745,0.3096924811079745
141
+ FuXi,fuxi,global,123,decision,shallow spatial adapter,,,,0.3096924811079745
142
+ Pangu-Weather,pangu_weather,fire_prone,1,PR-AUC,pixel MLP head,0.014813375233921266,0.04101254412979794,0.7185324707231184,0.7185324707231184
143
+ Pangu-Weather,pangu_weather,fire_prone,1,decision,pixel MLP head,,,,0.7185324707231184
144
+ Pangu-Weather,pangu_weather,fire_prone,7,PR-AUC,pixel MLP head,0.029883397445312192,0.0,0.0,0.0
145
+ Pangu-Weather,pangu_weather,fire_prone,7,decision,shallow spatial adapter,,,,0.7185324707231184
146
+ Pangu-Weather,pangu_weather,fire_prone,42,PR-AUC,pixel MLP head,0.02188826028256454,0.0,0.0,0.0
147
+ Pangu-Weather,pangu_weather,fire_prone,42,decision,shallow spatial adapter,,,,0.7185324707231184
148
+ Pangu-Weather,pangu_weather,fire_prone,99,PR-AUC,linear probe,0.02093558282208589,0.0,0.0,0.0
149
+ Pangu-Weather,pangu_weather,fire_prone,99,decision,shallow spatial adapter,,,,0.7185324707231184
150
+ Pangu-Weather,pangu_weather,fire_prone,123,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
151
+ Pangu-Weather,pangu_weather,fire_prone,123,decision,linear probe,,,,0.7185324707231184
152
+ Pangu-Weather,pangu_weather,global,1,PR-AUC,linear probe,0.00024254792826221397,0.00048497822606044473,0.23755358049655212,0.23755358049655212
153
+ Pangu-Weather,pangu_weather,global,1,decision,shallow spatial adapter,,,,0.240793572992212
154
+ Pangu-Weather,pangu_weather,global,7,PR-AUC,shallow spatial adapter,0.00028045576992618193,0.0004925501476206198,0.240793572992212,0.240793572992212
155
+ Pangu-Weather,pangu_weather,global,7,decision,shallow spatial adapter,,,,0.240793572992212
156
+ Pangu-Weather,pangu_weather,global,42,PR-AUC,pixel MLP head,0.00021817709863716954,0.0,0.0,0.0
157
+ Pangu-Weather,pangu_weather,global,42,decision,shallow spatial adapter,,,,0.240793572992212
158
+ Pangu-Weather,pangu_weather,global,99,PR-AUC,shallow spatial adapter,0.0003408299850116487,0.0004887421693148924,0.23917716245227552,0.23917716245227552
159
+ Pangu-Weather,pangu_weather,global,99,decision,pixel MLP head,,,,0.229804399271568
160
+ Pangu-Weather,pangu_weather,global,123,PR-AUC,shallow spatial adapter,0.0003300754798792237,0.0004925501476206198,0.240793572992212,0.240793572992212
161
+ Pangu-Weather,pangu_weather,global,123,decision,shallow spatial adapter,,,,0.240793572992212
162
+ Pangu-Weather,pangu6,fire_prone,1,PR-AUC,shallow spatial adapter,0.02328829578760066,0.04418397717161179,0.7231388400090598,0.7231388400090598
163
+ Pangu-Weather,pangu6,fire_prone,1,decision,shallow spatial adapter,,,,0.7231388400090598
164
+ Pangu-Weather,pangu6,fire_prone,7,PR-AUC,pixel MLP head,0.018664183428008234,0.04101254412979794,0.7185324707231184,0.7185324707231184
165
+ Pangu-Weather,pangu6,fire_prone,7,decision,shallow spatial adapter,,,,0.719461376864001
166
+ Pangu-Weather,pangu6,fire_prone,42,PR-AUC,shallow spatial adapter,0.021420904714594617,0.0430065972893146,0.7241279907754397,0.7241279907754397
167
+ Pangu-Weather,pangu6,fire_prone,42,decision,shallow spatial adapter,,,,0.7241279907754397
168
+ Pangu-Weather,pangu6,fire_prone,99,PR-AUC,linear probe,0.024740444457605402,0.0044004400440044,0.7183382629739177,0.7183382629739177
169
+ Pangu-Weather,pangu6,fire_prone,99,decision,shallow spatial adapter,,,,0.719015307962107
170
+ Pangu-Weather,pangu6,fire_prone,123,PR-AUC,pixel MLP head,0.02254610440161657,0.04101254412979794,0.7185324707231184,0.7185324707231184
171
+ Pangu-Weather,pangu6,fire_prone,123,decision,linear probe,,,,0.7261721555350364
172
+ Pangu-Weather,pangu6,global,1,PR-AUC,shallow spatial adapter,0.00042881385578730365,0.0,0.32066974948800614,0.32066974948800614
173
+ Pangu-Weather,pangu6,global,1,decision,shallow spatial adapter,,,,0.32066974948800614
174
+ Pangu-Weather,pangu6,global,7,PR-AUC,shallow spatial adapter,0.00038395193539280824,0.0,0.31120670593952293,0.31120670593952293
175
+ Pangu-Weather,pangu6,global,7,decision,shallow spatial adapter,,,,0.31120670593952293
176
+ Pangu-Weather,pangu6,global,42,PR-AUC,shallow spatial adapter,0.00040651309469793043,0.0,0.32154249424354786,0.32154249424354786
177
+ Pangu-Weather,pangu6,global,42,decision,shallow spatial adapter,,,,0.32154249424354786
178
+ Pangu-Weather,pangu6,global,99,PR-AUC,shallow spatial adapter,0.0004450373086921994,0.0,0.3214547875801752,0.3214547875801752
179
+ Pangu-Weather,pangu6,global,99,decision,shallow spatial adapter,,,,0.3214547875801752
180
+ Pangu-Weather,pangu6,global,123,PR-AUC,shallow spatial adapter,0.00043738577276574255,0.0,0.31812983009681495,0.31812983009681495
181
+ Pangu-Weather,pangu6,global,123,decision,shallow spatial adapter,,,,0.31812983009681495
182
+ Prithvi-WxC,prithvi_wxc,fire_prone,1,PR-AUC,shallow spatial adapter,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
183
+ Prithvi-WxC,prithvi_wxc,fire_prone,1,decision,shallow spatial adapter,,,,0.7185324707231184
184
+ Prithvi-WxC,prithvi_wxc,fire_prone,7,PR-AUC,shallow spatial adapter,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
185
+ Prithvi-WxC,prithvi_wxc,fire_prone,7,decision,shallow spatial adapter,,,,0.7185324707231184
186
+ Prithvi-WxC,prithvi_wxc,fire_prone,42,PR-AUC,pixel MLP head,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
187
+ Prithvi-WxC,prithvi_wxc,fire_prone,42,decision,shallow spatial adapter,,,,0.7185324707231184
188
+ Prithvi-WxC,prithvi_wxc,fire_prone,99,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
189
+ Prithvi-WxC,prithvi_wxc,fire_prone,99,decision,shallow spatial adapter,,,,0.7185324707231184
190
+ Prithvi-WxC,prithvi_wxc,fire_prone,123,PR-AUC,shallow spatial adapter,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
191
+ Prithvi-WxC,prithvi_wxc,fire_prone,123,decision,shallow spatial adapter,,,,0.7185324707231184
192
+ Prithvi-WxC,prithvi_wxc,global,1,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
193
+ Prithvi-WxC,prithvi_wxc,global,1,decision,shallow spatial adapter,,,,0.240793572992212
194
+ Prithvi-WxC,prithvi_wxc,global,7,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
195
+ Prithvi-WxC,prithvi_wxc,global,7,decision,shallow spatial adapter,,,,0.240793572992212
196
+ Prithvi-WxC,prithvi_wxc,global,42,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
197
+ Prithvi-WxC,prithvi_wxc,global,42,decision,shallow spatial adapter,,,,0.240793572992212
198
+ Prithvi-WxC,prithvi_wxc,global,99,PR-AUC,shallow spatial adapter,0.0002454330184381399,0.00048497822606044473,0.23755358049655212,0.23755358049655212
199
+ Prithvi-WxC,prithvi_wxc,global,99,decision,shallow spatial adapter,,,,0.23755358049655212
200
+ Prithvi-WxC,prithvi_wxc,global,123,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
201
+ Prithvi-WxC,prithvi_wxc,global,123,decision,shallow spatial adapter,,,,0.240793572992212
202
+ Reference,reference,fire_prone,1,PR-AUC,shallow spatial adapter,0.10204224118176683,0.1611624834874505,0.799032457577039,0.799032457577039
203
+ Reference,reference,fire_prone,1,decision,linear probe,,,,0.8685988450931266
204
+ Reference,reference,fire_prone,7,PR-AUC,shallow spatial adapter,0.1323902067230726,0.22885572139303484,0.8027807546359551,0.8027807546359551
205
+ Reference,reference,fire_prone,7,decision,shallow spatial adapter,,,,0.8027807546359551
206
+ Reference,reference,fire_prone,42,PR-AUC,shallow spatial adapter,0.12048427320762313,0.19225806451612906,0.8358151221553631,0.8358151221553631
207
+ Reference,reference,fire_prone,42,decision,shallow spatial adapter,,,,0.8358151221553631
208
+ Reference,reference,fire_prone,99,PR-AUC,shallow spatial adapter,0.11947246238005743,0.19477124183006533,0.8534759193943048,0.8534759193943048
209
+ Reference,reference,fire_prone,99,decision,linear probe,,,,0.9039923296574045
210
+ Reference,reference,fire_prone,123,PR-AUC,shallow spatial adapter,0.11551882470432043,0.19165378670788252,0.8066689866810086,0.8066689866810086
211
+ Reference,reference,fire_prone,123,decision,pixel MLP head,,,,0.8567215417854325
212
+ Reference,reference,global,1,PR-AUC,shallow spatial adapter,0.002624341503354088,0.017134572294714646,0.6186566066408326,0.6186566066408326
213
+ Reference,reference,global,1,decision,linear probe,,,,0.7286109603326707
214
+ Reference,reference,global,7,PR-AUC,shallow spatial adapter,0.0030317345997110698,0.02287675150128682,0.5002193417313746,0.5002193417313746
215
+ Reference,reference,global,7,decision,shallow spatial adapter,,,,0.5002193417313746
216
+ Reference,reference,global,42,PR-AUC,shallow spatial adapter,0.0032729435045747413,0.025515210991167808,0.7239943741463363,0.7239943741463363
217
+ Reference,reference,global,42,decision,shallow spatial adapter,,,,0.7239943741463363
218
+ Reference,reference,global,99,PR-AUC,shallow spatial adapter,0.0031953098868323193,0.021876258220373104,0.6698005926442897,0.6698005926442897
219
+ Reference,reference,global,99,decision,linear probe,,,,0.7647466876509988
220
+ Reference,reference,global,123,PR-AUC,shallow spatial adapter,0.0025603887793133394,0.019320660641944532,0.5009707461135111,0.5009707461135111
221
+ Reference,reference,global,123,decision,pixel MLP head,,,,0.735221546471909
222
+ StormCast,stormcast,fire_prone,1,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
223
+ StormCast,stormcast,fire_prone,1,decision,shallow spatial adapter,,,,0.7185324707231184
224
+ StormCast,stormcast,fire_prone,7,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
225
+ StormCast,stormcast,fire_prone,7,decision,shallow spatial adapter,,,,0.7185324707231184
226
+ StormCast,stormcast,fire_prone,42,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
227
+ StormCast,stormcast,fire_prone,42,decision,shallow spatial adapter,,,,0.7185324707231184
228
+ StormCast,stormcast,fire_prone,99,PR-AUC,pixel MLP head,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
229
+ StormCast,stormcast,fire_prone,99,decision,shallow spatial adapter,,,,0.7185324707231184
230
+ StormCast,stormcast,fire_prone,123,PR-AUC,linear probe,0.02093558282208589,0.04101254412979794,0.7185324707231184,0.7185324707231184
231
+ StormCast,stormcast,fire_prone,123,decision,shallow spatial adapter,,,,0.7185324707231184
232
+ StormCast,stormcast,global,1,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
233
+ StormCast,stormcast,global,1,decision,shallow spatial adapter,,,,0.240793572992212
234
+ StormCast,stormcast,global,7,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
235
+ StormCast,stormcast,global,7,decision,shallow spatial adapter,,,,0.240793572992212
236
+ StormCast,stormcast,global,42,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
237
+ StormCast,stormcast,global,42,decision,shallow spatial adapter,,,,0.240793572992212
238
+ StormCast,stormcast,global,99,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004905319945803844,0.2399251001974223,0.2399251001974223
239
+ StormCast,stormcast,global,99,decision,shallow spatial adapter,,,,0.2399251001974223
240
+ StormCast,stormcast,global,123,PR-AUC,shallow spatial adapter,0.0002463357401629007,0.0004925501476206198,0.240793572992212,0.240793572992212
241
+ StormCast,stormcast,global,123,decision,shallow spatial adapter,,,,0.240793572992212
artifacts/results/selection_regret_main_table.generated.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[!t]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{4pt}
5
+ \caption{Fixed-feature selection-regret check across evaluation scopes. Values are percentage-point regret \(\delta = D(h_D)-D(h_R)\) under union-\(F_1\), where \(h_R\) is selected by PR-AUC and \(h_D\) by the decision metric. Top-\(k\) columns use train-defined fire-prone scopes. Rows report mean with small std over five seeds; \(0.0000\) means the two selectors give the same decision score for all seeds.}
6
+ \label{tab:selection_regret_diagnostic}
7
+ \begin{tabular}{lcccc}
8
+ \toprule
9
+ \textbf{Feature source} & \textbf{\(\Omega=\)global} & \textbf{\(\Omega=\)top 5\%} & \textbf{\(\Omega=\)top 10\%} & \textbf{\(\Omega=\)top 20\%} \\
10
+ \midrule
11
+ \textcolor{blue}{FireWx-FM ref.} & \ms{7.3831}{7.4536} & \ms{0.3664}{0.6812} & \ms{1.2275}{1.2665} & \ms{2.9385}{2.7513} \\
12
+ Prithvi-WxC & 0.0000 & 0.0000 & 0.0000 & 0.0000 \\
13
+ Aurora & \ms{4.9455}{10.6974} & \ms{15.4283}{34.4987} & \ms{13.9934}{31.2903} & \ms{14.3706}{32.1337} \\
14
+ ClimaX & \ms{0.1296}{0.1775} & 0.0000 & 0.0000 & 0.0000 \\
15
+ StormCast & 0.0000 & 0.0000 & 0.0000 & 0.0000 \\
16
+ DLWP & 0.0000 & \ms{1.6716}{1.6079} & \ms{2.8465}{2.6938} & \ms{4.4634}{4.3561} \\
17
+ FCN & 0.0000 & \ms{0.4510}{1.0071} & \ms{0.4200}{0.9390} & \ms{1.1680}{1.9872} \\
18
+ FengWu & 0.0000 & \ms{0.8796}{0.5532} & \ms{0.4023}{0.5511} & \ms{0.5222}{0.6239} \\
19
+ FuXi & 0.0000 & \ms{1.3545}{2.0970} & \ms{0.1656}{0.3703} & \ms{0.2833}{0.3681} \\
20
+ Pangu-Weather & 0.0000 & \ms{0.7593}{0.8974} & \ms{0.3048}{0.5054} & \ms{0.1868}{0.3255} \\
21
+ AlphaEarth & \ms{17.2217}{8.8492} & \ms{6.3846}{4.9653} & \ms{6.5738}{6.8970} & \ms{3.8804}{5.9483} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
artifacts/results/selection_regret_per_seed.csv ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ family,model_tag,scope,seed,ranking_head,decision_head,ranking_pr_auc,ranking_union_f1,decision_union_f1,regret,top1_agreement
2
+ AlphaEarth,alphaearth,fire_prone,1,shallow spatial adapter,shallow spatial adapter,0.04390614036305261,0.7863702028280513,0.7863702028280513,0.0,True
3
+ AlphaEarth,alphaearth,fire_prone,7,shallow spatial adapter,shallow spatial adapter,0.05955397140893137,0.8294499693616161,0.8294499693616161,0.0,True
4
+ AlphaEarth,alphaearth,fire_prone,42,shallow spatial adapter,pixel MLP head,0.038083070948941686,0.7112901458230849,0.8461131676361712,0.13482302181308625,False
5
+ AlphaEarth,alphaearth,fire_prone,99,shallow spatial adapter,pixel MLP head,0.0458102699699856,0.7758298037709835,0.8350245452333586,0.05919474146237502,False
6
+ AlphaEarth,alphaearth,fire_prone,123,shallow spatial adapter,shallow spatial adapter,0.045809049876129763,0.7789089693560928,0.7789089693560928,0.0,True
7
+ AlphaEarth,alphaearth,global,1,shallow spatial adapter,pixel MLP head,0.0006549130347299629,0.40561891947698747,0.6337627266658229,0.22814380718883542,False
8
+ AlphaEarth,alphaearth,global,7,shallow spatial adapter,pixel MLP head,0.001005722733868245,0.6184842128568402,0.6691395427484861,0.050655329891645895,False
9
+ AlphaEarth,alphaearth,global,42,shallow spatial adapter,pixel MLP head,0.0005634701573991865,0.4087444681515033,0.6812131506751973,0.272468682523694,False
10
+ AlphaEarth,alphaearth,global,99,shallow spatial adapter,pixel MLP head,0.0006577120081349608,0.3921547570095426,0.5842714676652996,0.19211671065575697,False
11
+ AlphaEarth,alphaearth,global,123,shallow spatial adapter,pixel MLP head,0.0007047712457371991,0.4427625907752311,0.5604635792586619,0.11770098848343075,False
12
+ Aurora,aurora,fire_prone,1,linear probe,linear probe,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,True
13
+ Aurora,aurora,fire_prone,7,linear probe,shallow spatial adapter,0.024802820904513342,0.7184857293868923,0.7185324707231184,4.674133622617482e-05,False
14
+ Aurora,aurora,fire_prone,42,linear probe,linear probe,0.02613792907867929,0.7185324707231184,0.7185324707231184,0.0,True
15
+ Aurora,aurora,fire_prone,99,linear probe,shallow spatial adapter,0.02093558282208589,0.0,0.7185324707231184,0.7185324707231184,False
16
+ Aurora,aurora,fire_prone,123,pixel MLP head,linear probe,0.03014151567817997,0.7175172112337449,0.7185324707231184,0.0010152594893735323,False
17
+ Aurora,aurora,global,1,linear probe,shallow spatial adapter,0.00024254792826221397,0.23755358049655212,0.240793572992212,0.0032399924956598714,False
18
+ Aurora,aurora,global,7,linear probe,shallow spatial adapter,0.00027660331739269843,0.23734400297937533,0.240793572992212,0.0034495700128366613,False
19
+ Aurora,aurora,global,42,linear probe,shallow spatial adapter,0.0002876372030063385,0.0,0.240793572992212,0.240793572992212,False
20
+ Aurora,aurora,global,99,linear probe,shallow spatial adapter,0.00024254792826221397,0.0,0.240793572992212,0.240793572992212,False
21
+ Aurora,aurora,global,123,pixel MLP head,shallow spatial adapter,0.00031683315961488916,0.23647112940979162,0.240793572992212,0.004322443582420371,False
22
+ ClimaX,climax,fire_prone,1,linear probe,linear probe,0.02281272244151735,0.7185324707231184,0.7185324707231184,0.0,True
23
+ ClimaX,climax,fire_prone,7,linear probe,linear probe,0.021317405351800135,0.7185324707231184,0.7185324707231184,0.0,True
24
+ ClimaX,climax,fire_prone,42,linear probe,linear probe,0.021516770872035896,0.7185324707231184,0.7185324707231184,0.0,True
25
+ ClimaX,climax,fire_prone,99,shallow spatial adapter,shallow spatial adapter,0.02099123219536693,0.7185324707231184,0.7185324707231184,0.0,True
26
+ ClimaX,climax,fire_prone,123,shallow spatial adapter,shallow spatial adapter,0.024930358757410707,0.7185324707231184,0.7185324707231184,0.0,True
27
+ ClimaX,climax,global,1,linear probe,shallow spatial adapter,0.0002543464414550104,0.23755358049655212,0.240793572992212,0.0032399924956598714,False
28
+ ClimaX,climax,global,7,pixel MLP head,shallow spatial adapter,0.00025423546642937565,0.23755358049655212,0.240793572992212,0.0032399924956598714,False
29
+ ClimaX,climax,global,42,shallow spatial adapter,shallow spatial adapter,0.00023723426605001756,0.240793572992212,0.240793572992212,0.0,True
30
+ ClimaX,climax,global,99,shallow spatial adapter,shallow spatial adapter,0.0002340075376021003,0.2384111045733381,0.2384111045733381,0.0,True
31
+ ClimaX,climax,global,123,shallow spatial adapter,shallow spatial adapter,0.00025952340213823634,0.240793572992212,0.240793572992212,0.0,True
32
+ DLWP,dlwp,fire_prone,1,linear probe,linear probe,0.019747545663020845,0.7280364139105968,0.7280364139105968,0.0,True
33
+ DLWP,dlwp,fire_prone,7,pixel MLP head,shallow spatial adapter,0.018519739310339497,0.7185324707231184,0.762536895087842,0.044004424364723516,False
34
+ DLWP,dlwp,fire_prone,42,pixel MLP head,shallow spatial adapter,0.020762153794205103,0.6606900017471591,0.7728400679088107,0.11215006616165157,False
35
+ DLWP,dlwp,fire_prone,99,linear probe,shallow spatial adapter,0.02136633936888583,0.7187730423241409,0.7653346239087763,0.046561581584635414,False
36
+ DLWP,dlwp,fire_prone,123,pixel MLP head,linear probe,0.021118517500793188,0.7185324707231184,0.7321459738801157,0.013613503156997275,False
37
+ DLWP,dlwp,global,1,shallow spatial adapter,shallow spatial adapter,0.0006257446338466172,0.38023285660836226,0.38023285660836226,0.0,True
38
+ DLWP,dlwp,global,7,shallow spatial adapter,shallow spatial adapter,0.0005264872646085452,0.3432315705541329,0.3432315705541329,0.0,True
39
+ DLWP,dlwp,global,42,shallow spatial adapter,shallow spatial adapter,0.0006203713852571992,0.3405125814370199,0.3405125814370199,0.0,True
40
+ DLWP,dlwp,global,99,shallow spatial adapter,shallow spatial adapter,0.0007477128447471452,0.3979559626836394,0.3979559626836394,0.0,True
41
+ DLWP,dlwp,global,123,shallow spatial adapter,shallow spatial adapter,0.0007129763973023342,0.3797689460796109,0.3797689460796109,0.0,True
42
+ FCN,fcn,fire_prone,1,shallow spatial adapter,linear probe,0.01844011667219625,0.7185324707231184,0.7182175622542595,0.0,False
43
+ FCN,fcn,fire_prone,7,pixel MLP head,linear probe,0.02050876208409485,0.7185324707231184,0.7644129739607127,0.045880503237594294,False
44
+ FCN,fcn,fire_prone,42,shallow spatial adapter,shallow spatial adapter,0.018030815062946615,0.7197180735022655,0.7197180735022655,0.0,True
45
+ FCN,fcn,fire_prone,99,linear probe,linear probe,0.029098665712304895,0.726408418760773,0.726408418760773,0.0,True
46
+ FCN,fcn,fire_prone,123,pixel MLP head,linear probe,0.019943278646881796,0.7185324707231184,0.7310509974227326,0.012518526699614174,False
47
+ FCN,fcn,global,1,shallow spatial adapter,shallow spatial adapter,0.00037256097806901117,0.31167484413093016,0.31167484413093016,0.0,True
48
+ FCN,fcn,global,7,shallow spatial adapter,shallow spatial adapter,0.0003268363416054406,0.3051941376005135,0.3051941376005135,0.0,True
49
+ FCN,fcn,global,42,shallow spatial adapter,pixel MLP head,0.00041063897933390575,0.31987973649439366,0.2870596305028149,0.0,False
50
+ FCN,fcn,global,99,shallow spatial adapter,shallow spatial adapter,0.00038120453362995967,0.3054145960271247,0.3054145960271247,0.0,True
51
+ FCN,fcn,global,123,shallow spatial adapter,shallow spatial adapter,0.000387412312932838,0.3096850885545486,0.3096850885545486,0.0,True
52
+ FengWu,fengwu,fire_prone,1,shallow spatial adapter,shallow spatial adapter,0.022736128443885992,0.7269980510116651,0.7269980510116651,0.0,True
53
+ FengWu,fengwu,fire_prone,7,pixel MLP head,shallow spatial adapter,0.016801706970978273,0.7185324707231184,0.722901134194411,0.004368663471292611,False
54
+ FengWu,fengwu,fire_prone,42,shallow spatial adapter,shallow spatial adapter,0.021165185967854567,0.7265705731122933,0.7265705731122933,0.0,True
55
+ FengWu,fengwu,fire_prone,99,linear probe,shallow spatial adapter,0.02199779031689959,0.7185324707231184,0.7336829717908426,0.015150501067724198,False
56
+ FengWu,fengwu,fire_prone,123,pixel MLP head,shallow spatial adapter,0.020495144103834257,0.7185324707231184,0.7251252524331628,0.006592781710044404,False
57
+ FengWu,fengwu,global,1,shallow spatial adapter,shallow spatial adapter,0.000398077435365184,0.31071390711162444,0.31071390711162444,0.0,True
58
+ FengWu,fengwu,global,7,shallow spatial adapter,shallow spatial adapter,0.00036963872610239895,0.30641904273669174,0.30641904273669174,0.0,True
59
+ FengWu,fengwu,global,42,shallow spatial adapter,shallow spatial adapter,0.00036987379624400454,0.31219058559732665,0.31219058559732665,0.0,True
60
+ FengWu,fengwu,global,99,shallow spatial adapter,shallow spatial adapter,0.00042782651526874734,0.3111214819309595,0.3111214819309595,0.0,True
61
+ FengWu,fengwu,global,123,shallow spatial adapter,shallow spatial adapter,0.0004116035724473925,0.3145618361221859,0.3145618361221859,0.0,True
62
+ FuXi,fuxi,fire_prone,1,shallow spatial adapter,linear probe,0.01904942261850253,0.7246636456247585,0.7261195534617713,0.001455907837012771,False
63
+ FuXi,fuxi,fire_prone,7,shallow spatial adapter,shallow spatial adapter,0.018717347905238046,0.7235687421646468,0.7235687421646468,0.0,True
64
+ FuXi,fuxi,fire_prone,42,shallow spatial adapter,shallow spatial adapter,0.020363596550511454,0.7054843984273774,0.7054843984273774,0.0,True
65
+ FuXi,fuxi,fire_prone,99,pixel MLP head,shallow spatial adapter,0.02225455497934009,0.7203102915557309,0.7183426482806338,0.0,False
66
+ FuXi,fuxi,fire_prone,123,pixel MLP head,linear probe,0.021045021724179047,0.7185324707231184,0.7224961571477053,0.0039636864245868875,False
67
+ FuXi,fuxi,global,1,shallow spatial adapter,linear probe,0.0003596048414560045,0.3091927111996169,0.21516044416153002,0.0,False
68
+ FuXi,fuxi,global,7,shallow spatial adapter,shallow spatial adapter,0.00037118462783325537,0.3062654921252336,0.3062654921252336,0.0,True
69
+ FuXi,fuxi,global,42,shallow spatial adapter,shallow spatial adapter,0.0003830459807690152,0.316668570748256,0.316668570748256,0.0,True
70
+ FuXi,fuxi,global,99,shallow spatial adapter,shallow spatial adapter,0.0003758107890486513,0.3108110703043797,0.3108110703043797,0.0,True
71
+ FuXi,fuxi,global,123,shallow spatial adapter,shallow spatial adapter,0.00039999784430104664,0.3096924811079745,0.3096924811079745,0.0,True
72
+ Pangu-Weather,pangu_weather,fire_prone,1,pixel MLP head,pixel MLP head,0.014813375233921266,0.7185324707231184,0.7185324707231184,0.0,True
73
+ Pangu-Weather,pangu_weather,fire_prone,7,pixel MLP head,shallow spatial adapter,0.029883397445312192,0.0,0.7185324707231184,0.7185324707231184,False
74
+ Pangu-Weather,pangu_weather,fire_prone,42,pixel MLP head,shallow spatial adapter,0.02188826028256454,0.0,0.7185324707231184,0.7185324707231184,False
75
+ Pangu-Weather,pangu_weather,fire_prone,99,linear probe,shallow spatial adapter,0.02093558282208589,0.0,0.7185324707231184,0.7185324707231184,False
76
+ Pangu-Weather,pangu_weather,fire_prone,123,linear probe,linear probe,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,True
77
+ Pangu-Weather,pangu_weather,global,1,linear probe,shallow spatial adapter,0.00024254792826221397,0.23755358049655212,0.240793572992212,0.0032399924956598714,False
78
+ Pangu-Weather,pangu_weather,global,7,shallow spatial adapter,shallow spatial adapter,0.00028045576992618193,0.240793572992212,0.240793572992212,0.0,True
79
+ Pangu-Weather,pangu_weather,global,42,pixel MLP head,shallow spatial adapter,0.00021817709863716954,0.0,0.240793572992212,0.240793572992212,False
80
+ Pangu-Weather,pangu_weather,global,99,shallow spatial adapter,pixel MLP head,0.0003408299850116487,0.23917716245227552,0.229804399271568,0.0,False
81
+ Pangu-Weather,pangu_weather,global,123,shallow spatial adapter,shallow spatial adapter,0.0003300754798792237,0.240793572992212,0.240793572992212,0.0,True
82
+ Pangu-Weather,pangu6,fire_prone,1,shallow spatial adapter,shallow spatial adapter,0.02328829578760066,0.7231388400090598,0.7231388400090598,0.0,True
83
+ Pangu-Weather,pangu6,fire_prone,7,pixel MLP head,shallow spatial adapter,0.018664183428008234,0.7185324707231184,0.719461376864001,0.0009289061408825905,False
84
+ Pangu-Weather,pangu6,fire_prone,42,shallow spatial adapter,shallow spatial adapter,0.021420904714594617,0.7241279907754397,0.7241279907754397,0.0,True
85
+ Pangu-Weather,pangu6,fire_prone,99,linear probe,shallow spatial adapter,0.024740444457605402,0.7183382629739177,0.719015307962107,0.0006770449881893237,False
86
+ Pangu-Weather,pangu6,fire_prone,123,pixel MLP head,linear probe,0.02254610440161657,0.7185324707231184,0.7261721555350364,0.007639684811918013,False
87
+ Pangu-Weather,pangu6,global,1,shallow spatial adapter,shallow spatial adapter,0.00042881385578730365,0.32066974948800614,0.32066974948800614,0.0,True
88
+ Pangu-Weather,pangu6,global,7,shallow spatial adapter,shallow spatial adapter,0.00038395193539280824,0.31120670593952293,0.31120670593952293,0.0,True
89
+ Pangu-Weather,pangu6,global,42,shallow spatial adapter,shallow spatial adapter,0.00040651309469793043,0.32154249424354786,0.32154249424354786,0.0,True
90
+ Pangu-Weather,pangu6,global,99,shallow spatial adapter,shallow spatial adapter,0.0004450373086921994,0.3214547875801752,0.3214547875801752,0.0,True
91
+ Pangu-Weather,pangu6,global,123,shallow spatial adapter,shallow spatial adapter,0.00043738577276574255,0.31812983009681495,0.31812983009681495,0.0,True
92
+ Prithvi-WxC,prithvi_wxc,fire_prone,1,shallow spatial adapter,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,True
93
+ Prithvi-WxC,prithvi_wxc,fire_prone,7,shallow spatial adapter,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,True
94
+ Prithvi-WxC,prithvi_wxc,fire_prone,42,pixel MLP head,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
95
+ Prithvi-WxC,prithvi_wxc,fire_prone,99,linear probe,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
96
+ Prithvi-WxC,prithvi_wxc,fire_prone,123,shallow spatial adapter,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,True
97
+ Prithvi-WxC,prithvi_wxc,global,1,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
98
+ Prithvi-WxC,prithvi_wxc,global,7,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
99
+ Prithvi-WxC,prithvi_wxc,global,42,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
100
+ Prithvi-WxC,prithvi_wxc,global,99,shallow spatial adapter,shallow spatial adapter,0.0002454330184381399,0.23755358049655212,0.23755358049655212,0.0,True
101
+ Prithvi-WxC,prithvi_wxc,global,123,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
102
+ Reference,reference,fire_prone,1,shallow spatial adapter,linear probe,0.10204224118176683,0.799032457577039,0.8685988450931266,0.06956638751608757,False
103
+ Reference,reference,fire_prone,7,shallow spatial adapter,shallow spatial adapter,0.1323902067230726,0.8027807546359551,0.8027807546359551,0.0,True
104
+ Reference,reference,fire_prone,42,shallow spatial adapter,shallow spatial adapter,0.12048427320762313,0.8358151221553631,0.8358151221553631,0.0,True
105
+ Reference,reference,fire_prone,99,shallow spatial adapter,linear probe,0.11947246238005743,0.8534759193943048,0.9039923296574045,0.05051641026309972,False
106
+ Reference,reference,fire_prone,123,shallow spatial adapter,pixel MLP head,0.11551882470432043,0.8066689866810086,0.8567215417854325,0.05005255510442386,False
107
+ Reference,reference,global,1,shallow spatial adapter,linear probe,0.002624341503354088,0.6186566066408326,0.7286109603326707,0.10995435369183815,False
108
+ Reference,reference,global,7,shallow spatial adapter,shallow spatial adapter,0.0030317345997110698,0.5002193417313746,0.5002193417313746,0.0,True
109
+ Reference,reference,global,42,shallow spatial adapter,shallow spatial adapter,0.0032729435045747413,0.7239943741463363,0.7239943741463363,0.0,True
110
+ Reference,reference,global,99,shallow spatial adapter,linear probe,0.0031953098868323193,0.6698005926442897,0.7647466876509988,0.09494609500670914,False
111
+ Reference,reference,global,123,shallow spatial adapter,pixel MLP head,0.0025603887793133394,0.5009707461135111,0.735221546471909,0.23425080035839785,False
112
+ StormCast,stormcast,fire_prone,1,linear probe,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
113
+ StormCast,stormcast,fire_prone,7,linear probe,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
114
+ StormCast,stormcast,fire_prone,42,linear probe,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
115
+ StormCast,stormcast,fire_prone,99,pixel MLP head,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
116
+ StormCast,stormcast,fire_prone,123,linear probe,shallow spatial adapter,0.02093558282208589,0.7185324707231184,0.7185324707231184,0.0,False
117
+ StormCast,stormcast,global,1,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
118
+ StormCast,stormcast,global,7,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
119
+ StormCast,stormcast,global,42,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
120
+ StormCast,stormcast,global,99,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.2399251001974223,0.2399251001974223,0.0,True
121
+ StormCast,stormcast,global,123,shallow spatial adapter,shallow spatial adapter,0.0002463357401629007,0.240793572992212,0.240793572992212,0.0,True
artifacts/results/selection_regret_rq2_figure_values.csv ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ feature_source,global_mean_pp,global_std_pp,top20_mean_pp,top20_std_pp
2
+ FireWx-FM ref.,7.3831,7.4536,2.9385,2.7513
3
+ Prithvi-WxC,0.0000,0.0000,0.0000,0.0000
4
+ Aurora,4.9455,10.6974,14.3706,32.1337
5
+ ClimaX,0.1296,0.1775,0.0000,0.0000
6
+ StormCast,0.0000,0.0000,0.0000,0.0000
7
+ DLWP,0.0000,0.0000,4.4634,4.3561
8
+ FCN,0.0000,0.0000,1.1680,1.9872
9
+ FengWu,0.0000,0.0000,0.5222,0.6239
10
+ FuXi,0.0000,0.0000,0.2833,0.3681
11
+ Pangu-Weather,0.0000,0.0000,0.1868,0.3255
12
+ AlphaEarth,17.2217,8.8492,3.8804,5.9483
artifacts/results/selection_regret_scope_sweep_20260505.csv ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_tag,label,scope,scope_label,n,seeds,exact_regret_mean,exact_regret_std,exact_regret_min,exact_regret_max,tolerated_regret_mean,tolerated_regret_std,tolerated_regret_min,tolerated_regret_max,union_regret_mean,union_regret_std,union_regret_min,union_regret_max
2
+ reference,FireWx-FM ref.,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.07383089600948442,0.07453636071372995,0.0,0.17497107865629713,0.07383089600948442,0.07453636071372995,0.0,0.17497107865629713
3
+ reference,FireWx-FM ref.,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.003663718115532055,0.006812231244812292,0.0,0.015676762201120686,0.003663718115532055,0.006812231244812292,0.0,0.015676762201120686
4
+ reference,FireWx-FM ref.,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.012275489592085752,0.012665162001740834,0.0,0.02670880922526031,0.012275489592085752,0.012665162001740834,0.0,0.02670880922526031
5
+ reference,FireWx-FM ref.,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.029384646387840017,0.02751315335001922,0.0,0.05675140203555318,0.029384646387840017,0.02751315335001922,0.0,0.05675140203555318
6
+ prithvi_wxc,Prithvi-WxC,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
7
+ prithvi_wxc,Prithvi-WxC,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
8
+ prithvi_wxc,Prithvi-WxC,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
9
+ prithvi_wxc,Prithvi-WxC,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
10
+ aurora,Aurora,global,\(\Omega=\)global,5,1 7 42 99 123,0.00010153879814819402,0.00021861477435572763,0.0,0.0004925501476206198,0.04945471159670635,0.10697394238964528,0.0,0.240793572992212,0.04945471159670635,0.10697394238964528,0.0,0.240793572992212
11
+ aurora,Aurora,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.023667154505540456,0.05292136630837888,0.0,0.11833577252770228,0.1542829840966487,0.34498724021162547,0.0,0.7714149204832434,0.1542829840966487,0.34498724021162547,0.0,0.7714149204832434
12
+ aurora,Aurora,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.014651279478173606,0.032761256870543834,0.0,0.07325639739086803,0.1399343063221699,0.31290262132065055,0.0,0.6996715316108496,0.1399343063221699,0.31290262132065055,0.0,0.6996715316108496
13
+ aurora,Aurora,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.008202508825959588,0.01834136732088763,0.0,0.04101254412979794,0.1437064941446237,0.32133748971555404,0.0,0.7185324707231184,0.1437064941446237,0.32133748971555404,0.0,0.7185324707231184
14
+ climax,ClimaX,global,\(\Omega=\)global,5,1 7 42 99 123,3.0287686240700486e-06,4.147312242167625e-06,0.0,7.571921560175121e-06,0.0012959969982639485,0.0017746169760203706,0.0,0.0032399924956598714,0.0012959969982639485,0.0017746169760203706,0.0,0.0032399924956598714
15
+ climax,ClimaX,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
16
+ climax,ClimaX,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
17
+ climax,ClimaX,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
18
+ stormcast,StormCast,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
19
+ stormcast,StormCast,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
20
+ stormcast,StormCast,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
21
+ stormcast,StormCast,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
22
+ dlwp,DLWP,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
23
+ dlwp,DLWP,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0048037709215293075,0.006217185202152866,0.0,0.015203005078871956,0.016716228534155796,0.016079313546074458,0.0,0.03305057342744666,0.016716228534155796,0.016079313546074458,0.0,0.03305057342744666
24
+ dlwp,DLWP,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0017281632798742507,0.002514722758075371,0.0,0.005523780499856246,0.02846514801700826,0.026938012702643194,0.0,0.053927677500854476,0.02846514801700826,0.026938012702643194,0.0,0.053927677500854476
25
+ dlwp,DLWP,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0007702319787454587,0.0010995336594539604,0.0,0.0023651634514294945,0.04463354681768479,0.04356064433532197,0.0,0.11215006616165157,0.04463354681768479,0.04356064433532197,0.0,0.11215006616165157
26
+ fcn,FCN,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
27
+ fcn,FCN,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0006342898232943345,0.0009899554165032742,0.0,0.002257520679520411,0.004509624980300697,0.010070611656609236,0.0,0.022524473456150496,0.004509624980300697,0.010070611656609236,0.0,0.022524473456150496
28
+ fcn,FCN,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.00021156854817603877,0.0004730816556225618,0.0,0.0010578427408801938,0.004199537050817615,0.009390450319657174,0.0,0.020997685254088072,0.004199537050817615,0.009390450319657174,0.0,0.020997685254088072
29
+ fcn,FCN,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,5.754560074337778e-06,1.2867587506825515e-05,0.0,2.877280037168889e-05,0.011679805987441694,0.019872372458657642,0.0,0.045880503237594294,0.011679805987441694,0.019872372458657642,0.0,0.045880503237594294
30
+ fengwu,FengWu,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
31
+ fengwu,FengWu,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0005029843170376968,0.0008109166521114917,0.0,0.0018628094907809783,0.008795951947678215,0.005532321338017505,0.0,0.01484136735033148,0.008795951947678215,0.005532321338017505,0.0,0.01484136735033148
32
+ fengwu,FengWu,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.000495228089292582,0.0007349190216431337,0.0,0.0016387212062008855,0.00402300475984525,0.005510851442075993,0.0,0.010273937098576491,0.00402300475984525,0.005510851442075993,0.0,0.010273937098576491
33
+ fengwu,FengWu,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0006908222234409067,0.0011910586589384115,0.0,0.0027505832409660327,0.005222389249812243,0.0062394095558402415,0.0,0.015150501067724198,0.005222389249812243,0.0062394095558402415,0.0,0.015150501067724198
34
+ fuxi,FuXi,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
35
+ fuxi,FuXi,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.002973545331200933,0.0023946274991058026,0.0010927807990139538,0.007024214143542151,0.013545122545609134,0.02097023683418404,0.0,0.050156261654859424,0.013545122545609134,0.02097023683418404,0.0,0.050156261654859424
36
+ fuxi,FuXi,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.001383793743586542,0.0019248128430711165,0.0,0.003938013087198336,0.0016559834970027332,0.0037028916689159307,0.0,0.008279917485013666,0.0016559834970027332,0.0037028916689159307,0.0,0.008279917485013666
37
+ fuxi,FuXi,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.00283318355751887,0.0036808289681375247,0.0,0.008746323525994693,0.00283318355751887,0.0036808289681375247,0.0,0.008746323525994693
38
+ pangu6,Pangu-Weather,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
39
+ pangu6,Pangu-Weather,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.003154674487241463,0.002546125713211599,0.0,0.005711594157251587,0.007592888149777122,0.00897418737588444,0.0,0.019790633919317124,0.007592888149777122,0.00897418737588444,0.0,0.019790633919317124
40
+ pangu6,Pangu-Weather,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0017345627303725214,0.0019305189318827886,0.0,0.004535321555179647,0.003047840737004992,0.005053805614558161,0.0,0.011660780793438352,0.003047840737004992,0.005053805614558161,0.0,0.011660780793438352
41
+ pangu6,Pangu-Weather,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0007280423771922354,0.001178746460551365,0.0,0.0027096086413018403,0.0018679847512695024,0.0032548337047755126,0.0,0.007639684811918013,0.0018679847512695024,0.0032548337047755126,0.0,0.007639684811918013
42
+ alphaearth,AlphaEarth,global,\(\Omega=\)global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.1722171037486726,0.08849214830495522,0.050655329891645895,0.272468682523694,0.1722171037486726,0.08849214830495522,0.050655329891645895,0.272468682523694
43
+ alphaearth,AlphaEarth,top5,\(\Omega=\)top 5\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.06384618090125256,0.04965276403138872,0.0,0.1365277562230962,0.06384618090125256,0.04965276403138872,0.0,0.1365277562230962
44
+ alphaearth,AlphaEarth,top10,\(\Omega=\)top 10\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.06573776411084173,0.06897015340160571,0.0,0.1615566566666954,0.06573776411084173,0.06897015340160571,0.0,0.1615566566666954
45
+ alphaearth,AlphaEarth,top20,\(\Omega=\)top 20\%,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.038803552655092256,0.0594825313313219,0.0,0.13482302181308625,0.038803552655092256,0.0594825313313219,0.0,0.13482302181308625
artifacts/results/selection_regret_scope_sweep_20260505.generated.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[!t]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{4pt}
5
+ \caption{Fixed-feature selection-regret sweep across evaluation scopes. Values are percentage-point regret \(\delta = D(h_D)-D(h_R)\) under union-\(F_1\). Top-\(k\) scopes are train-defined fire-prone masks. Rows report mean with small std over five seeds.}
6
+ \label{tab:selection_regret_scope_sweep}
7
+ \begin{tabular}{lcccc}
8
+ \toprule
9
+ \textbf{Feature source} & \textbf{\(\Omega=\)global} & \textbf{\(\Omega=\)top 5\%} & \textbf{\(\Omega=\)top 10\%} & \textbf{\(\Omega=\)top 20\%} \\
10
+ \midrule
11
+ \textcolor{blue}{FireWx-FM ref.} & \ms{7.3831}{7.4536} & \ms{0.3664}{0.6812} & \ms{1.2275}{1.2665} & \ms{2.9385}{2.7513} \\
12
+ Prithvi-WxC & 0.0000 & 0.0000 & 0.0000 & 0.0000 \\
13
+ Aurora & \ms{4.9455}{10.6974} & \ms{15.4283}{34.4987} & \ms{13.9934}{31.2903} & \ms{14.3706}{32.1337} \\
14
+ ClimaX & \ms{0.1296}{0.1775} & 0.0000 & 0.0000 & 0.0000 \\
15
+ StormCast & 0.0000 & 0.0000 & 0.0000 & 0.0000 \\
16
+ DLWP & 0.0000 & \ms{1.6716}{1.6079} & \ms{2.8465}{2.6938} & \ms{4.4634}{4.3561} \\
17
+ FCN & 0.0000 & \ms{0.4510}{1.0071} & \ms{0.4200}{0.9390} & \ms{1.1680}{1.9872} \\
18
+ FengWu & 0.0000 & \ms{0.8796}{0.5532} & \ms{0.4023}{0.5511} & \ms{0.5222}{0.6239} \\
19
+ FuXi & 0.0000 & \ms{1.3545}{2.0970} & \ms{0.1656}{0.3703} & \ms{0.2833}{0.3681} \\
20
+ Pangu-Weather & 0.0000 & \ms{0.7593}{0.8974} & \ms{0.3048}{0.5054} & \ms{0.1868}{0.3255} \\
21
+ AlphaEarth & \ms{17.2217}{8.8492} & \ms{6.3846}{4.9653} & \ms{6.5738}{6.8970} & \ms{3.8804}{5.9483} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
artifacts/results/selection_regret_scope_sweep_20260505.json ADDED
The diff for this file is too large to render. See raw diff
 
artifacts/results/selection_regret_summary.csv ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_tag,label,scope,n,seeds,exact_regret_mean,exact_regret_std,tolerated_regret_mean,tolerated_regret_std,union_regret_mean,union_regret_std
2
+ reference,Reference,global,5,1 7 42 99 123,0.0,0.0,0.08783024981138902,0.09670495645481135,0.08783024981138902,0.09670495645481135
3
+ reference,Reference,fire_prone,5,1 7 42 99 123,0.0,0.0,0.03402707057672223,0.032044658643147844,0.03402707057672223,0.032044658643147844
4
+ prithvi_wxc,Prithvi-WxC,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
5
+ prithvi_wxc,Prithvi-WxC,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
6
+ aurora,Aurora,global,5,1 7 42 99 123,0.00020004882767231798,0.00026703384456332115,0.09851983041506818,0.1298781980037557,0.09851983041506818,0.1298781980037557
7
+ aurora,Aurora,fire_prone,5,1 7 42 99 123,0.008202508825959588,0.01834136732088763,0.14391889430974364,0.32121904665016227,0.14391889430974364,0.32121904665016227
8
+ climax,ClimaX,global,5,1 7 42 99 123,3.0287686240700486e-06,4.147312242167625e-06,0.0012959969982639485,0.0017746169760203706,0.0012959969982639485,0.0017746169760203706
9
+ climax,ClimaX,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
10
+ stormcast,StormCast,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
11
+ stormcast,StormCast,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
12
+ pangu_weather,Pangu-Weather,global,5,1 7 42 99 123,0.00013033979247265275,0.0002685372203690466,0.048806713097574374,0.10733308684741971,0.048806713097574374,0.10733308684741971
13
+ pangu_weather,Pangu-Weather,fire_prone,5,1 7 42 99 123,0.027875386332505546,0.02348779386900393,0.43111948243387105,0.39355644251497235,0.43111948243387105,0.39355644251497235
14
+ dlwp,DLWP,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
15
+ dlwp,DLWP,fire_prone,5,1 7 42 99 123,0.0007702319787454587,0.0010995336594539604,0.043265915053601556,0.04332331365579739,0.043265915053601556,0.04332331365579739
16
+ fcn,FCN,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
17
+ fcn,FCN,fire_prone,5,1 7 42 99 123,5.960229415004348e-06,1.3327478133443526e-05,0.011679805987441694,0.019872372458657642,0.011679805987441694,0.019872372458657642
18
+ fengwu,FengWu,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
19
+ fengwu,FengWu,fire_prone,5,1 7 42 99 123,0.0006908222234409067,0.0011910586589384115,0.005222389249812243,0.0062394095558402415,0.005222389249812243,0.0062394095558402415
20
+ fuxi,FuXi,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
21
+ fuxi,FuXi,fire_prone,5,1 7 42 99 123,0.0,0.0,0.0010839188523199318,0.0017288780545672386,0.0010839188523199318,0.0017288780545672386
22
+ pangu6,Pangu-Weather,global,5,1 7 42 99 123,0.0,0.0,0.0,0.0,0.0,0.0
23
+ pangu6,Pangu-Weather,fire_prone,5,1 7 42 99 123,0.0007280423771922354,0.001178746460551365,0.0018491271881979853,0.0032630386057089294,0.0018491271881979853,0.0032630386057089294
24
+ alphaearth,AlphaEarth,global,5,1 7 42 99 123,0.0,0.0,0.1722171037486726,0.08849214830495522,0.1722171037486726,0.08849214830495522
25
+ alphaearth,AlphaEarth,fire_prone,5,1 7 42 99 123,0.0,0.0,0.038803552655092256,0.0594825313313219,0.038803552655092256,0.0594825313313219
artifacts/results/selection_regret_tolerance_family_table.generated.tex ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ % Replaced by the all-backbone value table in sections/appendix.tex
2
+ % (Table~\ref{tab:appendix_selection_regret_tolerance}).
data_sources/DATA_SOURCES.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data Sources
2
+
3
+ This repository does not redistribute raw data. The table below records the
4
+ resources used by the paper, their role in the experiments, and public access
5
+ entry points. Users must obtain each source under its own terms.
6
+
7
+ | Source | Role in paper | Public access entry point |
8
+ |---|---|---|
9
+ | NOAA High-Resolution Rapid Refresh (HRRR) | Dynamic weather fields for the California regional gridded occupancy inputs. | NOAA/NCEI HRRR product page: <https://www.ncei.noaa.gov/products/weather-climate-models/high-resolution-rapid-refresh>; AWS Open Data archive: <https://registry.opendata.aws/noaa-hrrr-pds/>. |
10
+ | NASA FIRMS active-fire detections | Active-fire detections used to derive gridded occupancy labels. | FIRMS download and API services: <https://firms.modaps.eosdis.nasa.gov/download/> and <https://firms.modaps.eosdis.nasa.gov/api/>. |
11
+ | LANDFIRE 40 Fire Behavior Fuel Models | Static fuel layer used in the FireWx-FM gridded input. | LANDFIRE data access portal: <https://landfire.gov/data>. |
12
+ | LANDFIRE Forest Canopy Cover | Static canopy layer used in the FireWx-FM gridded input. | LANDFIRE data access portal: <https://landfire.gov/data>. |
13
+ | Wildfire Risk to Communities housing-unit density | Static exposure layer used in the FireWx-FM gridded input. | Wildfire Risk to Communities data access: <https://wildfirerisk.org/download/>. |
14
+ | LandScan Global 2024 | Static population layer used in the FireWx-FM gridded input. | Oak Ridge National Laboratory LandScan access: <https://landscan.ornl.gov/>. |
15
+ | WFIGS incident/perimeter attributes | Event-level incident metadata for supporting burned-area and analog tasks. | NIFC Open Data portal for WFIGS layers: <https://data-nifc.opendata.arcgis.com/>. |
16
+ | MTBS burned area and burn severity | Event-scale burned-area and burn-severity records for supporting tasks. | MTBS data access and direct download pages: <https://www.mtbs.gov/> and <https://www.mtbs.gov/direct-download>. |
17
+ | Earth-FM/backbone sources | Frozen feature sources for transferred Earth-FM comparisons. | Original model providers and their terms. Examples include Hugging Face model cards, model-provider GitHub repositories, and provider-hosted model files. |
18
+
19
+ ## Notes
20
+
21
+ - The paper places gridded resources on a projected 5 km EPSG:5070 grid.
22
+ - The bundled artifacts contain summary values only. They are not a substitute
23
+ for the original data.
24
+ - Full raw-data reruns require users to obtain each source independently and to
25
+ construct the intermediate grids/features described in the paper.
26
+ - Access mechanisms and licensing can change. The links above are entry points,
27
+ not redistributed copies.
docs/artifact_map.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Paper Artifact Map
2
+
3
+ This map links every table and figure label in the current manuscript to the
4
+ public release artifact and its provenance. Final output checksums are stored in
5
+ `artifacts/manifests/paper_outputs.sha256`.
6
+
7
+ ## Figures
8
+
9
+ | Paper label | Release file | Provenance |
10
+ |---|---|---|
11
+ | `fig:toy_occupancy_contract` | `paper_outputs/figures/matching.pdf` | Static vector schematic used by the manuscript. |
12
+ | `fig:task_contract_tiles` | `paper_outputs/figures/fig_task_contract_tiles.pdf` | Static contract-map figure used by the manuscript. |
13
+ | `fig:selection_regret_diagnostic` | `paper_outputs/figures/fig_selection_regret_rq2.tikz` | Rebuilt by `scripts/build_selection_regret_rq2_figure.py` from `artifacts/results/selection_regret_scope_sweep_20260505.csv`. |
14
+ | `fig:fireprone_contract_progression` | `paper_outputs/figures/fig_fireprone_contract_progression_compact.pdf` | Rebuilt by `scripts/build_fireprone_contract_progression_figure.py` from `artifacts/results/fireprone_contract_progression_summary.json`. |
15
+ | `fig:task_comparator_normalized_map` | `paper_outputs/figures/fig_task_rank_map.pdf` | Rebuilt by `scripts/build_task_rank_map.py` from `tab_primary_results.tex` and `tab_supporting_results.tex`. |
16
+
17
+ ## Main Tables
18
+
19
+ | Paper label | Release file | Provenance |
20
+ |---|---|---|
21
+ | `tab:primary_results` | `paper_outputs/tables/tab_primary_results.tex` | Frozen paper-output TeX extracted from the current manuscript source and verified by checksum. Raw reruns require the task scripts and non-redistributed feature caches. |
22
+ | `tab:supporting_results` | `paper_outputs/tables/tab_supporting_results.tex` | Frozen paper-output TeX extracted from the current manuscript source and verified by checksum. Raw reruns require the task scripts and non-redistributed feature caches. |
23
+
24
+ ## Appendix Tables
25
+
26
+ | Paper label | Release file | Provenance |
27
+ |---|---|---|
28
+ | `tab:app_matching_rule_params` | `paper_outputs/tables/tab_app_matching_rule_params.tex` | Contract parameter table from manuscript source, verified by checksum. |
29
+ | `tab:app_contract_params_full` | `paper_outputs/tables/tab_app_contract_params_full.tex` | Contract parameter table from manuscript source, verified by checksum. |
30
+ | `tab:app_scope_params` | `paper_outputs/tables/tab_app_scope_params.tex` | Scope parameter table from manuscript source, verified by checksum. |
31
+ | `tab:fireprone_contract_progression` | `paper_outputs/tables/tab_fireprone_contract_progression.tex` | Values from `artifacts/results/fireprone_contract_progression_summary.json`. |
32
+ | `tab:appendix_selection_regret_tolerance` | `paper_outputs/tables/tab_appendix_selection_regret_tolerance.tex` | Values from selection-regret summary artifacts. |
33
+ | `tab:app_occupancy_ppr_scope` | `paper_outputs/tables/tab_app_occupancy_ppr_scope.tex` | Values from `artifacts/results/fireprone_contract_progression_summary.json`. |
34
+ | `tab:app_spread_ap_by_scope` | `paper_outputs/tables/tab_app_spread_ap_by_scope.tex` | Frozen paper-output TeX extracted from current manuscript source, verified by checksum. |
35
+ | `tab:app_burned_area_median_acre` | `paper_outputs/tables/tab_app_burned_area_median_acre.tex` | Frozen paper-output TeX extracted from current manuscript source, verified by checksum. |
36
+ | `tab:app_analog_rank_depth` | `paper_outputs/tables/tab_app_analog_rank_depth.tex` | Frozen paper-output TeX extracted from current manuscript source, verified by checksum. |
37
+ | `tab:app_smoke_high_event` | `paper_outputs/tables/tab_app_smoke_high_event.tex` | Frozen paper-output TeX extracted from current manuscript source, verified by checksum. |
38
+ | `tab:app_heat_event_pr` | `paper_outputs/tables/tab_app_heat_event_pr.tex` | Frozen paper-output TeX extracted from current manuscript source, verified by checksum. |
39
+ | `tab:app_seed_robustness` | `paper_outputs/tables/tab_app_seed_robustness.tex` | Seed summary table from manuscript source, verified by checksum. |
40
+ | `tab:app_head_architectures` | `paper_outputs/tables/tab_app_head_architectures.tex` | Architecture description table from manuscript source, verified by checksum. |
41
+
42
+ ## Reproduction Commands
43
+
44
+ ```bash
45
+ python3 scripts/reproduce_paper_outputs.py
46
+ ```
47
+
48
+ This command rebuilds the outputs that depend only on released summary files,
49
+ checks all final paper-output hashes, and runs the release audit.
50
+
51
+ ## Raw Rerun Boundary
52
+
53
+ Some tables depend on raw gridded data, event data, or backbone feature caches
54
+ that are not redistributed. For public release, we provide the compact summary
55
+ artifacts used to reproduce the displayed paper values and document the raw data
56
+ sources separately.
docs/huggingface_release_design.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Release Design
2
+
3
+ This release follows the common Hugging Face pattern for research artifacts:
4
+
5
+ - `README.md` is the public card. It contains YAML metadata, intended use,
6
+ limitations, data provenance, reproduction commands, and citation text.
7
+ - `paper_outputs/` stores the final TeX, TikZ, and PDF artifacts used by the
8
+ manuscript.
9
+ - `artifacts/results/` stores compact CSV/JSON summaries that can be public.
10
+ - `artifacts/manifests/` maps paper labels to files and records output hashes.
11
+ - `data_sources/` documents external data resources without redistributing them.
12
+ - `experiments/` contains raw-rerun reference scripts and Slurm templates.
13
+
14
+ The repository is intentionally a paper-artifact release rather than a dataset
15
+ mirror or model-weight release. Full raw-data reruns require separately obtained
16
+ source data and local feature caches.
experiments/README.md ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Raw Rerun Notes
2
+
3
+ This directory documents the raw rerun boundary. The public artifact release does
4
+ not include local Slurm scripts with machine paths, raw wildfire inputs, or local
5
+ feature caches. Full raw reruns require users to obtain the source data listed in
6
+ `data_sources/DATA_SOURCES.md` and adapt the templates below to their own cluster.
7
+
8
+ The bundled paper-output reproduction path does not require these raw reruns.
9
+
10
+ ## Reference Scripts
11
+
12
+ The scripts under `raw_reference/` are sanitized references for the task-level
13
+ runs used in the paper. They preserve the command-line interfaces and evaluation
14
+ logic, but they require user-provided data tables, feature caches, and model
15
+ dependencies.
16
+
17
+ If a script imports local project modules from an external preprocessing tree,
18
+ set `WILDFIRE_FM_EXTRA_PYTHONPATH` before running it:
19
+
20
+ ```bash
21
+ export WILDFIRE_FM_EXTRA_PYTHONPATH=/path/to/your/project/src:/path/to/extra/site-packages
22
+ ```
23
+
24
+ The Slurm file in `slurm/` is a template only. Replace all placeholder paths
25
+ before submitting jobs on your own cluster.
experiments/raw_reference/run_selection_regret_scope_sweep_20260505.py ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Run fixed-feature head-selection regret for global and top-k fire-prone scopes."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import csv
8
+ import importlib.util
9
+ import json
10
+ import math
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import numpy as np
15
+
16
+
17
+ BASE_RUNNER = Path(__file__).resolve().parent / "task_scripts" / "run_all_backbone_selection_regret_20260504.py"
18
+ spec = importlib.util.spec_from_file_location("selection_regret_base_20260504", BASE_RUNNER)
19
+ if spec is None or spec.loader is None:
20
+ raise RuntimeError(f"Cannot import base runner: {BASE_RUNNER}")
21
+ base = importlib.util.module_from_spec(spec)
22
+ spec.loader.exec_module(base)
23
+
24
+ head_control = base.head_control
25
+
26
+ SCOPE_FRACS = (0.05, 0.10, 0.20)
27
+ SCOPE_ORDER = ("global", "top5", "top10", "top20")
28
+ SCOPE_LABELS = {
29
+ "global": "global",
30
+ "top5": "top 5%",
31
+ "top10": "top 10%",
32
+ "top20": "top 20%",
33
+ }
34
+
35
+
36
+ def parse_args() -> argparse.Namespace:
37
+ parser = argparse.ArgumentParser(description="Selection-regret scope sweep.")
38
+ parser.add_argument("--source-kind", choices=("reference", "attached", "spatial", "alphaearth"), required=True)
39
+ parser.add_argument("--feature-root", type=Path, required=True)
40
+ parser.add_argument("--daily-rows-csv", type=Path)
41
+ parser.add_argument("--support-dir", type=Path)
42
+ parser.add_argument("--alphaearth-cache-root", type=Path)
43
+ parser.add_argument("--output-dir", type=Path, required=True)
44
+ parser.add_argument("--fm-family", type=str, required=True)
45
+ parser.add_argument("--model-tag", type=str, required=True)
46
+ parser.add_argument("--seed", type=int, required=True)
47
+ parser.add_argument("--heads", nargs="+", choices=base.HEADS, default=["linear", "pixel_mlp", "shallow"])
48
+ parser.add_argument("--batch-size", type=int, default=8)
49
+ parser.add_argument("--epochs", type=int, default=2)
50
+ parser.add_argument("--learning-rate", type=float, default=8e-4)
51
+ parser.add_argument("--weight-decay", type=float, default=1e-5)
52
+ parser.add_argument("--pos-weight-cap", type=float, default=150.0)
53
+ parser.add_argument("--device", choices=("cpu", "cuda", "auto"), default="cpu")
54
+ parser.add_argument(
55
+ "--metric-thresholds",
56
+ nargs="+",
57
+ type=float,
58
+ default=[
59
+ 1e-5,
60
+ 2e-5,
61
+ 5e-5,
62
+ 1e-4,
63
+ 2e-4,
64
+ 5e-4,
65
+ 1e-3,
66
+ 2e-3,
67
+ 5e-3,
68
+ 1e-2,
69
+ 2e-2,
70
+ 5e-2,
71
+ 8e-2,
72
+ 1e-1,
73
+ 1.5e-1,
74
+ 2e-1,
75
+ 3e-1,
76
+ 5e-1,
77
+ ],
78
+ )
79
+ parser.add_argument("--variants", nargs="+", default=["identity"])
80
+ parser.add_argument("--fire-prone-top-fracs", nargs="+", type=float, default=list(SCOPE_FRACS))
81
+ parser.add_argument("--temporal-steps", type=int, default=3)
82
+ parser.add_argument("--spatial-radius", type=int, default=8)
83
+ parser.add_argument("--buffer-radius", type=int, default=8)
84
+ parser.add_argument("--boundary-radius", type=int, default=8)
85
+ parser.add_argument("--coarse-factor", type=int, default=8)
86
+ parser.add_argument("--time-step-hours", type=int, default=6)
87
+ return parser.parse_args()
88
+
89
+
90
+ def scope_name(top_frac: float) -> str:
91
+ pct = int(round(float(top_frac) * 100.0))
92
+ return f"top{pct}"
93
+
94
+
95
+ def scope_label(top_frac: float) -> str:
96
+ pct = int(round(float(top_frac) * 100.0))
97
+ return f"top {pct}%"
98
+
99
+
100
+ def build_scope_masks(
101
+ split_rows: dict[str, list[dict[str, str]]],
102
+ store: Any,
103
+ top_fracs: list[float],
104
+ ) -> tuple[dict[str, np.ndarray | None], dict[str, dict[str, Any]]]:
105
+ masks: dict[str, np.ndarray | None] = {"global": None}
106
+ meta: dict[str, dict[str, Any]] = {
107
+ "global": {
108
+ "scope_name": "global",
109
+ "reported_as": "global",
110
+ "top_fraction": None,
111
+ }
112
+ }
113
+ for frac in top_fracs:
114
+ name = scope_name(frac)
115
+ mask, mask_meta = head_control.build_fire_prone_mask(split_rows["train"], store, float(frac))
116
+ masks[name] = mask
117
+ meta[name] = {
118
+ "scope_name": name,
119
+ "reported_as": scope_label(frac),
120
+ **mask_meta,
121
+ }
122
+ return masks, meta
123
+
124
+
125
+ def build_posthoc_rows_for_scopes(
126
+ probs: np.ndarray,
127
+ targets: np.ndarray,
128
+ sample_times: np.ndarray,
129
+ split: str,
130
+ scope_masks: dict[str, np.ndarray | None],
131
+ args: argparse.Namespace,
132
+ ) -> list[dict[str, object]]:
133
+ rows_out: list[dict[str, object]] = []
134
+ for threshold in [float(v) for v in args.metric_thresholds]:
135
+ base_binary = probs >= threshold
136
+ for variant in args.variants:
137
+ binary = head_control.apply_variant(base_binary, variant)
138
+ tensors = head_control.evaluate_threshold_variant(
139
+ binary_np=binary,
140
+ target_np=targets,
141
+ sample_times=sample_times,
142
+ time_step_hours=args.time_step_hours,
143
+ temporal_steps=args.temporal_steps,
144
+ spatial_radius=args.spatial_radius,
145
+ buffer_radius=args.buffer_radius,
146
+ boundary_radius=args.boundary_radius,
147
+ coarse_factor=args.coarse_factor,
148
+ tolerance_hours=args.temporal_steps * args.time_step_hours,
149
+ )
150
+ for scope, region_mask in scope_masks.items():
151
+ row: dict[str, object] = {
152
+ "split": split,
153
+ "scope": scope,
154
+ "threshold": float(threshold),
155
+ "variant": variant,
156
+ "time_step_hours": int(args.time_step_hours),
157
+ "temporal_steps": int(args.temporal_steps),
158
+ "tolerance_hours": int(args.temporal_steps * args.time_step_hours),
159
+ "spatial_radius": int(args.spatial_radius),
160
+ "buffer_radius": int(args.buffer_radius),
161
+ "boundary_radius": int(args.boundary_radius),
162
+ "coarse_factor": int(args.coarse_factor),
163
+ }
164
+ row.update(head_control.metrics_for_scope(tensors, region_mask))
165
+ rows_out.append(row)
166
+ return rows_out
167
+
168
+
169
+ def read_csv(path: Path) -> list[dict[str, str]]:
170
+ with path.open("r", encoding="utf-8", newline="") as fh:
171
+ return list(csv.DictReader(fh))
172
+
173
+
174
+ def load_head_summary(
175
+ head_dir: Path,
176
+ head_arch: str,
177
+ scopes: tuple[str, ...],
178
+ ) -> tuple[list[dict[str, object]], dict[str, dict[str, float]], dict[str, object]] | None:
179
+ posthoc_path = head_dir / "posthoc_rows.csv"
180
+ summary_path = head_dir / "summary.json"
181
+ if not posthoc_path.exists() or not summary_path.exists():
182
+ return None
183
+ rows = [dict(row) for row in read_csv(posthoc_path)]
184
+ if not rows:
185
+ return None
186
+ try:
187
+ summary = json.loads(summary_path.read_text(encoding="utf-8"))
188
+ except json.JSONDecodeError:
189
+ return None
190
+ if str(summary.get("head_arch")) != str(head_arch):
191
+ return None
192
+ raw_pr_auc = summary.get("raw_pr_auc")
193
+ if not isinstance(raw_pr_auc, dict):
194
+ return None
195
+ try:
196
+ parsed_pr_auc = {
197
+ split: {scope: float(raw_pr_auc[split][scope]) for scope in scopes}
198
+ for split in ("val", "test")
199
+ }
200
+ except Exception:
201
+ return None
202
+ return rows, parsed_pr_auc, summary
203
+
204
+
205
+ def finite_json(value: Any) -> Any:
206
+ if isinstance(value, float):
207
+ return value if math.isfinite(value) else None
208
+ if isinstance(value, dict):
209
+ return {key: finite_json(val) for key, val in value.items()}
210
+ if isinstance(value, list):
211
+ return [finite_json(val) for val in value]
212
+ return value
213
+
214
+
215
+ def main() -> None:
216
+ args = parse_args()
217
+ args.output_dir.mkdir(parents=True, exist_ok=True)
218
+ base.set_seed(int(args.seed))
219
+ device = base.choose_device(args.device)
220
+
221
+ top_fracs = sorted({float(v) for v in args.fire_prone_top_fracs})
222
+ scope_order = ("global",) + tuple(scope_name(frac) for frac in top_fracs)
223
+ base.SCOPE_ORDER = scope_order
224
+
225
+ split_rows = {
226
+ split: base.read_rows(args.feature_root / "splits" / f"{split}.csv")
227
+ for split in ("train", "val", "test")
228
+ }
229
+ if args.source_kind == "reference":
230
+ store = base.build_reference_store(split_rows)
231
+ elif args.source_kind == "attached":
232
+ store = base.build_attached_store(args, split_rows)
233
+ elif args.source_kind == "spatial":
234
+ store = base.build_spatial_store(args, split_rows)
235
+ else:
236
+ store = base.build_alphaearth_store(args, split_rows)
237
+
238
+ loaders = base.make_loaders(split_rows, store, int(args.batch_size), device, int(args.seed))
239
+ first = next(iter(loaders["train"]))
240
+ in_ch = int(first["x"].shape[1])
241
+ prior_prob = base.total_positive_rate(split_rows["train"])
242
+ scope_masks, scope_meta = build_scope_masks(split_rows, store, top_fracs)
243
+
244
+ head_metrics: list[dict[str, object]] = []
245
+ head_artifacts: dict[str, str] = {}
246
+ for head_index, head_arch in enumerate(args.heads):
247
+ head_dir = args.output_dir / head_arch
248
+ head_dir.mkdir(parents=True, exist_ok=True)
249
+ cached = load_head_summary(head_dir, head_arch, scope_order)
250
+ if cached is not None:
251
+ posthoc_rows, raw_pr_auc, _ = cached
252
+ print(f"[scope-sweep] reuse {args.fm_family} seed={args.seed} head={head_arch}", flush=True)
253
+ else:
254
+ print(f"[scope-sweep] training {args.fm_family} seed={args.seed} head={head_arch}", flush=True)
255
+ model, history = base.train_one_head(
256
+ head_arch=head_arch,
257
+ in_ch=in_ch,
258
+ prior_prob=prior_prob,
259
+ loaders=loaders,
260
+ args=args,
261
+ device=device,
262
+ seed_offset=1009 * (head_index + 1),
263
+ )
264
+ posthoc_rows = []
265
+ raw_pr_auc: dict[str, dict[str, float]] = {}
266
+ for split in ("val", "test"):
267
+ probs, targets = base.collect_predictions(model, loaders[split], device)
268
+ sample_times = base.build_sample_times(split_rows[split])
269
+ raw_pr_auc[split] = {
270
+ scope: head_control._masked_average_precision(probs, targets, region_mask=mask)
271
+ for scope, mask in scope_masks.items()
272
+ }
273
+ posthoc_rows.extend(
274
+ build_posthoc_rows_for_scopes(
275
+ probs=probs,
276
+ targets=targets,
277
+ sample_times=sample_times,
278
+ split=split,
279
+ scope_masks=scope_masks,
280
+ args=args,
281
+ )
282
+ )
283
+ base.write_csv(posthoc_rows, head_dir / "posthoc_rows.csv")
284
+ head_summary = {
285
+ "head_arch": head_arch,
286
+ "head_label": head_control.HEAD_LABELS[head_arch],
287
+ "history": history,
288
+ "raw_pr_auc": raw_pr_auc,
289
+ "scope_meta": scope_meta,
290
+ "posthoc_rows_csv": str(head_dir / "posthoc_rows.csv"),
291
+ }
292
+ (head_dir / "summary.json").write_text(json.dumps(finite_json(head_summary), indent=2), encoding="utf-8")
293
+ head_artifacts[head_arch] = str(head_dir / "summary.json")
294
+ base.append_head_metrics(head_metrics, posthoc_rows, raw_pr_auc, head_arch, args)
295
+
296
+ selection_rows = base.summarize_head_scores(head_metrics)
297
+ for row in selection_rows:
298
+ row["model_tag"] = args.model_tag
299
+ row["family"] = args.fm_family
300
+ row["seed"] = int(args.seed)
301
+
302
+ base.write_csv(head_metrics, args.output_dir / "head_metrics.csv")
303
+ base.write_csv(selection_rows, args.output_dir / "selection_rows.csv")
304
+ summary = {
305
+ "experiment": "fixed-feature head-selection regret scope sweep",
306
+ "task": "wildfire_occupancy",
307
+ "model_tag": args.model_tag,
308
+ "fm_family": args.fm_family,
309
+ "source_kind": args.source_kind,
310
+ "seed": int(args.seed),
311
+ "feature_root": str(args.feature_root),
312
+ "daily_rows_csv": str(args.daily_rows_csv) if args.daily_rows_csv else None,
313
+ "support_dir": str(args.support_dir) if args.support_dir else None,
314
+ "alphaearth_cache_root": str(args.alphaearth_cache_root) if args.alphaearth_cache_root else None,
315
+ "device": str(device),
316
+ "heads": list(args.heads),
317
+ "scope_order": list(scope_order),
318
+ "scope_meta": scope_meta,
319
+ "input_channels": int(in_ch),
320
+ "prior_prob": float(prior_prob),
321
+ "metrics": base.METRICS,
322
+ "head_metrics": head_metrics,
323
+ "selection_rows": selection_rows,
324
+ "head_artifacts": head_artifacts,
325
+ "artifacts": {
326
+ "head_metrics_csv": str(args.output_dir / "head_metrics.csv"),
327
+ "selection_rows_csv": str(args.output_dir / "selection_rows.csv"),
328
+ },
329
+ }
330
+ (args.output_dir / "summary.json").write_text(json.dumps(finite_json(summary), indent=2), encoding="utf-8")
331
+ print(json.dumps(finite_json(summary), indent=2), flush=True)
332
+
333
+
334
+ if __name__ == "__main__":
335
+ main()
experiments/raw_reference/task_scripts/run_all_backbone_selection_regret_20260504.py ADDED
@@ -0,0 +1,656 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Run one fixed-feature head-selection regret job for one backbone and seed."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import csv
8
+ import json
9
+ import math
10
+ import random
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ import numpy as np
16
+ import torch
17
+ import torch.nn as nn
18
+ from torch.utils.data import DataLoader, Dataset
19
+
20
+
21
+ import os
22
+
23
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
24
+ if _p and _p not in sys.path:
25
+ sys.path.insert(0, _p)
26
+
27
+ import run_alphaearth_occupancy_benchmark as alpha_runner # noqa: E402
28
+ import run_attached_daily_occupancy_head_control as head_control # noqa: E402
29
+
30
+
31
+ HEADS = ("constant", "linear", "pixel_mlp", "shallow", "shallow_wide")
32
+ METRICS = {
33
+ "exact": "strict_f1",
34
+ "tolerated": "ts_f1",
35
+ "union": "comprehensive_union_f1",
36
+ }
37
+ SCOPE_ORDER = ("global", "fire_prone")
38
+
39
+
40
+ def parse_args() -> argparse.Namespace:
41
+ parser = argparse.ArgumentParser(description="All-backbone fixed-feature head-selection regret.")
42
+ parser.add_argument("--source-kind", choices=("reference", "attached", "spatial", "alphaearth"), required=True)
43
+ parser.add_argument("--feature-root", type=Path, required=True)
44
+ parser.add_argument("--daily-rows-csv", type=Path)
45
+ parser.add_argument("--support-dir", type=Path)
46
+ parser.add_argument("--alphaearth-cache-root", type=Path)
47
+ parser.add_argument("--output-dir", type=Path, required=True)
48
+ parser.add_argument("--fm-family", type=str, required=True)
49
+ parser.add_argument("--model-tag", type=str, required=True)
50
+ parser.add_argument("--seed", type=int, required=True)
51
+ parser.add_argument("--heads", nargs="+", choices=HEADS, default=list(HEADS))
52
+ parser.add_argument("--batch-size", type=int, default=4)
53
+ parser.add_argument("--epochs", type=int, default=4)
54
+ parser.add_argument("--learning-rate", type=float, default=8e-4)
55
+ parser.add_argument("--weight-decay", type=float, default=1e-5)
56
+ parser.add_argument("--pos-weight-cap", type=float, default=150.0)
57
+ parser.add_argument("--device", choices=("cpu", "cuda", "auto"), default="auto")
58
+ parser.add_argument(
59
+ "--metric-thresholds",
60
+ nargs="+",
61
+ type=float,
62
+ default=[
63
+ 1e-5,
64
+ 2e-5,
65
+ 5e-5,
66
+ 1e-4,
67
+ 2e-4,
68
+ 5e-4,
69
+ 1e-3,
70
+ 2e-3,
71
+ 5e-3,
72
+ 1e-2,
73
+ 2e-2,
74
+ 5e-2,
75
+ 8e-2,
76
+ 1e-1,
77
+ 1.5e-1,
78
+ 2e-1,
79
+ 3e-1,
80
+ 5e-1,
81
+ ],
82
+ )
83
+ parser.add_argument(
84
+ "--variants",
85
+ nargs="+",
86
+ default=["identity", "erode_r1", "close_r1"],
87
+ )
88
+ parser.add_argument("--fire-prone-top-frac", type=float, default=0.20)
89
+ parser.add_argument("--temporal-steps", type=int, default=3)
90
+ parser.add_argument("--spatial-radius", type=int, default=8)
91
+ parser.add_argument("--buffer-radius", type=int, default=8)
92
+ parser.add_argument("--boundary-radius", type=int, default=8)
93
+ parser.add_argument("--coarse-factor", type=int, default=8)
94
+ parser.add_argument("--time-step-hours", type=int, default=6)
95
+ return parser.parse_args()
96
+
97
+
98
+ def read_rows(path: Path) -> list[dict[str, str]]:
99
+ with path.open("r", encoding="utf-8", newline="") as fh:
100
+ return list(csv.DictReader(fh))
101
+
102
+
103
+ def choose_device(value: str) -> torch.device:
104
+ if value == "auto":
105
+ return torch.device("cuda" if torch.cuda.is_available() else "cpu")
106
+ device = torch.device(value)
107
+ if device.type == "cuda" and not torch.cuda.is_available():
108
+ raise RuntimeError("CUDA requested but not available.")
109
+ return device
110
+
111
+
112
+ def set_seed(seed: int) -> None:
113
+ random.seed(seed)
114
+ np.random.seed(seed)
115
+ torch.manual_seed(seed)
116
+ torch.cuda.manual_seed_all(seed)
117
+
118
+
119
+ def total_positive_rate(rows: list[dict[str, str]]) -> float:
120
+ pos = float(sum(int(row["pos_cells"]) for row in rows))
121
+ arr = np.load(rows[0]["feature_path"], allow_pickle=True)
122
+ try:
123
+ total = float(len(rows) * np.squeeze(arr["y_occ"]).size)
124
+ finally:
125
+ arr.close()
126
+ return float(pos / total) if total > 0 else 0.0
127
+
128
+
129
+ class Normalizer:
130
+ def __init__(self, mean: np.ndarray, std: np.ndarray) -> None:
131
+ self.mean = mean.astype(np.float32)
132
+ self.std = np.maximum(std.astype(np.float32), 1e-6)
133
+
134
+ def apply(self, x: np.ndarray) -> np.ndarray:
135
+ return (x.astype(np.float32) - self.mean[:, None, None]) / self.std[:, None, None]
136
+
137
+
138
+ def compute_map_normalizer(rows: list[dict[str, str]], map_path_fn: Any) -> Normalizer:
139
+ sum_c: np.ndarray | None = None
140
+ sumsq_c: np.ndarray | None = None
141
+ count = 0
142
+ for row in rows:
143
+ arr = np.load(map_path_fn(row), allow_pickle=True)
144
+ try:
145
+ x = np.nan_to_num(arr["features"].astype(np.float32), nan=0.0, posinf=0.0, neginf=0.0)
146
+ finally:
147
+ arr.close()
148
+ flat = x.reshape(x.shape[0], -1)
149
+ if sum_c is None:
150
+ sum_c = np.zeros(x.shape[0], dtype=np.float64)
151
+ sumsq_c = np.zeros(x.shape[0], dtype=np.float64)
152
+ sum_c += flat.sum(axis=1)
153
+ sumsq_c += np.square(flat, dtype=np.float64).sum(axis=1)
154
+ count += flat.shape[1]
155
+ if sum_c is None or sumsq_c is None or count == 0:
156
+ raise RuntimeError("Cannot compute feature normalizer.")
157
+ mean = sum_c / float(count)
158
+ var = np.maximum(sumsq_c / float(count) - mean * mean, 1e-12)
159
+ return Normalizer(mean=mean.astype(np.float32), std=np.sqrt(var).astype(np.float32))
160
+
161
+
162
+ def load_support_manifest(path: Path) -> dict[str, dict[str, str]]:
163
+ rows = read_rows(path)
164
+ support: dict[str, dict[str, str]] = {}
165
+ for row in rows:
166
+ support_path = Path(row["support_path"])
167
+ if row.get("status") in {"generated", "existing"} and support_path.exists():
168
+ support[str(row["sample_id"])] = row
169
+ return support
170
+
171
+
172
+ class ReferenceFeatureStore:
173
+ def __init__(self, rows: list[dict[str, str]], normalizer: Normalizer) -> None:
174
+ self.rows_by_id = {str(row["sample_id"]): row for row in rows}
175
+ self.normalizer = normalizer
176
+
177
+ def get(self, sample_id: str) -> dict[str, np.ndarray | str]:
178
+ row = self.rows_by_id[str(sample_id)]
179
+ arr = np.load(row["feature_path"], allow_pickle=True)
180
+ try:
181
+ x = np.nan_to_num(arr["features"].astype(np.float32), nan=0.0, posinf=0.0, neginf=0.0)
182
+ y = np.nan_to_num(arr["y_occ"].astype(np.float32), nan=0.0, posinf=0.0, neginf=0.0)
183
+ finally:
184
+ arr.close()
185
+ return {"x": self.normalizer.apply(x), "y_occ": y, "target_timestamp": row["target_timestamp"]}
186
+
187
+
188
+ class SpatialSupportStore:
189
+ def __init__(
190
+ self,
191
+ rows: list[dict[str, str]],
192
+ support: dict[str, dict[str, str]],
193
+ normalizer: Normalizer,
194
+ ) -> None:
195
+ self.rows_by_id = {str(row["sample_id"]): row for row in rows}
196
+ self.support = support
197
+ self.normalizer = normalizer
198
+
199
+ def get(self, sample_id: str) -> dict[str, np.ndarray | str]:
200
+ sid = str(sample_id)
201
+ row = self.rows_by_id[sid]
202
+ sarr = np.load(self.support[sid]["support_path"], allow_pickle=True)
203
+ farr = np.load(row["feature_path"], allow_pickle=True)
204
+ try:
205
+ x = np.nan_to_num(sarr["features"].astype(np.float32), nan=0.0, posinf=0.0, neginf=0.0)
206
+ y = np.nan_to_num(farr["y_occ"].astype(np.float32), nan=0.0, posinf=0.0, neginf=0.0)
207
+ finally:
208
+ sarr.close()
209
+ farr.close()
210
+ return {"x": self.normalizer.apply(x), "y_occ": y, "target_timestamp": row["target_timestamp"]}
211
+
212
+
213
+ class FullMapDataset(Dataset):
214
+ def __init__(self, rows: list[dict[str, str]], store: Any) -> None:
215
+ self.rows = rows
216
+ self.store = store
217
+
218
+ def __len__(self) -> int:
219
+ return len(self.rows)
220
+
221
+ def __getitem__(self, idx: int) -> dict[str, Any]:
222
+ row = self.rows[idx]
223
+ sample = self.store.get(str(row["sample_id"]))
224
+ return {
225
+ "x": torch.from_numpy(np.asarray(sample["x"], dtype=np.float32)),
226
+ "y": torch.from_numpy(np.asarray(sample["y_occ"], dtype=np.float32)),
227
+ "sample_id": str(row["sample_id"]),
228
+ "target_timestamp": str(sample["target_timestamp"]),
229
+ }
230
+
231
+
232
+ def make_loaders(
233
+ split_rows: dict[str, list[dict[str, str]]],
234
+ store: Any,
235
+ batch_size: int,
236
+ device: torch.device,
237
+ seed: int,
238
+ ) -> dict[str, DataLoader]:
239
+ loaders: dict[str, DataLoader] = {}
240
+ for split, rows in split_rows.items():
241
+ kwargs: dict[str, Any] = {}
242
+ if split == "train":
243
+ kwargs["generator"] = torch.Generator().manual_seed(int(seed))
244
+ loaders[split] = DataLoader(
245
+ FullMapDataset(rows, store),
246
+ batch_size=int(batch_size),
247
+ shuffle=(split == "train"),
248
+ num_workers=0,
249
+ pin_memory=device.type == "cuda",
250
+ **kwargs,
251
+ )
252
+ return loaders
253
+
254
+
255
+ def build_attached_store(args: argparse.Namespace, split_rows: dict[str, list[dict[str, str]]]) -> Any:
256
+ if args.daily_rows_csv is None:
257
+ raise ValueError("--daily-rows-csv is required for attached source.")
258
+ daily_lookup, ordered_times, ordered_features = head_control.build_daily_lookup(args.daily_rows_csv)
259
+ return head_control.FeatureStore(
260
+ split_rows["train"] + split_rows["val"] + split_rows["test"],
261
+ daily_lookup,
262
+ ordered_times,
263
+ ordered_features,
264
+ )
265
+
266
+
267
+ def build_alphaearth_store(args: argparse.Namespace, split_rows: dict[str, list[dict[str, str]]]) -> Any:
268
+ if args.alphaearth_cache_root is None:
269
+ raise ValueError("--alphaearth-cache-root is required for AlphaEarth source.")
270
+ grid_cache = alpha_runner.GridCache(args.alphaearth_cache_root)
271
+ return alpha_runner.FeatureStore(split_rows["train"] + split_rows["val"] + split_rows["test"], grid_cache)
272
+
273
+
274
+ def build_spatial_store(args: argparse.Namespace, split_rows: dict[str, list[dict[str, str]]]) -> SpatialSupportStore:
275
+ if args.support_dir is None:
276
+ raise ValueError("--support-dir is required for spatial source.")
277
+ support = load_support_manifest(args.support_dir / "support_manifest.csv")
278
+ missing = [
279
+ row["sample_id"]
280
+ for rows in split_rows.values()
281
+ for row in rows
282
+ if str(row["sample_id"]) not in support
283
+ ]
284
+ if missing:
285
+ raise RuntimeError(f"Missing spatial support maps for {len(missing)} samples; first={missing[:5]}")
286
+ normalizer = compute_map_normalizer(split_rows["train"], lambda row: support[str(row["sample_id"])]["support_path"])
287
+ return SpatialSupportStore(split_rows["train"] + split_rows["val"] + split_rows["test"], support, normalizer)
288
+
289
+
290
+ def build_reference_store(split_rows: dict[str, list[dict[str, str]]]) -> ReferenceFeatureStore:
291
+ normalizer = compute_map_normalizer(split_rows["train"], lambda row: row["feature_path"])
292
+ return ReferenceFeatureStore(split_rows["train"] + split_rows["val"] + split_rows["test"], normalizer)
293
+
294
+
295
+ def build_head(head_arch: str, in_ch: int, prior_prob: float) -> nn.Module:
296
+ if head_arch == "constant":
297
+ return head_control.ConstantHead(prior_prob=prior_prob)
298
+ if head_arch == "linear":
299
+ return head_control.LinearHead(in_ch=in_ch, prior_prob=prior_prob)
300
+ if head_arch == "pixel_mlp":
301
+ return head_control.PixelMLPHead(in_ch=in_ch, hidden=16, dropout=0.05, prior_prob=prior_prob)
302
+ if head_arch == "shallow_wide":
303
+ return head_control.WildfireHead(
304
+ in_ch=in_ch,
305
+ hidden=64,
306
+ dropout=0.10,
307
+ norm_type="group",
308
+ norm_groups=8,
309
+ prior_prob=prior_prob,
310
+ )
311
+ return head_control.WildfireHead(
312
+ in_ch=in_ch,
313
+ hidden=32,
314
+ dropout=0.05,
315
+ norm_type="group",
316
+ norm_groups=8,
317
+ prior_prob=prior_prob,
318
+ )
319
+
320
+
321
+ @torch.no_grad()
322
+ def collect_predictions(model: nn.Module, loader: DataLoader, device: torch.device) -> tuple[np.ndarray, np.ndarray]:
323
+ model.eval()
324
+ probs: list[np.ndarray] = []
325
+ targets: list[np.ndarray] = []
326
+ for batch in loader:
327
+ x = batch["x"].to(device, non_blocking=True)
328
+ y = batch["y"].to(device, non_blocking=True)
329
+ logits = model(x)
330
+ probs.append(np.nan_to_num(torch.sigmoid(logits).detach().cpu().numpy()[:, 0], nan=0.0, posinf=1.0, neginf=0.0))
331
+ targets.append(np.nan_to_num(y.detach().cpu().numpy()[:, 0], nan=0.0, posinf=0.0, neginf=0.0))
332
+ return np.concatenate(probs, axis=0), np.concatenate(targets, axis=0)
333
+
334
+
335
+ def train_one_head(
336
+ head_arch: str,
337
+ in_ch: int,
338
+ prior_prob: float,
339
+ loaders: dict[str, DataLoader],
340
+ args: argparse.Namespace,
341
+ device: torch.device,
342
+ seed_offset: int,
343
+ ) -> tuple[nn.Module, list[dict[str, float]]]:
344
+ set_seed(int(args.seed) + int(seed_offset))
345
+ model = build_head(head_arch, in_ch=in_ch, prior_prob=prior_prob).to(device)
346
+ optimizer = torch.optim.AdamW(model.parameters(), lr=float(args.learning_rate), weight_decay=float(args.weight_decay))
347
+ raw_weight = (1.0 - float(prior_prob)) / max(float(prior_prob), 1e-9)
348
+ pos_weight = float(min(float(args.pos_weight_cap), raw_weight))
349
+ criterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([pos_weight], dtype=torch.float32, device=device))
350
+ history: list[dict[str, float]] = []
351
+ for epoch in range(1, int(args.epochs) + 1):
352
+ model.train()
353
+ losses: list[float] = []
354
+ for batch in loaders["train"]:
355
+ x = batch["x"].to(device, non_blocking=True)
356
+ y = batch["y"].to(device, non_blocking=True)
357
+ optimizer.zero_grad(set_to_none=True)
358
+ logits = model(x)
359
+ loss = criterion(logits, y)
360
+ if not torch.isfinite(loss):
361
+ raise RuntimeError(f"Non-finite loss for head={head_arch}")
362
+ loss.backward()
363
+ optimizer.step()
364
+ losses.append(float(loss.item()))
365
+ history.append({"epoch": epoch, "train_bce": float(np.mean(losses)), "pos_weight": pos_weight})
366
+ return model, history
367
+
368
+
369
+ def build_sample_times(rows: list[dict[str, str]]) -> np.ndarray:
370
+ return np.array([row["target_timestamp"] for row in rows], dtype="datetime64[h]")
371
+
372
+
373
+ def select_val_posthoc(
374
+ rows: list[dict[str, object]],
375
+ scope: str,
376
+ metric: str,
377
+ ) -> dict[str, object]:
378
+ prefix = metric.rsplit("_", 1)[0]
379
+ precision_key = f"{prefix}_precision"
380
+ recall_key = f"{prefix}_recall"
381
+ selected = [row for row in rows if row["split"] == "val" and row["scope"] == scope]
382
+ if not selected:
383
+ raise RuntimeError(f"No validation rows for scope={scope}")
384
+ return max(
385
+ selected,
386
+ key=lambda row: (
387
+ float(row.get(metric, 0.0)),
388
+ float(row.get(precision_key, 0.0)),
389
+ float(row.get(recall_key, 0.0)),
390
+ -abs(float(row["threshold"]) - 0.5),
391
+ ),
392
+ )
393
+
394
+
395
+ def matching_test_row(rows: list[dict[str, object]], scope: str, selected: dict[str, object]) -> dict[str, object]:
396
+ threshold = float(selected["threshold"])
397
+ variant = str(selected["variant"])
398
+ for row in rows:
399
+ if (
400
+ row["split"] == "test"
401
+ and row["scope"] == scope
402
+ and str(row["variant"]) == variant
403
+ and abs(float(row["threshold"]) - threshold) < 1e-12
404
+ ):
405
+ return row
406
+ raise RuntimeError(f"No matching test row for scope={scope}, threshold={threshold}, variant={variant}")
407
+
408
+
409
+ def summarize_head_scores(
410
+ head_metrics: list[dict[str, object]],
411
+ ) -> list[dict[str, object]]:
412
+ selection_rows: list[dict[str, object]] = []
413
+ for scope in SCOPE_ORDER:
414
+ candidates = [row for row in head_metrics if row["scope"] == scope]
415
+ if not candidates:
416
+ continue
417
+ ranking_selected = max(
418
+ candidates,
419
+ key=lambda row: (
420
+ float(row["val_pr_auc"]),
421
+ float(row["val_union_f1"]),
422
+ float(row["val_tolerated_f1"]),
423
+ float(row["val_exact_f1"]),
424
+ ),
425
+ )
426
+ out: dict[str, object] = {
427
+ "scope": scope,
428
+ "ranking_selected_head": ranking_selected["head_label"],
429
+ "ranking_selected_head_arch": ranking_selected["head_arch"],
430
+ "ranking_selected_val_pr_auc": float(ranking_selected["val_pr_auc"]),
431
+ "ranking_selected_test_pr_auc": float(ranking_selected["test_pr_auc"]),
432
+ }
433
+ for short, column in (("exact", "exact_f1"), ("tolerated", "tolerated_f1"), ("union", "union_f1")):
434
+ val_column = f"val_{column}"
435
+ test_column = f"test_{column}"
436
+ decision_selected = max(
437
+ candidates,
438
+ key=lambda row: (
439
+ float(row[val_column]),
440
+ float(row["val_pr_auc"]),
441
+ str(row["head_arch"]),
442
+ ),
443
+ )
444
+ val_gap = float(decision_selected[val_column]) - float(ranking_selected[val_column])
445
+ test_gap = float(decision_selected[test_column]) - float(ranking_selected[test_column])
446
+ out[f"{short}_val_ranking_score"] = float(ranking_selected[val_column])
447
+ out[f"{short}_val_decision_score"] = float(decision_selected[val_column])
448
+ out[f"{short}_val_gap"] = float(max(0.0, val_gap))
449
+ out[f"{short}_ranking_score"] = float(ranking_selected[test_column])
450
+ out[f"{short}_decision_score"] = float(decision_selected[test_column])
451
+ out[f"{short}_test_gap"] = float(test_gap)
452
+ out[f"{short}_regret"] = float(max(0.0, test_gap))
453
+ out[f"{short}_decision_head"] = decision_selected["head_label"]
454
+ out[f"{short}_decision_head_arch"] = decision_selected["head_arch"]
455
+ selection_rows.append(out)
456
+ return selection_rows
457
+
458
+
459
+ def write_csv(rows: list[dict[str, object]], path: Path) -> None:
460
+ path.parent.mkdir(parents=True, exist_ok=True)
461
+ fieldnames = sorted({key for row in rows for key in row})
462
+ with path.open("w", newline="", encoding="utf-8") as fh:
463
+ writer = csv.DictWriter(fh, fieldnames=fieldnames)
464
+ writer.writeheader()
465
+ writer.writerows(rows)
466
+
467
+
468
+ def load_head_summary(head_dir: Path, head_arch: str) -> tuple[list[dict[str, object]], dict[str, dict[str, float]], dict[str, object]] | None:
469
+ posthoc_path = head_dir / "posthoc_rows.csv"
470
+ summary_path = head_dir / "summary.json"
471
+ if not posthoc_path.exists() or not summary_path.exists():
472
+ return None
473
+ rows = [dict(row) for row in read_rows(posthoc_path)]
474
+ if not rows:
475
+ return None
476
+ try:
477
+ summary = json.loads(summary_path.read_text(encoding="utf-8"))
478
+ except json.JSONDecodeError:
479
+ return None
480
+ if str(summary.get("head_arch")) != str(head_arch):
481
+ return None
482
+ raw_pr_auc = summary.get("raw_pr_auc")
483
+ if not isinstance(raw_pr_auc, dict):
484
+ return None
485
+ try:
486
+ parsed_pr_auc = {
487
+ split: {
488
+ scope: float(raw_pr_auc[split][scope])
489
+ for scope in SCOPE_ORDER
490
+ }
491
+ for split in ("val", "test")
492
+ }
493
+ except Exception:
494
+ return None
495
+ return rows, parsed_pr_auc, summary
496
+
497
+
498
+ def append_head_metrics(
499
+ head_metrics: list[dict[str, object]],
500
+ posthoc_rows: list[dict[str, object]],
501
+ raw_pr_auc: dict[str, dict[str, float]],
502
+ head_arch: str,
503
+ args: argparse.Namespace,
504
+ ) -> None:
505
+ for scope in SCOPE_ORDER:
506
+ metric_scores: dict[str, float] = {}
507
+ selected_thresholds: dict[str, float] = {}
508
+ selected_variants: dict[str, str] = {}
509
+ for short, metric in METRICS.items():
510
+ selected = select_val_posthoc(posthoc_rows, scope, metric)
511
+ test_row = matching_test_row(posthoc_rows, scope, selected)
512
+ metric_scores[f"val_{short}_f1"] = float(selected[metric])
513
+ metric_scores[f"test_{short}_f1"] = float(test_row[metric])
514
+ selected_thresholds[short] = float(selected["threshold"])
515
+ selected_variants[short] = str(selected["variant"])
516
+ head_metrics.append(
517
+ {
518
+ "model_tag": args.model_tag,
519
+ "family": args.fm_family,
520
+ "seed": int(args.seed),
521
+ "scope": scope,
522
+ "head_arch": head_arch,
523
+ "head_label": head_control.HEAD_LABELS[head_arch],
524
+ "val_pr_auc": float(raw_pr_auc["val"][scope]),
525
+ "test_pr_auc": float(raw_pr_auc["test"][scope]),
526
+ **metric_scores,
527
+ "selected_thresholds": selected_thresholds,
528
+ "selected_variants": selected_variants,
529
+ }
530
+ )
531
+
532
+
533
+ def main() -> None:
534
+ args = parse_args()
535
+ args.output_dir.mkdir(parents=True, exist_ok=True)
536
+ set_seed(int(args.seed))
537
+ device = choose_device(args.device)
538
+
539
+ split_rows = {
540
+ split: read_rows(args.feature_root / "splits" / f"{split}.csv")
541
+ for split in ("train", "val", "test")
542
+ }
543
+ if args.source_kind == "reference":
544
+ store = build_reference_store(split_rows)
545
+ elif args.source_kind == "attached":
546
+ store = build_attached_store(args, split_rows)
547
+ elif args.source_kind == "spatial":
548
+ store = build_spatial_store(args, split_rows)
549
+ else:
550
+ store = build_alphaearth_store(args, split_rows)
551
+
552
+ loaders = make_loaders(split_rows, store, int(args.batch_size), device, int(args.seed))
553
+ first = next(iter(loaders["train"]))
554
+ in_ch = int(first["x"].shape[1])
555
+ prior_prob = total_positive_rate(split_rows["train"])
556
+ fire_prone_mask, fire_prone_meta = head_control.build_fire_prone_mask(
557
+ split_rows["train"],
558
+ store,
559
+ float(args.fire_prone_top_frac),
560
+ )
561
+
562
+ head_metrics: list[dict[str, object]] = []
563
+ head_artifacts: dict[str, str] = {}
564
+ for head_index, head_arch in enumerate(args.heads):
565
+ head_dir = args.output_dir / head_arch
566
+ head_dir.mkdir(parents=True, exist_ok=True)
567
+ cached = load_head_summary(head_dir, head_arch)
568
+ if cached is not None:
569
+ posthoc_rows, raw_pr_auc, _ = cached
570
+ print(f"[selection-regret] reuse {args.fm_family} seed={args.seed} head={head_arch}", flush=True)
571
+ else:
572
+ print(f"[selection-regret] training {args.fm_family} seed={args.seed} head={head_arch}", flush=True)
573
+ model, history = train_one_head(
574
+ head_arch=head_arch,
575
+ in_ch=in_ch,
576
+ prior_prob=prior_prob,
577
+ loaders=loaders,
578
+ args=args,
579
+ device=device,
580
+ seed_offset=1009 * (head_index + 1),
581
+ )
582
+ posthoc_rows = []
583
+ raw_pr_auc = {}
584
+ for split in ("val", "test"):
585
+ probs, targets = collect_predictions(model, loaders[split], device)
586
+ sample_times = build_sample_times(split_rows[split])
587
+ raw_pr_auc[split] = {
588
+ "global": head_control._masked_average_precision(probs, targets, region_mask=None),
589
+ "fire_prone": head_control._masked_average_precision(probs, targets, region_mask=fire_prone_mask),
590
+ }
591
+ posthoc_rows.extend(
592
+ head_control.build_posthoc_rows(
593
+ probs=probs,
594
+ targets=targets,
595
+ sample_times=sample_times,
596
+ split=split,
597
+ fire_prone_mask=fire_prone_mask,
598
+ args=args,
599
+ )
600
+ )
601
+
602
+ write_csv(posthoc_rows, head_dir / "posthoc_rows.csv")
603
+ head_summary = {
604
+ "head_arch": head_arch,
605
+ "head_label": head_control.HEAD_LABELS[head_arch],
606
+ "history": history,
607
+ "raw_pr_auc": raw_pr_auc,
608
+ "posthoc_rows_csv": str(head_dir / "posthoc_rows.csv"),
609
+ }
610
+ (head_dir / "summary.json").write_text(json.dumps(head_summary, indent=2), encoding="utf-8")
611
+ head_artifacts[head_arch] = str(head_dir / "summary.json")
612
+ append_head_metrics(head_metrics, posthoc_rows, raw_pr_auc, head_arch, args)
613
+
614
+ selection_rows = summarize_head_scores(head_metrics)
615
+ for row in selection_rows:
616
+ row["model_tag"] = args.model_tag
617
+ row["family"] = args.fm_family
618
+ row["seed"] = int(args.seed)
619
+
620
+ write_csv(head_metrics, args.output_dir / "head_metrics.csv")
621
+ write_csv(selection_rows, args.output_dir / "selection_rows.csv")
622
+ summary = {
623
+ "experiment": "all-backbone fixed-feature head-selection regret",
624
+ "task": "wildfire_occupancy",
625
+ "model_tag": args.model_tag,
626
+ "fm_family": args.fm_family,
627
+ "source_kind": args.source_kind,
628
+ "seed": int(args.seed),
629
+ "feature_root": str(args.feature_root),
630
+ "daily_rows_csv": str(args.daily_rows_csv) if args.daily_rows_csv else None,
631
+ "support_dir": str(args.support_dir) if args.support_dir else None,
632
+ "alphaearth_cache_root": str(args.alphaearth_cache_root) if args.alphaearth_cache_root else None,
633
+ "device": str(device),
634
+ "heads": list(args.heads),
635
+ "input_channels": int(in_ch),
636
+ "prior_prob": float(prior_prob),
637
+ "fire_prone_scope": {
638
+ "scope_name": "fire_prone",
639
+ "reported_as": "top 20%",
640
+ **fire_prone_meta,
641
+ },
642
+ "metrics": METRICS,
643
+ "head_metrics": head_metrics,
644
+ "selection_rows": selection_rows,
645
+ "head_artifacts": head_artifacts,
646
+ "artifacts": {
647
+ "head_metrics_csv": str(args.output_dir / "head_metrics.csv"),
648
+ "selection_rows_csv": str(args.output_dir / "selection_rows.csv"),
649
+ },
650
+ }
651
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
652
+ print(json.dumps(summary, indent=2), flush=True)
653
+
654
+
655
+ if __name__ == "__main__":
656
+ main()
experiments/raw_reference/task_scripts/run_analog_extended_retrieval_sweep_seeded.py ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Dict, List, Tuple
9
+
10
+
11
+ import os
12
+
13
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
14
+ if _p and _p not in sys.path:
15
+ sys.path.insert(0, _p)
16
+
17
+ import numpy as np
18
+ import pandas as pd
19
+ from sklearn.compose import ColumnTransformer
20
+ from sklearn.impute import SimpleImputer
21
+ from sklearn.linear_model import ElasticNet, Ridge
22
+ from sklearn.metrics.pairwise import cosine_similarity
23
+ from sklearn.pipeline import Pipeline
24
+ from sklearn.preprocessing import OneHotEncoder, StandardScaler
25
+
26
+
27
+ DROP_COLUMNS = {
28
+ "Event_ID",
29
+ "Incid_Name",
30
+ "incident_name_norm",
31
+ "wfigs_name",
32
+ "Ig_Date",
33
+ "weather_date",
34
+ "BurnBndAc",
35
+ "target_log_burn_acres",
36
+ }
37
+ CATEGORICAL_COLUMNS = ["Incid_Type", "state_abbr", "county_name", "wfigs_match_type"]
38
+
39
+
40
+ def build_splits(df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
41
+ ordered = df.sort_values("Ig_Date").reset_index(drop=True)
42
+ n = len(ordered)
43
+ train_end = max(int(round(n * 0.6)), 1)
44
+ val_end = max(int(round(n * 0.8)), train_end + 1)
45
+ val_end = min(val_end, n - 1) if n >= 3 else n
46
+ train = ordered.iloc[:train_end].copy()
47
+ val = ordered.iloc[train_end:val_end].copy()
48
+ test = ordered.iloc[val_end:].copy()
49
+ if len(val) == 0 and len(test) > 1:
50
+ val = test.iloc[:1].copy()
51
+ test = test.iloc[1:].copy()
52
+ return train, val, test
53
+
54
+
55
+ def block_columns(df: pd.DataFrame, exclude: set[str]) -> Dict[str, List[str]]:
56
+ numeric = [
57
+ c
58
+ for c in df.columns
59
+ if c not in DROP_COLUMNS
60
+ and c not in CATEGORICAL_COLUMNS
61
+ and c not in exclude
62
+ and pd.api.types.is_numeric_dtype(df[c])
63
+ ]
64
+ return {
65
+ "weather": [c for c in numeric if c.startswith("weather_")],
66
+ "geo_fire": [
67
+ c
68
+ for c in numeric
69
+ if c.startswith("firms_")
70
+ or c.startswith("landfire_")
71
+ or c in {"BurnBndLat", "BurnBndLon", "lat", "lon", "wfigs_acres", "wfigs_date_diff_days", "wfigs_dist_km", "is_conus_static"}
72
+ ],
73
+ "categorical": [c for c in CATEGORICAL_COLUMNS if c in df.columns and c not in exclude],
74
+ }
75
+
76
+
77
+ def make_block_matrix(train: pd.DataFrame, val: pd.DataFrame, test: pd.DataFrame, cols: List[str], categorical: bool) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
78
+ if not cols:
79
+ n_train, n_val, n_test = len(train), len(val), len(test)
80
+ return np.zeros((n_train, 0), dtype=np.float32), np.zeros((n_val, 0), dtype=np.float32), np.zeros((n_test, 0), dtype=np.float32)
81
+ if categorical:
82
+ transformer = ColumnTransformer(
83
+ [("cat", Pipeline([("impute", SimpleImputer(strategy="most_frequent")), ("onehot", OneHotEncoder(handle_unknown="ignore"))]), cols)],
84
+ remainder="drop",
85
+ )
86
+ else:
87
+ transformer = ColumnTransformer(
88
+ [("num", Pipeline([("impute", SimpleImputer(strategy="median")), ("scale", StandardScaler())]), cols)],
89
+ remainder="drop",
90
+ )
91
+ train_x = transformer.fit_transform(train[cols])
92
+ val_x = transformer.transform(val[cols])
93
+ test_x = transformer.transform(test[cols])
94
+ if hasattr(train_x, "toarray"):
95
+ train_x = train_x.toarray()
96
+ val_x = val_x.toarray()
97
+ test_x = test_x.toarray()
98
+ return train_x.astype(np.float32), val_x.astype(np.float32), test_x.astype(np.float32)
99
+
100
+
101
+ def graded_relevance(query_target: float, retrieved_targets: np.ndarray) -> np.ndarray:
102
+ delta = np.abs(np.asarray(retrieved_targets, dtype=np.float64) - float(query_target))
103
+ return np.select([delta <= 0.5, delta <= 1.0, delta <= 1.5], [3.0, 2.0, 1.0], default=0.0)
104
+
105
+
106
+ def dcg(relevance: np.ndarray) -> float:
107
+ rel = np.asarray(relevance, dtype=np.float64)
108
+ discounts = 1.0 / np.log2(np.arange(rel.size, dtype=np.float64) + 2.0)
109
+ return float(np.sum(rel * discounts))
110
+
111
+
112
+ def ndcg_at_k(relevance: np.ndarray, ideal_relevance: np.ndarray, k: int) -> float:
113
+ denom = dcg(ideal_relevance[:k])
114
+ return float(dcg(relevance[:k]) / denom) if denom > 0 else 0.0
115
+
116
+
117
+ def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
118
+ return float(np.sqrt(np.mean((np.asarray(y_true) - np.asarray(y_pred)) ** 2)))
119
+
120
+
121
+ def spearman_corr(y_true: np.ndarray, y_pred: np.ndarray) -> float:
122
+ value = pd.Series(y_true).corr(pd.Series(y_pred), method="spearman")
123
+ return float(value) if pd.notna(value) else 0.0
124
+
125
+
126
+ def target_weight_vectors(train_vec: np.ndarray, val_vec: np.ndarray, test_vec: np.ndarray, target: np.ndarray, power: float, floor: float) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
127
+ if train_vec.shape[1] == 0:
128
+ return train_vec, val_vec, test_vec
129
+ x = np.asarray(train_vec, dtype=np.float64)
130
+ y = np.asarray(target, dtype=np.float64)
131
+ y = y - y.mean()
132
+ x_centered = x - x.mean(axis=0, keepdims=True)
133
+ denom = np.clip(np.sqrt(np.sum(x_centered**2, axis=0)) * np.sqrt(np.sum(y**2)), 1e-12, None)
134
+ corr = np.abs(np.sum(x_centered * y[:, None], axis=0) / denom)
135
+ corr = np.nan_to_num(corr, nan=0.0, posinf=0.0, neginf=0.0)
136
+ if float(corr.max()) > 0:
137
+ corr = corr / float(corr.max())
138
+ weights = (floor + np.power(corr, power)).astype(np.float32)
139
+ return train_vec * weights, val_vec * weights, test_vec * weights
140
+
141
+
142
+ def score_vectors(query_vec: np.ndarray, library_vec: np.ndarray, query_df: pd.DataFrame, library_df: pd.DataFrame, k: int, mode: str) -> Dict[str, float]:
143
+ k_eff = min(k, library_vec.shape[0])
144
+ lib_norm = library_vec / np.clip(np.linalg.norm(library_vec, axis=1, keepdims=True), 1e-12, None)
145
+ query_norm = query_vec / np.clip(np.linalg.norm(query_vec, axis=1, keepdims=True), 1e-12, None)
146
+ sim_all = cosine_similarity(query_norm, lib_norm)
147
+ knn_idx = np.argsort(-sim_all, axis=1)[:, :k_eff]
148
+ knn_sim = np.take_along_axis(sim_all, knn_idx, axis=1)
149
+ target_lib = library_df["target_log_burn_acres"].to_numpy(dtype=np.float64)
150
+ preds = []
151
+ ndcg5 = []
152
+ ndcg10 = []
153
+ hit1 = []
154
+ hit5 = []
155
+ hit10 = []
156
+ best_abs = []
157
+ for i in range(query_df.shape[0]):
158
+ idx = knn_idx[i]
159
+ sims = knn_sim[i]
160
+ top_targets = target_lib[idx]
161
+ true = float(query_df.iloc[i]["target_log_burn_acres"])
162
+ relevance = graded_relevance(true, top_targets)
163
+ ideal = np.sort(graded_relevance(true, target_lib))[::-1]
164
+ ndcg5.append(ndcg_at_k(relevance, ideal, 5))
165
+ ndcg10.append(ndcg_at_k(relevance, ideal, 10))
166
+ hit1.append(float(relevance[:1].max() >= 2.0))
167
+ hit5.append(float(relevance[: min(5, k_eff)].max() >= 2.0))
168
+ hit10.append(float(relevance[: min(10, k_eff)].max() >= 2.0))
169
+ best_abs.append(float(np.min(np.abs(top_targets - true))))
170
+ if mode == "weighted":
171
+ weights = np.maximum((sims + 1.0) / 2.0, 1e-6)
172
+ preds.append(float(np.sum(weights * top_targets) / np.sum(weights)))
173
+ else:
174
+ preds.append(float(np.mean(top_targets)))
175
+ pred = np.asarray(preds, dtype=np.float64)
176
+ true_log = query_df["target_log_burn_acres"].to_numpy(dtype=np.float64)
177
+ return {
178
+ "count": int(len(query_df)),
179
+ "log_mae": float(np.mean(np.abs(true_log - pred))),
180
+ "log_rmse": rmse(true_log, pred),
181
+ "log_spearman": spearman_corr(true_log, pred),
182
+ "ndcg_at_5": float(np.mean(ndcg5)),
183
+ "ndcg_at_10": float(np.mean(ndcg10)),
184
+ "hit_at_1_tol1": float(np.mean(hit1)),
185
+ "hit_at_5_tol1": float(np.mean(hit5)),
186
+ "hit_at_10_tol1": float(np.mean(hit10)),
187
+ "mean_best_abs_log_delta_at_k": float(np.mean(best_abs)),
188
+ }
189
+
190
+
191
+ def append_supervised_scalar(
192
+ train_vec: np.ndarray,
193
+ val_vec: np.ndarray,
194
+ test_vec: np.ndarray,
195
+ train_df: pd.DataFrame,
196
+ model_name: str,
197
+ weight: float,
198
+ seed: int,
199
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
200
+ y = train_df["target_log_burn_acres"].to_numpy(dtype=np.float64)
201
+ model = Ridge(alpha=1.0) if model_name == "ridge" else ElasticNet(alpha=0.01, l1_ratio=0.2, random_state=seed, max_iter=10000)
202
+ model.fit(train_vec, y)
203
+ train_pred = model.predict(train_vec)
204
+ val_pred = model.predict(val_vec)
205
+ test_pred = model.predict(test_vec)
206
+ mean = float(np.mean(train_pred))
207
+ std = float(np.std(train_pred)) or 1.0
208
+ def _append(x: np.ndarray, pred: np.ndarray) -> np.ndarray:
209
+ scalar = ((pred - mean) / std).reshape(-1, 1).astype(np.float32) * float(weight)
210
+ return np.concatenate([x, scalar], axis=1)
211
+ return _append(train_vec, train_pred), _append(val_vec, val_pred), _append(test_vec, test_pred)
212
+
213
+
214
+ def main() -> None:
215
+ parser = argparse.ArgumentParser()
216
+ parser.add_argument("--event-table", type=Path, required=True)
217
+ parser.add_argument("--output-dir", type=Path, required=True)
218
+ parser.add_argument("--exclude-columns", nargs="*", default=[])
219
+ parser.add_argument("--seed", type=int, default=7)
220
+ args = parser.parse_args()
221
+
222
+ df = pd.read_csv(args.event_table)
223
+ df["Ig_Date"] = pd.to_datetime(df["Ig_Date"])
224
+ train_df, val_df, test_df = build_splits(df)
225
+ exclude = set(args.exclude_columns)
226
+ blocks = block_columns(df, exclude)
227
+ matrices = {
228
+ "weather": make_block_matrix(train_df, val_df, test_df, blocks["weather"], categorical=False),
229
+ "geo_fire": make_block_matrix(train_df, val_df, test_df, blocks["geo_fire"], categorical=False),
230
+ "categorical": make_block_matrix(train_df, val_df, test_df, blocks["categorical"], categorical=True),
231
+ }
232
+
233
+ candidate_rows: List[Dict[str, object]] = []
234
+ best = None
235
+ best_score = None
236
+ best_test = None
237
+ block_weight_grid = [
238
+ {"weather": 1.0, "geo_fire": 1.0, "categorical": 1.0},
239
+ {"weather": 0.5, "geo_fire": 1.5, "categorical": 1.0},
240
+ {"weather": 0.25, "geo_fire": 2.0, "categorical": 1.0},
241
+ {"weather": 1.5, "geo_fire": 1.0, "categorical": 0.5},
242
+ {"weather": 0.0, "geo_fire": 2.0, "categorical": 1.0},
243
+ {"weather": 2.0, "geo_fire": 0.5, "categorical": 0.5},
244
+ ]
245
+ target_weight_settings = [(0.0, 1.0), (0.5, 0.25), (1.0, 0.25), (2.0, 0.10)]
246
+ scalar_settings = [("none", 0.0), ("ridge", 0.5), ("ridge", 1.0), ("ridge", 2.0), ("enet", 1.0), ("enet", 2.0)]
247
+
248
+ for bw in block_weight_grid:
249
+ base_train = np.concatenate([matrices[name][0] * bw[name] for name in ["weather", "geo_fire", "categorical"]], axis=1)
250
+ base_val = np.concatenate([matrices[name][1] * bw[name] for name in ["weather", "geo_fire", "categorical"]], axis=1)
251
+ base_test = np.concatenate([matrices[name][2] * bw[name] for name in ["weather", "geo_fire", "categorical"]], axis=1)
252
+ for power, floor in target_weight_settings:
253
+ tw_train, tw_val, tw_test = target_weight_vectors(
254
+ base_train,
255
+ base_val,
256
+ base_test,
257
+ train_df["target_log_burn_acres"].to_numpy(dtype=np.float64),
258
+ power=power,
259
+ floor=floor,
260
+ )
261
+ for scalar_model, scalar_weight in scalar_settings:
262
+ if scalar_model == "none":
263
+ train_vec, val_vec, test_vec = tw_train, tw_val, tw_test
264
+ else:
265
+ train_vec, val_vec, test_vec = append_supervised_scalar(
266
+ tw_train,
267
+ tw_val,
268
+ tw_test,
269
+ train_df,
270
+ scalar_model,
271
+ scalar_weight,
272
+ args.seed,
273
+ )
274
+ for k in [3, 5, 10, 15, 20]:
275
+ for mode in ["mean", "weighted"]:
276
+ val_metrics = score_vectors(val_vec, train_vec, val_df, train_df, k=k, mode=mode)
277
+ test_metrics = score_vectors(test_vec, train_vec, test_df, train_df, k=k, mode=mode)
278
+ row = {
279
+ "block_weights": bw,
280
+ "target_weight_power": power,
281
+ "target_weight_floor": floor,
282
+ "supervised_scalar": scalar_model,
283
+ "supervised_scalar_weight": scalar_weight,
284
+ "k": k,
285
+ "mode": mode,
286
+ "val_metrics": val_metrics,
287
+ "test_metrics": test_metrics,
288
+ }
289
+ candidate_rows.append(row)
290
+ score = float(val_metrics["ndcg_at_10"])
291
+ if best_score is None or score > best_score:
292
+ best_score = score
293
+ best = row
294
+ best_test = test_metrics
295
+
296
+ args.output_dir.mkdir(parents=True, exist_ok=True)
297
+ candidate_df = pd.DataFrame(
298
+ [
299
+ {
300
+ "val_ndcg_at_10": r["val_metrics"]["ndcg_at_10"],
301
+ "val_log_mae": r["val_metrics"]["log_mae"],
302
+ "test_ndcg_at_10": r["test_metrics"]["ndcg_at_10"],
303
+ "test_log_mae": r["test_metrics"]["log_mae"],
304
+ "k": r["k"],
305
+ "mode": r["mode"],
306
+ "target_weight_power": r["target_weight_power"],
307
+ "target_weight_floor": r["target_weight_floor"],
308
+ "supervised_scalar": r["supervised_scalar"],
309
+ "supervised_scalar_weight": r["supervised_scalar_weight"],
310
+ **{f"block_{k}": v for k, v in r["block_weights"].items()},
311
+ }
312
+ for r in candidate_rows
313
+ ]
314
+ )
315
+ candidate_df.to_csv(args.output_dir / "candidate_grid.csv", index=False)
316
+ summary = {
317
+ "task_id": "wildfire_analog_retrieval_extended_hybrid_sweep",
318
+ "event_table": str(args.event_table),
319
+ "seed": int(args.seed),
320
+ "excluded_columns": sorted(exclude),
321
+ "split_sizes": {"train": len(train_df), "val": len(val_df), "test": len(test_df)},
322
+ "feature_blocks": blocks,
323
+ "selection_metric": "val_ndcg_at_10",
324
+ "selected_retrieval": best,
325
+ "test_metrics": best_test,
326
+ "candidate_count": len(candidate_rows),
327
+ }
328
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
329
+ print(json.dumps(summary, indent=2))
330
+
331
+
332
+ if __name__ == "__main__":
333
+ main()
experiments/raw_reference/task_scripts/run_event_analog_taskmodel_seeded.py ADDED
@@ -0,0 +1,350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Dict, List, Tuple
9
+
10
+
11
+ import os
12
+
13
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
14
+ if _p and _p not in sys.path:
15
+ sys.path.insert(0, _p)
16
+
17
+ import faiss
18
+ import hnswlib
19
+ import numpy as np
20
+ import pandas as pd
21
+ from sklearn.compose import ColumnTransformer
22
+ from sklearn.impute import SimpleImputer
23
+ from sklearn.metrics.pairwise import cosine_similarity
24
+ from sklearn.pipeline import Pipeline
25
+ from sklearn.preprocessing import OneHotEncoder, StandardScaler
26
+
27
+
28
+ DROP_COLUMNS = {
29
+ "Event_ID",
30
+ "Incid_Name",
31
+ "incident_name_norm",
32
+ "wfigs_name",
33
+ "Ig_Date",
34
+ "weather_date",
35
+ "BurnBndAc",
36
+ "target_log_burn_acres",
37
+ }
38
+ CATEGORICAL_COLUMNS = ["Incid_Type", "state_abbr", "county_name", "wfigs_match_type"]
39
+
40
+
41
+ def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
42
+ return float(np.sqrt(np.mean((np.asarray(y_true) - np.asarray(y_pred)) ** 2)))
43
+
44
+
45
+ def mape(y_true: np.ndarray, y_pred: np.ndarray) -> float:
46
+ denom = np.clip(np.asarray(y_true, dtype=np.float64), 1e-6, None)
47
+ frac = np.abs(np.asarray(y_true, dtype=np.float64) - np.asarray(y_pred, dtype=np.float64)) / denom
48
+ return float(np.mean(frac))
49
+
50
+
51
+ def r2_score_manual(y_true: np.ndarray, y_pred: np.ndarray) -> float:
52
+ y_true = np.asarray(y_true, dtype=np.float64)
53
+ y_pred = np.asarray(y_pred, dtype=np.float64)
54
+ ss_res = float(np.sum((y_true - y_pred) ** 2))
55
+ ss_tot = float(np.sum((y_true - y_true.mean()) ** 2))
56
+ return float(1.0 - ss_res / ss_tot) if ss_tot > 0 else 0.0
57
+
58
+
59
+ def spearman_corr(y_true: np.ndarray, y_pred: np.ndarray) -> float:
60
+ a = pd.Series(np.asarray(y_true, dtype=np.float64))
61
+ b = pd.Series(np.asarray(y_pred, dtype=np.float64))
62
+ value = a.corr(b, method="spearman")
63
+ return float(value) if pd.notna(value) else 0.0
64
+
65
+
66
+ def build_splits(df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
67
+ ordered = df.sort_values("Ig_Date").reset_index(drop=True)
68
+ n = len(ordered)
69
+ train_end = max(int(round(n * 0.6)), 1)
70
+ val_end = max(int(round(n * 0.8)), train_end + 1)
71
+ val_end = min(val_end, n - 1) if n >= 3 else n
72
+ train = ordered.iloc[:train_end].copy()
73
+ val = ordered.iloc[train_end:val_end].copy()
74
+ test = ordered.iloc[val_end:].copy()
75
+ if len(val) == 0 and len(test) > 1:
76
+ val = test.iloc[:1].copy()
77
+ test = test.iloc[1:].copy()
78
+ return train, val, test
79
+
80
+
81
+ def feature_columns(df: pd.DataFrame, feature_profile: str = "all") -> Tuple[List[str], List[str]]:
82
+ categorical = [c for c in CATEGORICAL_COLUMNS if c in df.columns]
83
+ numeric = []
84
+ for col in df.columns:
85
+ if col in DROP_COLUMNS or col in categorical:
86
+ continue
87
+ if pd.api.types.is_numeric_dtype(df[col]):
88
+ numeric.append(col)
89
+ if feature_profile == "weather_fm":
90
+ numeric = [c for c in numeric if c.startswith("weather_")]
91
+ categorical = []
92
+ return numeric, categorical
93
+
94
+
95
+ def make_preprocessor(numeric_cols: List[str], categorical_cols: List[str]) -> ColumnTransformer:
96
+ return ColumnTransformer(
97
+ transformers=[
98
+ (
99
+ "num",
100
+ Pipeline(
101
+ steps=[
102
+ ("impute", SimpleImputer(strategy="median")),
103
+ ("scale", StandardScaler()),
104
+ ]
105
+ ),
106
+ numeric_cols,
107
+ ),
108
+ (
109
+ "cat",
110
+ Pipeline(
111
+ steps=[
112
+ ("impute", SimpleImputer(strategy="most_frequent")),
113
+ ("onehot", OneHotEncoder(handle_unknown="ignore")),
114
+ ]
115
+ ),
116
+ categorical_cols,
117
+ ),
118
+ ],
119
+ remainder="drop",
120
+ )
121
+
122
+
123
+ def to_dense_float32(x) -> np.ndarray:
124
+ if hasattr(x, "toarray"):
125
+ x = x.toarray()
126
+ return np.asarray(x, dtype=np.float32)
127
+
128
+
129
+ def weighted_prediction(sim: np.ndarray, targets: np.ndarray) -> float:
130
+ weights = np.maximum((np.asarray(sim, dtype=np.float64) + 1.0) / 2.0, 1e-6)
131
+ return float(np.sum(weights * targets) / np.sum(weights))
132
+
133
+
134
+ def graded_relevance(query_target: float, retrieved_targets: np.ndarray) -> np.ndarray:
135
+ delta = np.abs(np.asarray(retrieved_targets, dtype=np.float64) - float(query_target))
136
+ return np.select([delta <= 0.5, delta <= 1.0, delta <= 1.5], [3.0, 2.0, 1.0], default=0.0)
137
+
138
+
139
+ def dcg(relevance: np.ndarray) -> float:
140
+ rel = np.asarray(relevance, dtype=np.float64)
141
+ if rel.size == 0:
142
+ return 0.0
143
+ discounts = 1.0 / np.log2(np.arange(rel.size, dtype=np.float64) + 2.0)
144
+ return float(np.sum(rel * discounts))
145
+
146
+
147
+ def ndcg_at_k(relevance: np.ndarray, ideal_relevance: np.ndarray, k: int) -> float:
148
+ rel = np.asarray(relevance, dtype=np.float64)[:k]
149
+ ideal = np.asarray(ideal_relevance, dtype=np.float64)[:k]
150
+ denom = dcg(ideal)
151
+ return float(dcg(rel) / denom) if denom > 0 else 0.0
152
+
153
+
154
+ def score_backend(
155
+ name: str,
156
+ query_vec: np.ndarray,
157
+ library_vec: np.ndarray,
158
+ query_df: pd.DataFrame,
159
+ library_df: pd.DataFrame,
160
+ k: int,
161
+ mode: str,
162
+ ) -> Tuple[Dict[str, float], pd.DataFrame]:
163
+ target_lib = library_df["target_log_burn_acres"].to_numpy(dtype=np.float64)
164
+ rows = []
165
+ preds = []
166
+ ndcg5 = []
167
+ ndcg10 = []
168
+ hit1 = []
169
+ hit5 = []
170
+ hit10 = []
171
+ best_abs_delta = []
172
+
173
+ k_eff = min(int(k), int(library_vec.shape[0]))
174
+ if name == "cosine_exact":
175
+ sim_all = cosine_similarity(query_vec, library_vec)
176
+ knn_idx = np.argsort(-sim_all, axis=1)[:, :k_eff]
177
+ knn_sim = np.take_along_axis(sim_all, knn_idx, axis=1)
178
+ else:
179
+ library_norm = library_vec / np.clip(np.linalg.norm(library_vec, axis=1, keepdims=True), 1e-12, None)
180
+ query_norm = query_vec / np.clip(np.linalg.norm(query_vec, axis=1, keepdims=True), 1e-12, None)
181
+ if name == "faiss_flat_ip":
182
+ index = faiss.IndexFlatIP(library_norm.shape[1])
183
+ index.add(library_norm.astype(np.float32))
184
+ knn_sim, knn_idx = index.search(query_norm.astype(np.float32), k_eff)
185
+ elif name == "hnsw_cosine":
186
+ index = hnswlib.Index(space="cosine", dim=library_norm.shape[1])
187
+ index.init_index(max_elements=library_norm.shape[0], ef_construction=100, M=16)
188
+ index.add_items(library_norm.astype(np.float32), np.arange(library_norm.shape[0]))
189
+ index.set_ef(max(50, k_eff))
190
+ knn_idx, dist = index.knn_query(query_norm.astype(np.float32), k=k_eff)
191
+ knn_sim = 1.0 - dist
192
+ else:
193
+ raise ValueError(name)
194
+
195
+ for i in range(query_df.shape[0]):
196
+ order = knn_idx[i]
197
+ top_sim = knn_sim[i]
198
+ top_targets = target_lib[order]
199
+ query_target = float(query_df.iloc[i]["target_log_burn_acres"])
200
+ relevance = graded_relevance(query_target, top_targets)
201
+ ideal_relevance = np.sort(graded_relevance(query_target, target_lib))[::-1]
202
+ abs_delta = np.abs(top_targets - float(query_df.iloc[i]["target_log_burn_acres"]))
203
+ ndcg5.append(ndcg_at_k(relevance, ideal_relevance, 5))
204
+ ndcg10.append(ndcg_at_k(relevance, ideal_relevance, 10))
205
+ hit1.append(float(relevance[:1].max() >= 2.0))
206
+ hit5.append(float(relevance[: min(5, k_eff)].max() >= 2.0))
207
+ hit10.append(float(relevance[: min(10, k_eff)].max() >= 2.0))
208
+ best_abs_delta.append(float(abs_delta.min()))
209
+ pred = float(np.mean(top_targets)) if mode == "mean" else weighted_prediction(top_sim, top_targets)
210
+ preds.append(pred)
211
+ rows.append(
212
+ {
213
+ "query_event_id": query_df.iloc[i]["Event_ID"],
214
+ "true_log_burn_acres": float(query_df.iloc[i]["target_log_burn_acres"]),
215
+ "pred_log_burn_acres": pred,
216
+ "backend": name,
217
+ "k": k,
218
+ "effective_k": k_eff,
219
+ "mode": mode,
220
+ "top_relevance": relevance.tolist(),
221
+ "best_abs_log_delta": float(abs_delta.min()),
222
+ }
223
+ )
224
+
225
+ pred_arr = np.asarray(preds, dtype=np.float64)
226
+ true_log = query_df["target_log_burn_acres"].to_numpy(dtype=np.float64)
227
+ true_acres = query_df["BurnBndAc"].to_numpy(dtype=np.float64)
228
+ pred_acres = np.exp(pred_arr)
229
+ metrics = {
230
+ "count": int(len(query_df)),
231
+ "log_mae": float(np.mean(np.abs(true_log - pred_arr))),
232
+ "log_rmse": rmse(true_log, pred_arr),
233
+ "log_r2": r2_score_manual(true_log, pred_arr),
234
+ "log_spearman": spearman_corr(true_log, pred_arr),
235
+ "log_median_ae": float(np.median(np.abs(true_log - pred_arr))),
236
+ "acres_mae": float(np.mean(np.abs(true_acres - pred_acres))),
237
+ "acres_rmse": rmse(true_acres, pred_acres),
238
+ "acres_median_ae": float(np.median(np.abs(true_acres - pred_acres))),
239
+ "acres_mape": mape(true_acres, pred_acres),
240
+ "ndcg_at_5": float(np.mean(ndcg5)) if ndcg5 else 0.0,
241
+ "ndcg_at_10": float(np.mean(ndcg10)) if ndcg10 else 0.0,
242
+ "hit_at_1_tol1": float(np.mean(hit1)) if hit1 else 0.0,
243
+ "hit_at_5_tol1": float(np.mean(hit5)) if hit5 else 0.0,
244
+ "hit_at_10_tol1": float(np.mean(hit10)) if hit10 else 0.0,
245
+ "mean_best_abs_log_delta_at_k": float(np.mean(best_abs_delta)) if best_abs_delta else 0.0,
246
+ }
247
+ return metrics, pd.DataFrame(rows)
248
+
249
+
250
+ def target_weight_vectors(train_vec: np.ndarray, val_vec: np.ndarray, test_vec: np.ndarray, target: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
251
+ x = np.asarray(train_vec, dtype=np.float64)
252
+ y = np.asarray(target, dtype=np.float64)
253
+ y = y - y.mean()
254
+ x_centered = x - x.mean(axis=0, keepdims=True)
255
+ denom = np.clip(np.sqrt(np.sum(x_centered**2, axis=0)) * np.sqrt(np.sum(y**2)), 1e-12, None)
256
+ corr = np.abs(np.sum(x_centered * y[:, None], axis=0) / denom)
257
+ corr = np.nan_to_num(corr, nan=0.0, posinf=0.0, neginf=0.0)
258
+ if float(corr.max()) > 0:
259
+ corr = corr / float(corr.max())
260
+ weights = (0.25 + corr).astype(np.float32)
261
+ return train_vec * weights, val_vec * weights, test_vec * weights
262
+
263
+
264
+ def main() -> None:
265
+ parser = argparse.ArgumentParser()
266
+ parser.add_argument("--event-table", type=Path, required=True)
267
+ parser.add_argument("--output-dir", type=Path, required=True)
268
+ parser.add_argument("--selection-metric", choices=("log_mae", "ndcg_at_10"), default="ndcg_at_10")
269
+ parser.add_argument("--feature-profile", choices=("all", "weather_fm"), default="all")
270
+ parser.add_argument("--fm-family", type=str, default="")
271
+ parser.add_argument("--seed", type=int, default=7)
272
+ args = parser.parse_args()
273
+
274
+ df = pd.read_csv(args.event_table)
275
+ df["Ig_Date"] = pd.to_datetime(df["Ig_Date"])
276
+ train_df, val_df, test_df = build_splits(df)
277
+ numeric_cols, categorical_cols = feature_columns(df, feature_profile=args.feature_profile)
278
+ if not numeric_cols and not categorical_cols:
279
+ raise SystemExit(f"No usable features found for profile={args.feature_profile}")
280
+ x_cols = numeric_cols + categorical_cols
281
+ pre = make_preprocessor(numeric_cols, categorical_cols)
282
+ train_vec = to_dense_float32(pre.fit_transform(train_df[x_cols]))
283
+ val_vec = to_dense_float32(pre.transform(val_df[x_cols]))
284
+ test_vec = to_dense_float32(pre.transform(test_df[x_cols]))
285
+ weighted_train_vec, weighted_val_vec, weighted_test_vec = target_weight_vectors(
286
+ train_vec,
287
+ val_vec,
288
+ test_vec,
289
+ train_df["target_log_burn_acres"].to_numpy(dtype=np.float64),
290
+ )
291
+ vector_variants = {
292
+ "standard": (train_vec, val_vec, test_vec),
293
+ "target_weighted": (weighted_train_vec, weighted_val_vec, weighted_test_vec),
294
+ }
295
+
296
+ candidate_validation: List[Dict[str, object]] = []
297
+ best = None
298
+ best_score = None
299
+ best_val_rows = None
300
+ best_test_rows = None
301
+ for variant, (lib_vec, v_vec, _) in vector_variants.items():
302
+ for backend in ["cosine_exact", "faiss_flat_ip", "hnsw_cosine"]:
303
+ for k in [1, 3, 5, 10]:
304
+ for mode in ["mean", "weighted"]:
305
+ val_metrics, val_rows = score_backend(backend, v_vec, lib_vec, val_df, train_df, k, mode)
306
+ candidate_validation.append({"variant": variant, "backend": backend, "k": k, "mode": mode, "val_metrics": val_metrics})
307
+ score = float(val_metrics[args.selection_metric])
308
+ better = score > best_score if args.selection_metric == "ndcg_at_10" and best_score is not None else score < best_score if best_score is not None else True
309
+ if better:
310
+ best_score = score
311
+ best = {"variant": variant, "backend": backend, "k": k, "mode": mode}
312
+ best_val_rows = val_rows
313
+
314
+ assert best is not None
315
+ best_train_vec, _, best_test_vec = vector_variants[str(best["variant"])]
316
+ test_metrics, test_rows = score_backend(best["backend"], best_test_vec, best_train_vec, test_df, train_df, int(best["k"]), str(best["mode"]))
317
+ best_test_rows = test_rows
318
+
319
+ args.output_dir.mkdir(parents=True, exist_ok=True)
320
+ if best_val_rows is not None:
321
+ best_val_rows.to_csv(args.output_dir / "val_retrieval_examples.csv", index=False)
322
+ if best_test_rows is not None:
323
+ best_test_rows.to_csv(args.output_dir / "test_retrieval_examples.csv", index=False)
324
+
325
+ summary = {
326
+ "task_id": "wildfire_analog_retrieval_taskmodels",
327
+ "task_form": "event_level_retrieval_with_induced_outcome_error",
328
+ "event_table": str(args.event_table),
329
+ "output_dir": str(args.output_dir),
330
+ "feature_profile": args.feature_profile,
331
+ "seed": int(args.seed),
332
+ "split_sizes": {
333
+ "train": int(len(train_df)),
334
+ "val": int(len(val_df)),
335
+ "test": int(len(test_df)),
336
+ },
337
+ "feature_columns": {"numeric": numeric_cols, "categorical": categorical_cols},
338
+ "candidate_validation": candidate_validation,
339
+ "selected_retrieval": best,
340
+ "selection_metric": args.selection_metric,
341
+ "test_metrics": test_metrics,
342
+ "model_family": "popular_open_source_retrieval_backends_with_train_only_target_weighting",
343
+ "fm_family": (args.fm_family or "weather_fm_derived_features") if args.feature_profile == "weather_fm" else None,
344
+ }
345
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
346
+ print(json.dumps(summary, indent=2))
347
+
348
+
349
+ if __name__ == "__main__":
350
+ main()
experiments/raw_reference/task_scripts/run_extreme_heat_alphaearth_suite_seeded.py ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import re
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Dict, Iterable, List, Optional, Tuple
11
+
12
+
13
+ import os
14
+
15
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
16
+ if _p and _p not in sys.path:
17
+ sys.path.insert(0, _p)
18
+
19
+ import numpy as np
20
+ import pandas as pd
21
+ from catboost import CatBoostRegressor
22
+ from lightgbm import LGBMRegressor
23
+ from netCDF4 import Dataset
24
+ from sklearn.linear_model import ElasticNet, Ridge
25
+ from sklearn.metrics import mean_absolute_error, mean_squared_error
26
+ from xgboost import XGBRegressor
27
+
28
+
29
+ PRED_RE = re.compile(r"pred_(\d{8})_(\d{2})\.nc$")
30
+ WEATHER_VARS = ["T2M", "QV2M", "TQV", "U10M", "V10M", "TS"]
31
+
32
+
33
+ def parse_args() -> argparse.Namespace:
34
+ parser = argparse.ArgumentParser()
35
+ parser.add_argument("--pred-root", type=Path, action="append", required=True)
36
+ parser.add_argument("--merra-root", type=Path, required=True)
37
+ parser.add_argument("--alphaearth-year-csv", type=Path, required=True)
38
+ parser.add_argument("--output-dir", type=Path, required=True)
39
+ parser.add_argument("--model-family", choices=("full", "lite"), default="full")
40
+ parser.add_argument("--alphaearth-prefix", type=str, default="alphaearth_")
41
+ parser.add_argument("--lat-min", type=float, default=24.0)
42
+ parser.add_argument("--lat-max", type=float, default=50.0)
43
+ parser.add_argument("--lon-min", type=float, default=-125.0)
44
+ parser.add_argument("--lon-max", type=float, default=-66.0)
45
+ parser.add_argument("--seed", type=int, default=7)
46
+ return parser.parse_args()
47
+
48
+
49
+ def choose_split(path: Path) -> Optional[str]:
50
+ name = path.name
51
+ if name == "output2022":
52
+ return "train"
53
+ if name == "output2024":
54
+ return "val"
55
+ if name == "output2025":
56
+ return "test"
57
+ return None
58
+
59
+
60
+ def parse_pred_timestamp(path: Path) -> Tuple[str, int]:
61
+ match = PRED_RE.match(path.name)
62
+ if not match:
63
+ raise ValueError(f"Unexpected prediction filename: {path}")
64
+ return match.group(1), int(match.group(2))
65
+
66
+
67
+ def nearest_indices(src: np.ndarray, dst: np.ndarray) -> np.ndarray:
68
+ idx = np.searchsorted(dst, src)
69
+ idx = np.clip(idx, 0, len(dst) - 1)
70
+ prev_idx = np.clip(idx - 1, 0, len(dst) - 1)
71
+ choose_prev = np.abs(dst[prev_idx] - src) <= np.abs(dst[idx] - src)
72
+ return np.where(choose_prev, prev_idx, idx).astype(np.int64)
73
+
74
+
75
+ def build_grid_alignment(sample_pred: Path, sample_merra: Path, lat_min: float, lat_max: float, lon_min: float, lon_max: float) -> Dict[str, np.ndarray]:
76
+ with Dataset(sample_pred) as pred_ds, Dataset(sample_merra) as merra_ds:
77
+ pred_lat = np.asarray(pred_ds.variables["lat"][:], dtype=np.float64)
78
+ pred_lon = np.asarray(pred_ds.variables["lon"][:], dtype=np.float64)
79
+ merra_lat = np.asarray(merra_ds.variables["lat"][:], dtype=np.float64)
80
+ merra_lon = np.asarray(merra_ds.variables["lon"][:], dtype=np.float64)
81
+
82
+ lat_mask = (pred_lat >= lat_min) & (pred_lat <= lat_max)
83
+ lon_mask = (pred_lon >= lon_min) & (pred_lon <= lon_max)
84
+ pred_lat_idx = np.flatnonzero(lat_mask)
85
+ pred_lon_idx = np.flatnonzero(lon_mask)
86
+ pred_lat_sel = pred_lat[pred_lat_idx]
87
+ pred_lon_sel = pred_lon[pred_lon_idx]
88
+ merra_lat_idx = nearest_indices(pred_lat_sel, merra_lat)
89
+ merra_lon_idx = nearest_indices(pred_lon_sel, merra_lon)
90
+
91
+ return {
92
+ "pred_lat_idx": pred_lat_idx,
93
+ "pred_lon_idx": pred_lon_idx,
94
+ "merra_lat_idx": merra_lat_idx,
95
+ "merra_lon_idx": merra_lon_idx,
96
+ }
97
+
98
+
99
+ def feature_stats(arr: np.ndarray) -> Dict[str, float]:
100
+ return {"mean": float(np.mean(arr)), "max": float(np.max(arr)), "std": float(np.std(arr))}
101
+
102
+
103
+ def build_rows(pred_roots: Iterable[Path], merra_root: Path, alignment: Dict[str, np.ndarray]) -> pd.DataFrame:
104
+ rows: List[Dict[str, float]] = []
105
+ for root in pred_roots:
106
+ split = choose_split(root)
107
+ if split is None:
108
+ continue
109
+ for path in sorted(root.glob("pred_*.nc")):
110
+ day, hour = parse_pred_timestamp(path)
111
+ if hour % 3 != 0:
112
+ continue
113
+ merra_path = merra_root / f"MERRA2_sfc_{day}.nc"
114
+ if not merra_path.exists():
115
+ continue
116
+ time_index = hour // 3
117
+ with Dataset(path) as pred_ds, Dataset(merra_path) as merra_ds:
118
+ date = pd.Timestamp(day)
119
+ record: Dict[str, float] = {"split": split, "hour": float(hour), "year": float(date.year), "date": day}
120
+ record["doy"] = float(date.dayofyear)
121
+ record["month"] = float(date.month)
122
+ for var in WEATHER_VARS:
123
+ pred_arr = np.asarray(
124
+ pred_ds.variables[var][0, alignment["pred_lat_idx"], alignment["pred_lon_idx"]],
125
+ dtype=np.float64,
126
+ )
127
+ stats = feature_stats(pred_arr)
128
+ record[f"pred_{var.lower()}_mean"] = stats["mean"]
129
+ record[f"pred_{var.lower()}_max"] = stats["max"]
130
+ record[f"pred_{var.lower()}_std"] = stats["std"]
131
+ record["pred_wind_mean"] = float(
132
+ np.mean(
133
+ np.sqrt(
134
+ np.square(pred_ds.variables["U10M"][0, alignment["pred_lat_idx"], alignment["pred_lon_idx"]])
135
+ + np.square(pred_ds.variables["V10M"][0, alignment["pred_lat_idx"], alignment["pred_lon_idx"]])
136
+ )
137
+ )
138
+ )
139
+ truth_t2m = np.asarray(merra_ds.variables["T2M"][time_index], dtype=np.float64)[
140
+ np.ix_(alignment["merra_lat_idx"], alignment["merra_lon_idx"])
141
+ ]
142
+ truth_ts = np.asarray(merra_ds.variables["TS"][time_index], dtype=np.float64)[
143
+ np.ix_(alignment["merra_lat_idx"], alignment["merra_lon_idx"])
144
+ ]
145
+ record["target_t2m_mean_c"] = float(np.mean(truth_t2m) - 273.15)
146
+ record["target_t2m_max_c"] = float(np.max(truth_t2m) - 273.15)
147
+ record["target_ts_mean_c"] = float(np.mean(truth_ts) - 273.15)
148
+ rows.append(record)
149
+
150
+ if not rows:
151
+ raise SystemExit("No extreme-heat rows were built from the provided roots.")
152
+ df = pd.DataFrame(rows)
153
+ angle_day = 2.0 * np.pi * df["doy"].to_numpy(dtype=np.float64) / 366.0
154
+ angle_hour = 2.0 * np.pi * df["hour"].to_numpy(dtype=np.float64) / 24.0
155
+ df["doy_sin"] = np.sin(angle_day)
156
+ df["doy_cos"] = np.cos(angle_day)
157
+ df["hour_sin"] = np.sin(angle_hour)
158
+ df["hour_cos"] = np.cos(angle_hour)
159
+ return df
160
+
161
+
162
+ def drop_nonfinite_rows(df: pd.DataFrame, columns: List[str]) -> pd.DataFrame:
163
+ mask = np.ones(len(df), dtype=bool)
164
+ for col in columns:
165
+ mask &= np.isfinite(pd.to_numeric(df[col], errors="coerce").to_numpy(dtype=np.float64))
166
+ return df.loc[mask].reset_index(drop=True)
167
+
168
+
169
+ def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
170
+ return float(math.sqrt(mean_squared_error(y_true, y_pred)))
171
+
172
+
173
+ def pearson_corr(y_true: np.ndarray, y_pred: np.ndarray) -> float:
174
+ a = np.asarray(y_true, dtype=np.float64)
175
+ b = np.asarray(y_pred, dtype=np.float64)
176
+ if a.size < 2 or np.allclose(a, a[0]) or np.allclose(b, b[0]):
177
+ return 0.0
178
+ value = float(np.corrcoef(a, b)[0, 1])
179
+ return value if np.isfinite(value) else 0.0
180
+
181
+
182
+ def prf(y_true: np.ndarray, y_pred: np.ndarray, threshold: float) -> Dict[str, float]:
183
+ truth = np.asarray(y_true >= threshold)
184
+ pred = np.asarray(y_pred >= threshold)
185
+ tp = int(np.logical_and(pred, truth).sum())
186
+ fp = int(np.logical_and(pred, ~truth).sum())
187
+ fn = int(np.logical_and(~pred, truth).sum())
188
+ precision = float(tp / (tp + fp)) if (tp + fp) else 0.0
189
+ recall = float(tp / (tp + fn)) if (tp + fn) else 0.0
190
+ f1 = float((2.0 * precision * recall) / (precision + recall)) if (precision + recall) else 0.0
191
+ return {"precision": precision, "recall": recall, "f1": f1}
192
+
193
+
194
+ def evaluate(y_true: np.ndarray, y_pred: np.ndarray) -> Dict[str, float]:
195
+ return {
196
+ "count": int(y_true.shape[0]),
197
+ "rmse_c": rmse(y_true, y_pred),
198
+ "mae_c": float(mean_absolute_error(y_true, y_pred)),
199
+ "pearson_r": pearson_corr(y_true, y_pred),
200
+ }
201
+
202
+
203
+ def main() -> None:
204
+ args = parse_args()
205
+ pred_files = [path for root in args.pred_root for path in root.glob("pred_*.nc")]
206
+ if not pred_files:
207
+ raise SystemExit("No prediction files found.")
208
+ sample_pred = sorted(pred_files)[0]
209
+ sample_day, _ = parse_pred_timestamp(sample_pred)
210
+ sample_merra = args.merra_root / f"MERRA2_sfc_{sample_day}.nc"
211
+ if not sample_merra.exists():
212
+ raise SystemExit(f"Sample MERRA file missing: {sample_merra}")
213
+
214
+ alignment = build_grid_alignment(
215
+ sample_pred,
216
+ sample_merra,
217
+ lat_min=args.lat_min,
218
+ lat_max=args.lat_max,
219
+ lon_min=args.lon_min,
220
+ lon_max=args.lon_max,
221
+ )
222
+ df = build_rows(args.pred_root, args.merra_root, alignment)
223
+
224
+ alpha = pd.read_csv(args.alphaearth_year_csv)
225
+ alpha["source_year_key"] = pd.to_numeric(alpha["alphaearth_source_year"], errors="coerce").astype("Int64")
226
+ df["source_year_key"] = pd.to_numeric(df["year"], errors="coerce").clip(lower=2017, upper=2024).astype("Int64")
227
+ df = df.merge(alpha.drop(columns=[c for c in ["requested_year"] if c in alpha.columns]), on="source_year_key", how="left")
228
+
229
+ feature_cols = [c for c in df.columns if c.startswith("pred_") or c in {"month", "doy_sin", "doy_cos", "hour_sin", "hour_cos"}]
230
+ feature_cols.extend(sorted([c for c in df.columns if c.startswith(args.alphaearth_prefix)]))
231
+ finite_cols = feature_cols + ["target_t2m_mean_c"]
232
+ df = drop_nonfinite_rows(df, finite_cols)
233
+ if df.empty:
234
+ raise SystemExit("Extreme-heat AlphaEarth suite has no finite rows after filtering.")
235
+
236
+ train = df[df["split"] == "train"].copy()
237
+ val = df[df["split"] == "val"].copy()
238
+ test = df[df["split"] == "test"].copy()
239
+ if len(train) == 0 or len(val) == 0 or len(test) == 0:
240
+ raise SystemExit("Extreme-heat AlphaEarth suite is missing one of train/val/test.")
241
+
242
+ x_train = train[feature_cols].to_numpy(dtype=np.float64)
243
+ x_val = val[feature_cols].to_numpy(dtype=np.float64)
244
+ x_test = test[feature_cols].to_numpy(dtype=np.float64)
245
+ y_train = train["target_t2m_mean_c"].to_numpy(dtype=np.float64)
246
+ y_val = val["target_t2m_mean_c"].to_numpy(dtype=np.float64)
247
+ y_test = test["target_t2m_mean_c"].to_numpy(dtype=np.float64)
248
+
249
+ candidates: Dict[str, object] = {
250
+ "ridge": Ridge(alpha=1.0, random_state=args.seed),
251
+ "enet": ElasticNet(alpha=0.01, l1_ratio=0.2, random_state=args.seed, max_iter=10000),
252
+ }
253
+ if args.model_family == "full":
254
+ candidates.update(
255
+ {
256
+ "xgboost": XGBRegressor(
257
+ n_estimators=300,
258
+ max_depth=6,
259
+ learning_rate=0.05,
260
+ subsample=0.8,
261
+ colsample_bytree=0.8,
262
+ objective="reg:squarederror",
263
+ tree_method="hist",
264
+ random_state=args.seed,
265
+ n_jobs=8,
266
+ ),
267
+ "lightgbm": LGBMRegressor(
268
+ n_estimators=300,
269
+ learning_rate=0.05,
270
+ num_leaves=63,
271
+ subsample=0.8,
272
+ colsample_bytree=0.8,
273
+ random_state=args.seed,
274
+ n_jobs=8,
275
+ verbose=-1,
276
+ ),
277
+ "catboost": CatBoostRegressor(
278
+ iterations=400,
279
+ depth=8,
280
+ learning_rate=0.05,
281
+ loss_function="RMSE",
282
+ eval_metric="RMSE",
283
+ random_seed=args.seed,
284
+ verbose=False,
285
+ ),
286
+ }
287
+ )
288
+
289
+ candidate_rows = []
290
+ best_name = None
291
+ best_model = None
292
+ best_rmse = None
293
+ for name, model in candidates.items():
294
+ model.fit(x_train, y_train)
295
+ val_pred = np.asarray(model.predict(x_val), dtype=np.float64)
296
+ val_metrics = evaluate(y_val, val_pred)
297
+ candidate_rows.append({"model": name, "validation": val_metrics})
298
+ if best_rmse is None or val_metrics["rmse_c"] < best_rmse:
299
+ best_name = name
300
+ best_model = model
301
+ best_rmse = val_metrics["rmse_c"]
302
+
303
+ assert best_model is not None and best_name is not None
304
+ val_pred = np.asarray(best_model.predict(x_val), dtype=np.float64)
305
+ test_pred = np.asarray(best_model.predict(x_test), dtype=np.float64)
306
+
307
+ thresholds = [27.0, 30.0, 33.0]
308
+ val_events = [{"threshold_c": t, **prf(y_val, val_pred, t)} for t in thresholds]
309
+ val_events = sorted(val_events, key=lambda row: (-row["f1"], -row["recall"], -row["precision"], row["threshold_c"]))
310
+ selected_event = val_events[0]
311
+ test_event = {"threshold_c": selected_event["threshold_c"], **prf(y_test, test_pred, selected_event["threshold_c"])}
312
+
313
+ summary = {
314
+ "task_id": "extreme_heat_alphaearth",
315
+ "core_line": "extreme_heat",
316
+ "task_form": "continuous_temperature_forecast_with_secondary_exceedance_view",
317
+ "seed": int(args.seed),
318
+ "pred_roots": [str(path) for path in args.pred_root],
319
+ "merra_root": str(args.merra_root),
320
+ "alphaearth_year_csv": str(args.alphaearth_year_csv),
321
+ "model_family": args.model_family,
322
+ "feature_columns": feature_cols,
323
+ "alphaearth_feature_count": int(sum(c.startswith(args.alphaearth_prefix) for c in feature_cols)),
324
+ "candidate_validation": candidate_rows,
325
+ "selected_model": best_name,
326
+ "validation_metrics": evaluate(y_val, val_pred),
327
+ "test_metrics": evaluate(y_test, test_pred),
328
+ "selected_event_candidate": selected_event,
329
+ "selected_event_candidate_test": test_event,
330
+ "selection_rule": "same heat benchmark; choose regressor by validation RMSE and choose exceedance threshold by validation F1",
331
+ "tmt_policy": {
332
+ "task": "extreme_heat",
333
+ "metric": "continuous RMSE/MAE with thresholded exceedance as a secondary event policy",
334
+ "tolerance": "none for continuous headline; event threshold only for operational view"
335
+ },
336
+ }
337
+
338
+ args.output_dir.mkdir(parents=True, exist_ok=True)
339
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
340
+ print(json.dumps(summary, indent=2))
341
+
342
+
343
+ if __name__ == "__main__":
344
+ main()
experiments/raw_reference/task_scripts/run_final_area_taskmodel_seeded.py ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Dict, List, Tuple
9
+
10
+
11
+ import os
12
+
13
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
14
+ if _p and _p not in sys.path:
15
+ sys.path.insert(0, _p)
16
+
17
+ import numpy as np
18
+ import pandas as pd
19
+ from catboost import CatBoostRegressor
20
+ from lightgbm import LGBMRegressor
21
+ from sklearn.compose import ColumnTransformer
22
+ from sklearn.impute import SimpleImputer
23
+ from sklearn.linear_model import ElasticNet
24
+ from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
25
+ from sklearn.pipeline import Pipeline
26
+ from sklearn.preprocessing import OneHotEncoder, StandardScaler
27
+ from xgboost import XGBRegressor
28
+
29
+
30
+ DROP_COLUMNS = {
31
+ "Event_ID",
32
+ "Incid_Name",
33
+ "incident_name_norm",
34
+ "wfigs_name",
35
+ "Ig_Date",
36
+ "weather_date",
37
+ "BurnBndAc",
38
+ "target_log_burn_acres",
39
+ }
40
+
41
+ CATEGORICAL_COLUMNS = [
42
+ "Incid_Type",
43
+ "state_abbr",
44
+ "county_name",
45
+ "wfigs_match_type",
46
+ ]
47
+
48
+
49
+ def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
50
+ return float(np.sqrt(mean_squared_error(y_true, y_pred)))
51
+
52
+
53
+ def mape(y_true: np.ndarray, y_pred: np.ndarray) -> float:
54
+ denom = np.clip(np.asarray(y_true, dtype=np.float64), 1e-6, None)
55
+ frac = np.abs(np.asarray(y_true, dtype=np.float64) - np.asarray(y_pred, dtype=np.float64)) / denom
56
+ return float(np.mean(frac))
57
+
58
+
59
+ def spearman_corr(y_true: np.ndarray, y_pred: np.ndarray) -> float:
60
+ a = pd.Series(np.asarray(y_true, dtype=np.float64))
61
+ b = pd.Series(np.asarray(y_pred, dtype=np.float64))
62
+ value = a.corr(b, method="spearman")
63
+ return float(value) if pd.notna(value) else 0.0
64
+
65
+
66
+ def build_splits(df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
67
+ ordered = df.sort_values("Ig_Date").reset_index(drop=True)
68
+ n = len(ordered)
69
+ train_end = max(int(round(n * 0.6)), 1)
70
+ val_end = max(int(round(n * 0.8)), train_end + 1)
71
+ val_end = min(val_end, n - 1) if n >= 3 else n
72
+ train = ordered.iloc[:train_end].copy()
73
+ val = ordered.iloc[train_end:val_end].copy()
74
+ test = ordered.iloc[val_end:].copy()
75
+ if len(val) == 0 and len(test) > 1:
76
+ val = test.iloc[:1].copy()
77
+ test = test.iloc[1:].copy()
78
+ return train, val, test
79
+
80
+
81
+ def feature_columns(df: pd.DataFrame, feature_profile: str = "all") -> Tuple[List[str], List[str]]:
82
+ categorical = [c for c in CATEGORICAL_COLUMNS if c in df.columns]
83
+ numeric = []
84
+ for col in df.columns:
85
+ if col in DROP_COLUMNS or col in categorical:
86
+ continue
87
+ if pd.api.types.is_numeric_dtype(df[col]):
88
+ numeric.append(col)
89
+ if feature_profile == "weather_fm":
90
+ numeric = [c for c in numeric if c.startswith("weather_")]
91
+ categorical = []
92
+ return numeric, categorical
93
+
94
+
95
+ def make_sparse_preprocessor(numeric_cols: List[str], categorical_cols: List[str]) -> ColumnTransformer:
96
+ return ColumnTransformer(
97
+ transformers=[
98
+ (
99
+ "num",
100
+ Pipeline(
101
+ steps=[
102
+ ("impute", SimpleImputer(strategy="median")),
103
+ ("scale", StandardScaler()),
104
+ ]
105
+ ),
106
+ numeric_cols,
107
+ ),
108
+ (
109
+ "cat",
110
+ Pipeline(
111
+ steps=[
112
+ ("impute", SimpleImputer(strategy="most_frequent")),
113
+ ("onehot", OneHotEncoder(handle_unknown="ignore")),
114
+ ]
115
+ ),
116
+ categorical_cols,
117
+ ),
118
+ ],
119
+ remainder="drop",
120
+ )
121
+
122
+
123
+ def prepare_catboost_frames(
124
+ train_df: pd.DataFrame,
125
+ val_df: pd.DataFrame,
126
+ test_df: pd.DataFrame,
127
+ numeric_cols: List[str],
128
+ categorical_cols: List[str],
129
+ ) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
130
+ medians = {c: float(train_df[c].median()) for c in numeric_cols}
131
+ modes = {
132
+ c: str(train_df[c].mode(dropna=True).iloc[0]) if not train_df[c].mode(dropna=True).empty else "missing"
133
+ for c in categorical_cols
134
+ }
135
+
136
+ def _prep(frame: pd.DataFrame) -> pd.DataFrame:
137
+ out = frame[numeric_cols + categorical_cols].copy()
138
+ for col in numeric_cols:
139
+ out[col] = pd.to_numeric(out[col], errors="coerce").fillna(medians[col])
140
+ for col in categorical_cols:
141
+ out[col] = out[col].astype("string").fillna(modes[col]).astype(str)
142
+ return out
143
+
144
+ return _prep(train_df), _prep(val_df), _prep(test_df)
145
+
146
+
147
+ def evaluate_split(frame: pd.DataFrame, pred_log: np.ndarray) -> Dict[str, float]:
148
+ true_log = frame["target_log_burn_acres"].to_numpy(dtype=np.float64)
149
+ true_acres = frame["BurnBndAc"].to_numpy(dtype=np.float64)
150
+ pred_log = np.asarray(pred_log, dtype=np.float64)
151
+ pred_acres = np.exp(pred_log)
152
+ return {
153
+ "count": int(len(frame)),
154
+ "log_mae": float(mean_absolute_error(true_log, pred_log)),
155
+ "log_rmse": rmse(true_log, pred_log),
156
+ "log_r2": float(r2_score(true_log, pred_log)) if len(frame) > 1 else 0.0,
157
+ "log_spearman": spearman_corr(true_log, pred_log),
158
+ "log_median_ae": float(np.median(np.abs(true_log - pred_log))),
159
+ "acres_mae": float(mean_absolute_error(true_acres, pred_acres)),
160
+ "acres_rmse": rmse(true_acres, pred_acres),
161
+ "acres_median_ae": float(np.median(np.abs(true_acres - pred_acres))),
162
+ "acres_mape": mape(true_acres, pred_acres),
163
+ }
164
+
165
+
166
+ def main() -> None:
167
+ parser = argparse.ArgumentParser()
168
+ parser.add_argument("--event-table", type=Path, required=True)
169
+ parser.add_argument("--output-dir", type=Path, required=True)
170
+ parser.add_argument("--feature-profile", choices=("all", "weather_fm"), default="all")
171
+ parser.add_argument("--model-family", choices=("full", "lite"), default="full")
172
+ parser.add_argument("--fm-family", type=str, default="")
173
+ parser.add_argument("--seed", type=int, default=7)
174
+ args = parser.parse_args()
175
+
176
+ df = pd.read_csv(args.event_table)
177
+ df["Ig_Date"] = pd.to_datetime(df["Ig_Date"])
178
+ train_df, val_df, test_df = build_splits(df)
179
+ numeric_cols, categorical_cols = feature_columns(df, feature_profile=args.feature_profile)
180
+ if not numeric_cols and not categorical_cols:
181
+ raise SystemExit(f"No usable features found for profile={args.feature_profile}")
182
+ x_cols = numeric_cols + categorical_cols
183
+
184
+ pre = make_sparse_preprocessor(numeric_cols, categorical_cols)
185
+ x_train = pre.fit_transform(train_df[x_cols])
186
+ x_val = pre.transform(val_df[x_cols])
187
+ x_test = pre.transform(test_df[x_cols])
188
+ y_train = train_df["target_log_burn_acres"].to_numpy(dtype=np.float64)
189
+
190
+ cat_train, cat_val, cat_test = prepare_catboost_frames(train_df, val_df, test_df, numeric_cols, categorical_cols)
191
+ cat_feature_idx = list(range(len(numeric_cols), len(numeric_cols) + len(categorical_cols)))
192
+
193
+ candidates: List[Tuple[str, object, str]] = [
194
+ (
195
+ "enet",
196
+ ElasticNet(alpha=0.01, l1_ratio=0.2, random_state=args.seed, max_iter=10000),
197
+ "sparse",
198
+ ),
199
+ ]
200
+ if args.model_family == "full":
201
+ candidates.extend(
202
+ [
203
+ (
204
+ "xgboost",
205
+ XGBRegressor(
206
+ n_estimators=400,
207
+ max_depth=6,
208
+ learning_rate=0.05,
209
+ subsample=0.8,
210
+ colsample_bytree=0.8,
211
+ reg_lambda=1.0,
212
+ objective="reg:squarederror",
213
+ tree_method="hist",
214
+ random_state=args.seed,
215
+ n_jobs=8,
216
+ ),
217
+ "sparse",
218
+ ),
219
+ (
220
+ "lightgbm",
221
+ LGBMRegressor(
222
+ n_estimators=400,
223
+ learning_rate=0.05,
224
+ num_leaves=63,
225
+ subsample=0.8,
226
+ colsample_bytree=0.8,
227
+ reg_lambda=1.0,
228
+ random_state=args.seed,
229
+ n_jobs=8,
230
+ verbose=-1,
231
+ ),
232
+ "sparse",
233
+ ),
234
+ (
235
+ "catboost",
236
+ CatBoostRegressor(
237
+ iterations=500,
238
+ depth=8,
239
+ learning_rate=0.05,
240
+ loss_function="RMSE",
241
+ eval_metric="RMSE",
242
+ random_seed=args.seed,
243
+ verbose=False,
244
+ ),
245
+ "cat",
246
+ ),
247
+ ]
248
+ )
249
+
250
+ candidate_validation: List[Dict[str, object]] = []
251
+ best_name = None
252
+ best_kind = None
253
+ best_model = None
254
+ best_score = None
255
+
256
+ for name, model, kind in candidates:
257
+ if kind == "sparse":
258
+ model.fit(x_train, y_train)
259
+ val_pred = model.predict(x_val)
260
+ else:
261
+ model.fit(cat_train, y_train, cat_features=cat_feature_idx, eval_set=(cat_val, val_df["target_log_burn_acres"]), use_best_model=False)
262
+ val_pred = model.predict(cat_val)
263
+ val_metrics = evaluate_split(val_df, val_pred)
264
+ candidate_validation.append({"model_name": name, "val_metrics": val_metrics})
265
+ score = float(val_metrics["log_mae"])
266
+ if best_score is None or score < best_score:
267
+ best_score = score
268
+ best_name = name
269
+ best_kind = kind
270
+ best_model = model
271
+
272
+ assert best_model is not None and best_name is not None and best_kind is not None
273
+
274
+ combined_train = pd.concat([train_df, val_df], ignore_index=True)
275
+ if best_kind == "sparse":
276
+ x_combined = pre.fit_transform(combined_train[x_cols])
277
+ x_train_final = pre.transform(train_df[x_cols])
278
+ x_val_final = pre.transform(val_df[x_cols])
279
+ x_test_final = pre.transform(test_df[x_cols])
280
+ best_model.fit(x_combined, combined_train["target_log_burn_acres"].to_numpy(dtype=np.float64))
281
+ train_pred = best_model.predict(x_train_final)
282
+ val_pred = best_model.predict(x_val_final)
283
+ test_pred = best_model.predict(x_test_final)
284
+ else:
285
+ cat_combined, cat_train_final, cat_test_final = prepare_catboost_frames(
286
+ combined_train, train_df, test_df, numeric_cols, categorical_cols
287
+ )
288
+ cat_val_final = prepare_catboost_frames(val_df, val_df, val_df, numeric_cols, categorical_cols)[0]
289
+ best_model.fit(
290
+ cat_combined,
291
+ combined_train["target_log_burn_acres"].to_numpy(dtype=np.float64),
292
+ cat_features=cat_feature_idx,
293
+ use_best_model=False,
294
+ )
295
+ train_pred = best_model.predict(cat_train_final)
296
+ val_pred = best_model.predict(cat_val_final)
297
+ test_pred = best_model.predict(cat_test_final)
298
+
299
+ args.output_dir.mkdir(parents=True, exist_ok=True)
300
+ pred_df = pd.concat(
301
+ [
302
+ train_df.assign(split="train", pred_log_burn_acres=train_pred, pred_burn_acres=np.exp(train_pred)),
303
+ val_df.assign(split="val", pred_log_burn_acres=val_pred, pred_burn_acres=np.exp(val_pred)),
304
+ test_df.assign(split="test", pred_log_burn_acres=test_pred, pred_burn_acres=np.exp(test_pred)),
305
+ ],
306
+ axis=0,
307
+ ignore_index=True,
308
+ )
309
+ pred_path = args.output_dir / "predictions.csv"
310
+ pred_df.to_csv(pred_path, index=False)
311
+
312
+ summary = {
313
+ "task_id": "wildfire_final_area_scalar_taskmodels",
314
+ "task_form": "event_level_regression",
315
+ "event_table": str(args.event_table),
316
+ "output_dir": str(args.output_dir),
317
+ "feature_profile": args.feature_profile,
318
+ "seed": int(args.seed),
319
+ "benchmark_protocol": "fm_lite_protocol" if args.feature_profile == "weather_fm" and args.model_family == "lite" else "standard_protocol",
320
+ "split_sizes": {
321
+ "train": int(len(train_df)),
322
+ "val": int(len(val_df)),
323
+ "test": int(len(test_df)),
324
+ },
325
+ "feature_columns": {
326
+ "numeric": numeric_cols,
327
+ "categorical": categorical_cols,
328
+ },
329
+ "candidate_validation": candidate_validation,
330
+ "selected_model": best_name,
331
+ "train_metrics": evaluate_split(train_df, train_pred),
332
+ "val_metrics": evaluate_split(val_df, val_pred),
333
+ "test_metrics": evaluate_split(test_df, test_pred),
334
+ "headline_metrics": {
335
+ "log_mae": float(evaluate_split(test_df, test_pred)["log_mae"]),
336
+ "log_rmse": float(evaluate_split(test_df, test_pred)["log_rmse"]),
337
+ "log_spearman": float(evaluate_split(test_df, test_pred)["log_spearman"]),
338
+ },
339
+ "predictions_path": str(pred_path),
340
+ "model_family": "lightweight_linear_task_heads" if args.model_family == "lite" else "popular_open_source_task_models",
341
+ "fm_family": (args.fm_family or "weather_fm_derived_features") if args.feature_profile == "weather_fm" else None,
342
+ "tmt_policy": {
343
+ "task": "final_burned_area",
344
+ "metric": "log-area regression error with rank agreement",
345
+ "tolerance": "secondary magnitude-band interpretation only",
346
+ },
347
+ }
348
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
349
+ print(json.dumps(summary, indent=2))
350
+
351
+
352
+ if __name__ == "__main__":
353
+ main()
experiments/raw_reference/task_scripts/run_smoke_pm25_alphaearth_suite_seeded.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Dict, List, Tuple
10
+
11
+
12
+ import os
13
+
14
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
15
+ if _p and _p not in sys.path:
16
+ sys.path.insert(0, _p)
17
+
18
+ import numpy as np
19
+ import pandas as pd
20
+ from catboost import CatBoostRegressor
21
+ from lightgbm import LGBMRegressor
22
+ from sklearn.metrics import mean_absolute_error, mean_squared_error
23
+ from xgboost import XGBRegressor
24
+
25
+
26
+ def parse_args() -> argparse.Namespace:
27
+ parser = argparse.ArgumentParser()
28
+ parser.add_argument("--aqs-daily", type=Path, required=True)
29
+ parser.add_argument("--output-dir", type=Path, required=True)
30
+ parser.add_argument("--exceedance-threshold", type=float, default=35.0)
31
+ parser.add_argument("--alphaearth-prefix", type=str, default="alphaearth_")
32
+ parser.add_argument("--seed", type=int, default=7)
33
+ return parser.parse_args()
34
+
35
+
36
+ def assign_split(ts: pd.Timestamp) -> str:
37
+ year = int(ts.year)
38
+ if year <= 2023:
39
+ return "train"
40
+ if year == 2024:
41
+ return "val"
42
+ if year == 2025:
43
+ return "test"
44
+ return "other"
45
+
46
+
47
+ def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
48
+ return float(math.sqrt(mean_squared_error(y_true, y_pred)))
49
+
50
+
51
+ def pearson_corr(y_true: np.ndarray, y_pred: np.ndarray) -> float:
52
+ a = np.asarray(y_true, dtype=np.float64)
53
+ b = np.asarray(y_pred, dtype=np.float64)
54
+ if a.size < 2 or np.allclose(a, a[0]) or np.allclose(b, b[0]):
55
+ return 0.0
56
+ value = float(np.corrcoef(a, b)[0, 1])
57
+ return value if np.isfinite(value) else 0.0
58
+
59
+
60
+ def prf(y_true: np.ndarray, y_pred: np.ndarray, threshold: float) -> Dict[str, float]:
61
+ truth = np.asarray(y_true >= threshold)
62
+ pred = np.asarray(y_pred >= threshold)
63
+ tp = int(np.logical_and(pred, truth).sum())
64
+ fp = int(np.logical_and(pred, ~truth).sum())
65
+ fn = int(np.logical_and(~pred, truth).sum())
66
+ precision = float(tp / (tp + fp)) if (tp + fp) else 0.0
67
+ recall = float(tp / (tp + fn)) if (tp + fn) else 0.0
68
+ f1 = float((2.0 * precision * recall) / (precision + recall)) if (precision + recall) else 0.0
69
+ return {"precision": precision, "recall": recall, "f1": f1}
70
+
71
+
72
+ def evaluate_frame(frame: pd.DataFrame, pred_col: str, threshold: float) -> Dict[str, float]:
73
+ y_true = frame["pm25_mean"].to_numpy(dtype=np.float64)
74
+ y_pred = frame[pred_col].to_numpy(dtype=np.float64)
75
+ event = prf(y_true, y_pred, threshold)
76
+ bias = np.asarray(y_pred - y_true, dtype=np.float64)
77
+ denom = float(np.sum(y_true))
78
+ return {
79
+ "count": int(len(frame)),
80
+ "rmse": rmse(y_true, y_pred),
81
+ "mae": float(mean_absolute_error(y_true, y_pred)),
82
+ "mean_bias": float(np.mean(bias)),
83
+ "normalized_mean_bias": float(np.sum(bias) / denom) if abs(denom) > 1e-12 else 0.0,
84
+ "pearson_r": pearson_corr(y_true, y_pred),
85
+ "event_precision": event["precision"],
86
+ "event_recall": event["recall"],
87
+ "event_f1": event["f1"],
88
+ }
89
+
90
+
91
+ def tune_event_shift(val_frame: pd.DataFrame, pred_col: str, threshold: float) -> Dict[str, float]:
92
+ best = None
93
+ for delta in np.linspace(-5.0, 15.0, 161):
94
+ shifted = val_frame.copy()
95
+ shifted["_shifted_pred"] = shifted[pred_col] + float(delta)
96
+ metrics = evaluate_frame(shifted, "_shifted_pred", threshold)
97
+ score = (metrics["event_f1"], metrics["event_recall"], -abs(float(delta)))
98
+ if best is None or score > best["score"]:
99
+ best = {"delta": float(delta), "metrics": metrics, "score": score}
100
+ assert best is not None
101
+ return {"delta": best["delta"], "val_event_calibrated_metrics": best["metrics"]}
102
+
103
+
104
+ def build_features(df: pd.DataFrame) -> pd.DataFrame:
105
+ df = df.sort_values(["site_key", "date"]).reset_index(drop=True).copy()
106
+ df["doy"] = df["date"].dt.dayofyear.astype(np.int32)
107
+ df["month"] = df["date"].dt.month.astype(np.int32)
108
+ df["doy_sin"] = np.sin(2.0 * np.pi * df["doy"] / 366.0)
109
+ df["doy_cos"] = np.cos(2.0 * np.pi * df["doy"] / 366.0)
110
+ grp = df.groupby("site_key", sort=False)
111
+ for lag in [1, 2, 3, 7]:
112
+ df[f"lag{lag}_pm25"] = grp["pm25_mean"].shift(lag)
113
+ df["roll3_prev"] = grp["pm25_mean"].rolling(3, min_periods=1).mean().reset_index(level=0, drop=True).shift(1)
114
+ df["roll7_prev"] = grp["pm25_mean"].rolling(7, min_periods=1).mean().reset_index(level=0, drop=True).shift(1)
115
+ return df
116
+
117
+
118
+ def prepare_frames(df: pd.DataFrame, alphaearth_prefix: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, List[str]]:
119
+ df = build_features(df)
120
+ train = df[df["split"] == "train"].copy()
121
+ val = df[df["split"] == "val"].copy()
122
+ test = df[df["split"] == "test"].copy()
123
+
124
+ site_mean = train.groupby("site_key")["pm25_mean"].mean()
125
+ global_mean = float(train["pm25_mean"].mean())
126
+ for frame in [train, val, test]:
127
+ frame["site_climo"] = frame["site_key"].map(site_mean).fillna(global_mean)
128
+ for frame in [train, val, test]:
129
+ for col in ["lag1_pm25", "lag2_pm25", "lag3_pm25", "lag7_pm25", "roll3_prev", "roll7_prev"]:
130
+ frame[col] = pd.to_numeric(frame[col], errors="coerce").fillna(frame["site_climo"])
131
+
132
+ feature_cols = [
133
+ "latitude",
134
+ "longitude",
135
+ "obs_count",
136
+ "site_climo",
137
+ "lag1_pm25",
138
+ "lag2_pm25",
139
+ "lag3_pm25",
140
+ "lag7_pm25",
141
+ "roll3_prev",
142
+ "roll7_prev",
143
+ "doy_sin",
144
+ "doy_cos",
145
+ "month",
146
+ ]
147
+ alpha_cols = [
148
+ c for c in df.columns
149
+ if c.startswith(alphaearth_prefix) and pd.api.types.is_numeric_dtype(df[c])
150
+ ]
151
+ feature_cols.extend(sorted(alpha_cols))
152
+ medians = train[feature_cols].median(numeric_only=True).fillna(0.0)
153
+ for frame in [train, val, test]:
154
+ frame.loc[:, feature_cols] = frame[feature_cols].fillna(medians)
155
+ frame.loc[:, feature_cols] = frame[feature_cols].fillna(0.0)
156
+ return train, val, test, feature_cols
157
+
158
+
159
+ def main() -> None:
160
+ args = parse_args()
161
+ df = pd.read_csv(args.aqs_daily, compression="infer", low_memory=False)
162
+ df["date"] = pd.to_datetime(df["date_gmt"], errors="coerce")
163
+ df["pm25_mean"] = pd.to_numeric(df["pm25_mean"], errors="coerce")
164
+ df["pm25_max"] = pd.to_numeric(df["pm25_max"], errors="coerce")
165
+ df["obs_count"] = pd.to_numeric(df["obs_count"], errors="coerce")
166
+ df = df.dropna(subset=["date", "site_key", "pm25_mean"]).copy()
167
+ df = (
168
+ df.groupby(["date", "site_key"], as_index=False)
169
+ .agg(
170
+ latitude=("Latitude", "first"),
171
+ longitude=("Longitude", "first"),
172
+ pm25_mean=("pm25_mean", "mean"),
173
+ pm25_max=("pm25_max", "max"),
174
+ obs_count=("obs_count", "sum"),
175
+ **{c: (c, "first") for c in df.columns if c.startswith(args.alphaearth_prefix)},
176
+ )
177
+ .sort_values(["site_key", "date"])
178
+ .reset_index(drop=True)
179
+ )
180
+ df["split"] = df["date"].map(assign_split)
181
+ df = df[df["split"].isin(["train", "val", "test"])].copy()
182
+
183
+ train, val, test, feature_cols = prepare_frames(df, alphaearth_prefix=args.alphaearth_prefix)
184
+ y_train = train["pm25_mean"].to_numpy(dtype=np.float64)
185
+
186
+ candidates = {
187
+ "xgboost": XGBRegressor(
188
+ n_estimators=300,
189
+ max_depth=8,
190
+ learning_rate=0.05,
191
+ subsample=0.8,
192
+ colsample_bytree=0.8,
193
+ objective="reg:squarederror",
194
+ tree_method="hist",
195
+ random_state=args.seed,
196
+ n_jobs=8,
197
+ ),
198
+ "lightgbm": LGBMRegressor(
199
+ n_estimators=300,
200
+ learning_rate=0.05,
201
+ num_leaves=127,
202
+ subsample=0.8,
203
+ colsample_bytree=0.8,
204
+ random_state=args.seed,
205
+ n_jobs=8,
206
+ verbose=-1,
207
+ ),
208
+ "catboost": CatBoostRegressor(
209
+ iterations=400,
210
+ depth=8,
211
+ learning_rate=0.05,
212
+ loss_function="RMSE",
213
+ eval_metric="RMSE",
214
+ random_seed=args.seed,
215
+ verbose=False,
216
+ ),
217
+ }
218
+
219
+ candidate_validation: List[Dict[str, object]] = []
220
+ best_name = None
221
+ best_model = None
222
+ best_score = None
223
+ for name, model in candidates.items():
224
+ if name == "catboost":
225
+ model.fit(train[feature_cols], y_train, use_best_model=False)
226
+ val_pred = model.predict(val[feature_cols])
227
+ else:
228
+ model.fit(train[feature_cols].to_numpy(dtype=np.float32), y_train)
229
+ val_pred = model.predict(val[feature_cols].to_numpy(dtype=np.float32))
230
+ val_frame = val.copy()
231
+ val_frame["pred"] = val_pred
232
+ metrics = evaluate_frame(val_frame, "pred", args.exceedance_threshold)
233
+ candidate_validation.append({"candidate": name, "val_metrics": metrics})
234
+ score = float(metrics["rmse"])
235
+ if best_score is None or score < best_score:
236
+ best_score = score
237
+ best_name = name
238
+ best_model = model
239
+
240
+ assert best_name is not None and best_model is not None
241
+ combined = pd.concat([train, val], ignore_index=True)
242
+ if best_name == "catboost":
243
+ best_model.fit(combined[feature_cols], combined["pm25_mean"].to_numpy(dtype=np.float64), use_best_model=False)
244
+ train_pred = best_model.predict(train[feature_cols])
245
+ val_pred = best_model.predict(val[feature_cols])
246
+ test_pred = best_model.predict(test[feature_cols])
247
+ else:
248
+ best_model.fit(combined[feature_cols].to_numpy(dtype=np.float32), combined["pm25_mean"].to_numpy(dtype=np.float64))
249
+ train_pred = best_model.predict(train[feature_cols].to_numpy(dtype=np.float32))
250
+ val_pred = best_model.predict(val[feature_cols].to_numpy(dtype=np.float32))
251
+ test_pred = best_model.predict(test[feature_cols].to_numpy(dtype=np.float32))
252
+
253
+ args.output_dir.mkdir(parents=True, exist_ok=True)
254
+ pred_df = pd.concat(
255
+ [
256
+ train.assign(pred_pm25=train_pred),
257
+ val.assign(pred_pm25=val_pred),
258
+ test.assign(pred_pm25=test_pred),
259
+ ],
260
+ ignore_index=True,
261
+ )
262
+ pred_path = args.output_dir / "predictions.csv.gz"
263
+ pred_df.to_csv(pred_path, index=False, compression="gzip")
264
+
265
+ train_eval = train.assign(pred=train_pred)
266
+ val_eval = val.assign(pred=val_pred)
267
+ test_eval = test.assign(pred=test_pred)
268
+ event_shift = tune_event_shift(val_eval, "pred", args.exceedance_threshold)
269
+ delta = event_shift["delta"]
270
+ train_event_eval = train_eval.assign(pred_event_calibrated=train_eval["pred"] + delta)
271
+ val_event_eval = val_eval.assign(pred_event_calibrated=val_eval["pred"] + delta)
272
+ test_event_eval = test_eval.assign(pred_event_calibrated=test_eval["pred"] + delta)
273
+
274
+ summary = {
275
+ "task_id": "smoke_pm25_alphaearth",
276
+ "task_form": "station_daily_regression",
277
+ "aqs_daily": str(args.aqs_daily),
278
+ "output_dir": str(args.output_dir),
279
+ "seed": int(args.seed),
280
+ "feature_columns": feature_cols,
281
+ "alphaearth_feature_count": int(sum(c.startswith(args.alphaearth_prefix) for c in feature_cols)),
282
+ "split_sizes": {"train": int(len(train)), "val": int(len(val)), "test": int(len(test))},
283
+ "candidate_validation": candidate_validation,
284
+ "selected_model": best_name,
285
+ "train_metrics": evaluate_frame(train_eval, "pred", args.exceedance_threshold),
286
+ "val_metrics": evaluate_frame(val_eval, "pred", args.exceedance_threshold),
287
+ "test_metrics": evaluate_frame(test_eval, "pred", args.exceedance_threshold),
288
+ "event_calibration": {
289
+ "delta": float(delta),
290
+ "val_metrics": event_shift["val_event_calibrated_metrics"],
291
+ "test_metrics": evaluate_frame(test_event_eval, "pred_event_calibrated", args.exceedance_threshold),
292
+ },
293
+ "predictions_path": str(pred_path),
294
+ "selection_rule": "same smoke benchmark; choose task-specific regressor by validation RMSE, then calibrate exceedance on validation only",
295
+ "tmt_policy": {
296
+ "task": "smoke_pm25",
297
+ "metric": "continuous RMSE/MAE with thresholded exceedance PRF",
298
+ "tolerance": "secondary event policy only",
299
+ },
300
+ }
301
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
302
+ print(json.dumps(summary, indent=2))
303
+
304
+
305
+ if __name__ == "__main__":
306
+ main()
experiments/raw_reference/task_scripts/run_smoke_pm25_attached_fm_suite_seeded.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Dict, List
10
+
11
+
12
+ import os
13
+
14
+ for _p in os.environ.get("WILDFIRE_FM_EXTRA_PYTHONPATH", "").split(os.pathsep):
15
+ if _p and _p not in sys.path:
16
+ sys.path.insert(0, _p)
17
+
18
+ import numpy as np
19
+ import pandas as pd
20
+ from catboost import CatBoostRegressor
21
+ from lightgbm import LGBMRegressor
22
+ from sklearn.linear_model import ElasticNet, Ridge
23
+ from sklearn.metrics import mean_absolute_error, mean_squared_error
24
+ from xgboost import XGBRegressor
25
+
26
+
27
+ def parse_args() -> argparse.Namespace:
28
+ parser = argparse.ArgumentParser()
29
+ parser.add_argument("--attached-csv", type=Path, required=True)
30
+ parser.add_argument("--output-dir", type=Path, required=True)
31
+ parser.add_argument("--fm-prefix", type=str, required=True)
32
+ parser.add_argument("--fm-family", type=str, required=True)
33
+ parser.add_argument("--model-family", choices=("full", "lite"), default="lite")
34
+ parser.add_argument("--exceedance-threshold", type=float, default=35.0)
35
+ parser.add_argument("--seed", type=int, default=7)
36
+ return parser.parse_args()
37
+
38
+
39
+ def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
40
+ return float(math.sqrt(mean_squared_error(y_true, y_pred)))
41
+
42
+
43
+ def pearson_corr(y_true: np.ndarray, y_pred: np.ndarray) -> float:
44
+ a = np.asarray(y_true, dtype=np.float64)
45
+ b = np.asarray(y_pred, dtype=np.float64)
46
+ if a.size < 2 or np.allclose(a, a[0]) or np.allclose(b, b[0]):
47
+ return 0.0
48
+ value = float(np.corrcoef(a, b)[0, 1])
49
+ return value if np.isfinite(value) else 0.0
50
+
51
+
52
+ def prf(y_true: np.ndarray, y_pred: np.ndarray, threshold: float) -> Dict[str, float]:
53
+ truth = np.asarray(y_true >= threshold)
54
+ pred = np.asarray(y_pred >= threshold)
55
+ tp = int(np.logical_and(pred, truth).sum())
56
+ fp = int(np.logical_and(pred, ~truth).sum())
57
+ fn = int(np.logical_and(~pred, truth).sum())
58
+ precision = float(tp / (tp + fp)) if (tp + fp) else 0.0
59
+ recall = float(tp / (tp + fn)) if (tp + fn) else 0.0
60
+ f1 = float((2.0 * precision * recall) / (precision + recall)) if (precision + recall) else 0.0
61
+ return {"precision": precision, "recall": recall, "f1": f1}
62
+
63
+
64
+ def evaluate_frame(frame: pd.DataFrame, pred_col: str, threshold: float) -> Dict[str, float]:
65
+ y_true = frame["pm25_mean"].to_numpy(dtype=np.float64)
66
+ y_pred = frame[pred_col].to_numpy(dtype=np.float64)
67
+ event = prf(y_true, y_pred, threshold)
68
+ bias = np.asarray(y_pred - y_true, dtype=np.float64)
69
+ denom = float(np.sum(y_true))
70
+ return {
71
+ "count": int(len(frame)),
72
+ "rmse": rmse(y_true, y_pred),
73
+ "mae": float(mean_absolute_error(y_true, y_pred)),
74
+ "mean_bias": float(np.mean(bias)),
75
+ "normalized_mean_bias": float(np.sum(bias) / denom) if abs(denom) > 1e-12 else 0.0,
76
+ "pearson_r": pearson_corr(y_true, y_pred),
77
+ "event_precision": event["precision"],
78
+ "event_recall": event["recall"],
79
+ "event_f1": event["f1"],
80
+ }
81
+
82
+
83
+ def main() -> None:
84
+ args = parse_args()
85
+ df = pd.read_csv(args.attached_csv)
86
+ df["date"] = pd.to_datetime(df["date_gmt"], errors="coerce")
87
+ df["pm25_mean"] = pd.to_numeric(df["pm25_mean"], errors="coerce")
88
+ df = df.dropna(subset=["date", "pm25_mean"]).copy()
89
+
90
+ feature_cols = [c for c in df.columns if c.startswith(args.fm_prefix)]
91
+ feature_cols = [c for c in feature_cols if pd.api.types.is_numeric_dtype(df[c])]
92
+ if not feature_cols:
93
+ raise SystemExit(f"No numeric FM feature columns found with prefix {args.fm_prefix}")
94
+
95
+ split_map = {"2020": "train", "2021": "train", "2022": "train", "2024": "val", "2025": "test"}
96
+ df["split"] = df["date"].dt.year.astype(str).map(split_map)
97
+ df = df[df["split"].isin(["train", "val", "test"])].copy()
98
+ train = df[df["split"] == "train"].copy()
99
+ val = df[df["split"] == "val"].copy()
100
+ test = df[df["split"] == "test"].copy()
101
+ if len(train) == 0 or len(val) == 0 or len(test) == 0:
102
+ raise SystemExit("Attached FM smoke table is missing one of train/val/test.")
103
+
104
+ medians = train[feature_cols].median(numeric_only=True).fillna(0.0)
105
+ train.loc[:, feature_cols] = train[feature_cols].fillna(medians)
106
+ val.loc[:, feature_cols] = val[feature_cols].fillna(medians)
107
+ test.loc[:, feature_cols] = test[feature_cols].fillna(medians)
108
+ train.loc[:, feature_cols] = train[feature_cols].fillna(0.0)
109
+ val.loc[:, feature_cols] = val[feature_cols].fillna(0.0)
110
+ test.loc[:, feature_cols] = test[feature_cols].fillna(0.0)
111
+
112
+ y_train = train["pm25_mean"].to_numpy(dtype=np.float64)
113
+ candidates: Dict[str, object] = {
114
+ "ridge": Ridge(alpha=1.0, random_state=args.seed),
115
+ "enet": ElasticNet(alpha=0.01, l1_ratio=0.2, random_state=args.seed, max_iter=10000),
116
+ }
117
+ if args.model_family == "full":
118
+ candidates.update(
119
+ {
120
+ "xgboost": XGBRegressor(
121
+ n_estimators=300,
122
+ max_depth=8,
123
+ learning_rate=0.05,
124
+ subsample=0.8,
125
+ colsample_bytree=0.8,
126
+ objective="reg:squarederror",
127
+ tree_method="hist",
128
+ random_state=args.seed,
129
+ n_jobs=8,
130
+ ),
131
+ "lightgbm": LGBMRegressor(
132
+ n_estimators=300,
133
+ learning_rate=0.05,
134
+ num_leaves=127,
135
+ subsample=0.8,
136
+ colsample_bytree=0.8,
137
+ random_state=args.seed,
138
+ n_jobs=8,
139
+ verbose=-1,
140
+ ),
141
+ "catboost": CatBoostRegressor(
142
+ iterations=400,
143
+ depth=8,
144
+ learning_rate=0.05,
145
+ loss_function="RMSE",
146
+ eval_metric="RMSE",
147
+ random_seed=args.seed,
148
+ verbose=False,
149
+ ),
150
+ }
151
+ )
152
+
153
+ candidate_validation: List[Dict[str, object]] = []
154
+ best_name = None
155
+ best_model = None
156
+ best_score = None
157
+ for name, model in candidates.items():
158
+ if name == "catboost":
159
+ model.fit(train[feature_cols], y_train, use_best_model=False)
160
+ val_pred = model.predict(val[feature_cols])
161
+ else:
162
+ model.fit(train[feature_cols].to_numpy(dtype=np.float32), y_train)
163
+ val_pred = model.predict(val[feature_cols].to_numpy(dtype=np.float32))
164
+ val_frame = val.copy()
165
+ val_frame["pred"] = val_pred
166
+ metrics = evaluate_frame(val_frame, "pred", args.exceedance_threshold)
167
+ candidate_validation.append({"candidate": name, "val_metrics": metrics})
168
+ score = float(metrics["rmse"])
169
+ if best_score is None or score < best_score:
170
+ best_score = score
171
+ best_name = name
172
+ best_model = model
173
+
174
+ assert best_name is not None and best_model is not None
175
+ combined = pd.concat([train, val], ignore_index=True)
176
+ if best_name == "catboost":
177
+ best_model.fit(combined[feature_cols], combined["pm25_mean"].to_numpy(dtype=np.float64), use_best_model=False)
178
+ train_pred = best_model.predict(train[feature_cols])
179
+ val_pred = best_model.predict(val[feature_cols])
180
+ test_pred = best_model.predict(test[feature_cols])
181
+ else:
182
+ best_model.fit(combined[feature_cols].to_numpy(dtype=np.float32), combined["pm25_mean"].to_numpy(dtype=np.float64))
183
+ train_pred = best_model.predict(train[feature_cols].to_numpy(dtype=np.float32))
184
+ val_pred = best_model.predict(val[feature_cols].to_numpy(dtype=np.float32))
185
+ test_pred = best_model.predict(test[feature_cols].to_numpy(dtype=np.float32))
186
+
187
+ args.output_dir.mkdir(parents=True, exist_ok=True)
188
+ pred_df = pd.concat(
189
+ [
190
+ train.assign(pred_pm25=train_pred),
191
+ val.assign(pred_pm25=val_pred),
192
+ test.assign(pred_pm25=test_pred),
193
+ ],
194
+ ignore_index=True,
195
+ )
196
+ pred_path = args.output_dir / "predictions.csv.gz"
197
+ pred_df.to_csv(pred_path, index=False, compression="gzip")
198
+
199
+ train_eval = train.assign(pred=train_pred)
200
+ val_eval = val.assign(pred=val_pred)
201
+ test_eval = test.assign(pred=test_pred)
202
+ summary = {
203
+ "task_id": "smoke_pm25_named_fm",
204
+ "task_form": "station_daily_regression",
205
+ "attached_csv": str(args.attached_csv),
206
+ "output_dir": str(args.output_dir),
207
+ "seed": int(args.seed),
208
+ "feature_columns": feature_cols,
209
+ "split_sizes": {"train": int(len(train)), "val": int(len(val)), "test": int(len(test))},
210
+ "candidate_validation": candidate_validation,
211
+ "selected_model": best_name,
212
+ "train_metrics": evaluate_frame(train_eval, "pred", args.exceedance_threshold),
213
+ "val_metrics": evaluate_frame(val_eval, "pred", args.exceedance_threshold),
214
+ "test_metrics": evaluate_frame(test_eval, "pred", args.exceedance_threshold),
215
+ "predictions_path": str(pred_path),
216
+ "model_family": "lightweight_linear_task_heads" if args.model_family == "lite" else "popular_open_source_task_models",
217
+ "fm_family": args.fm_family,
218
+ "benchmark_protocol": "fm_lite_protocol" if args.model_family == "lite" else "standard_protocol",
219
+ "selection_rule": "choose model by validation RMSE on named-FM attached rows; report on held-out test dates",
220
+ "tmt_policy": {
221
+ "task": "smoke_pm25",
222
+ "metric": "continuous RMSE/MAE with thresholded exceedance PRF",
223
+ "tolerance": "secondary event policy only",
224
+ },
225
+ }
226
+ (args.output_dir / "summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
227
+ print(json.dumps(summary, indent=2))
228
+
229
+
230
+ if __name__ == "__main__":
231
+ main()
experiments/raw_reference/task_scripts/summarize_forced_meanstd_20260429.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import re
8
+ import statistics
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+
13
+ SLUG_LABELS = {
14
+ "reference": "Reference",
15
+ "prithvi_wxc": "Prithvi-WxC",
16
+ "stormcast": "StormCast",
17
+ "aurora": "Aurora",
18
+ "climax": "ClimaX",
19
+ "alphaearth": "AlphaEarth",
20
+ }
21
+
22
+
23
+ def load(path: Path) -> dict[str, Any]:
24
+ return json.loads(path.read_text(encoding="utf-8"))
25
+
26
+
27
+ def stats(values: list[float]) -> dict[str, float | int]:
28
+ values = [float(v) for v in values if math.isfinite(float(v))]
29
+ if not values:
30
+ return {"n": 0, "mean": math.nan, "std": math.nan}
31
+ return {
32
+ "n": len(values),
33
+ "mean": float(statistics.fmean(values)),
34
+ "std": float(statistics.stdev(values)) if len(values) > 1 else 0.0,
35
+ }
36
+
37
+
38
+ def seed_from_path(path: Path) -> int | None:
39
+ match = re.search(r"_seed_(\d+)", str(path))
40
+ return int(match.group(1)) if match else None
41
+
42
+
43
+ def label_from_seed_dir(path: Path, prefix: str) -> str:
44
+ for part in path.parts:
45
+ if part.startswith(prefix) and "_seed_" in part:
46
+ slug = part[len(prefix) :].split("_seed_", 1)[0]
47
+ return SLUG_LABELS.get(slug, slug)
48
+ return "unknown"
49
+
50
+
51
+ def dedupe_rows(rows: list[dict[str, Any]], keys: tuple[str, ...]) -> list[dict[str, Any]]:
52
+ selected: dict[tuple[Any, ...], dict[str, Any]] = {}
53
+ for row in rows:
54
+ key = tuple(row.get(name) for name in keys)
55
+ old = selected.get(key)
56
+ if old is None:
57
+ selected[key] = row
58
+ continue
59
+ old_mtime = Path(str(old["path"])).stat().st_mtime
60
+ new_mtime = Path(str(row["path"])).stat().st_mtime
61
+ if new_mtime >= old_mtime:
62
+ selected[key] = row
63
+ return list(selected.values())
64
+
65
+
66
+ def best_val_threshold(data: dict[str, Any]) -> str:
67
+ entries = data["splits"]["val"]["threshold_metrics"]
68
+ return max(entries, key=lambda key: (float(entries[key]["f1"]), -float(entries[key]["threshold"])))
69
+
70
+
71
+ def collect_occupancy(run_root: Path) -> dict[str, Any]:
72
+ rows: list[dict[str, Any]] = []
73
+ for path in sorted(run_root.glob("table3_occupancy_*_seed_*/run_*/summary.json")):
74
+ data = load(path)
75
+ threshold_key = best_val_threshold(data)
76
+ test = data["splits"]["test"]
77
+ rows.append(
78
+ {
79
+ "label": data.get("fm_family") or label_from_seed_dir(path, "table3_occupancy_"),
80
+ "seed": seed_from_path(path),
81
+ "strict_f1": float(test["threshold_metrics"][threshold_key]["f1"]),
82
+ "tolerant_f1": float(test["tolerant_threshold_metrics"]["t0_s3"][threshold_key]["f1"]),
83
+ "union_f1": float(test["tolerant_threshold_metrics"]["t3_s3"][threshold_key]["f1"]),
84
+ "path": str(path),
85
+ }
86
+ )
87
+ return group(rows, ["strict_f1", "tolerant_f1", "union_f1"])
88
+
89
+
90
+ def collect_headcontrol(run_root: Path) -> dict[str, Any]:
91
+ rows: list[dict[str, Any]] = []
92
+ for path in sorted(run_root.glob("table2_prithvi_wxc_headcontrol_seed_*/run_*/summary.json")):
93
+ data = load(path)
94
+ seed = seed_from_path(path)
95
+ for row in data.get("selection_summary", {}).get("rows", []):
96
+ rows.append(
97
+ {
98
+ "label": "Prithvi-WxC",
99
+ "scope": row["scope"],
100
+ "seed": seed,
101
+ "ranking_selected_union_f1": float(row["ranking_selected_union_f1"]),
102
+ "decision_selected_union_f1": float(row["decision_selected_union_f1"]),
103
+ "decision_regret_union_f1": float(row["decision_regret_union_f1"]),
104
+ "selection_failure": bool(row.get("selection_failure", False)),
105
+ "path": str(path),
106
+ }
107
+ )
108
+ grouped: dict[str, Any] = {}
109
+ rows = dedupe_rows(rows, ("label", "scope", "seed"))
110
+ for scope in sorted({str(row["scope"]) for row in rows}):
111
+ selected = [row for row in rows if row["scope"] == scope]
112
+ grouped[scope] = {
113
+ "n": len(selected),
114
+ "failure_count": int(sum(1 for row in selected if row["selection_failure"])),
115
+ "ranking_selected_union_f1": stats([row["ranking_selected_union_f1"] for row in selected]),
116
+ "decision_selected_union_f1": stats([row["decision_selected_union_f1"] for row in selected]),
117
+ "decision_regret_union_f1": stats([row["decision_regret_union_f1"] for row in selected]),
118
+ }
119
+ return {"rows": rows, "summary": grouped}
120
+
121
+
122
+ def collect_spread(run_root: Path) -> dict[str, Any]:
123
+ rows: list[dict[str, Any]] = []
124
+ for pattern, prefix in [
125
+ ("table3_spread_*_seed_*/run_*/summary.json", "table3_spread_"),
126
+ ("table3_reference_spread_seed_*/run_*/summary.json", "table3_reference_spread_"),
127
+ ]:
128
+ for path in sorted(run_root.glob(pattern)):
129
+ data = load(path)
130
+ headline = data["headline_metrics"]
131
+ label = data.get("fm_family") or ("Reference" if "reference_spread" in str(path) else label_from_seed_dir(path, prefix))
132
+ rows.append(
133
+ {
134
+ "label": label,
135
+ "seed": seed_from_path(path),
136
+ "strict_f1": float(headline["strict_f1"]),
137
+ "spatial_f1": float(headline["same_sample_spatial_tolerance_f1"]["s4"]),
138
+ "ap": float(headline["strict_AP"]),
139
+ "path": str(path),
140
+ }
141
+ )
142
+ return group(rows, ["strict_f1", "spatial_f1", "ap"])
143
+
144
+
145
+ def collect_task(run_root: Path, glob_pattern: str, prefix: str, metrics_path: list[str], metric_keys: list[str]) -> dict[str, Any]:
146
+ rows: list[dict[str, Any]] = []
147
+ for path in sorted(run_root.glob(glob_pattern)):
148
+ data = load(path)
149
+ label = data.get("fm_family") or label_from_seed_dir(path, prefix)
150
+ node: Any = data
151
+ for key in metrics_path:
152
+ node = node[key]
153
+ row = {"label": label, "seed": seed_from_path(path), "path": str(path)}
154
+ for key in metric_keys:
155
+ row[key] = float(node[key])
156
+ rows.append(row)
157
+ return group(rows, metric_keys)
158
+
159
+
160
+ def group(rows: list[dict[str, Any]], metric_keys: list[str]) -> dict[str, Any]:
161
+ if rows and "seed" in rows[0]:
162
+ rows = dedupe_rows(rows, ("label", "seed"))
163
+ summary: dict[str, Any] = {}
164
+ for label in sorted({str(row["label"]) for row in rows}):
165
+ selected = [row for row in rows if row["label"] == label]
166
+ summary[label] = {"n": len(selected)}
167
+ for key in metric_keys:
168
+ summary[label][key] = stats([row[key] for row in selected])
169
+ return {"rows": rows, "summary": summary}
170
+
171
+
172
+ def fmt(value: dict[str, Any], scale: float = 1.0, digits: int = 2) -> str:
173
+ if int(value["n"]) == 0:
174
+ return "missing"
175
+ return f"{float(value['mean']) * scale:.{digits}f} +/- {float(value['std']) * scale:.{digits}f} (n={int(value['n'])})"
176
+
177
+
178
+ def write_markdown(out: Path, summary: dict[str, Any]) -> None:
179
+ lines = ["# Forced Mean/Std Gap-Fill Summary", ""]
180
+ for section in [
181
+ "table2_headcontrol",
182
+ "table3_occupancy",
183
+ "table3_spread",
184
+ "table4_final_area",
185
+ "table4_analog",
186
+ "table4_smoke",
187
+ "table4_heat",
188
+ ]:
189
+ lines += [f"## {section}", ""]
190
+ sec = summary.get(section, {}).get("summary", {})
191
+ if section == "table2_headcontrol":
192
+ for scope, row in sec.items():
193
+ lines.append(
194
+ f"- {scope}: regret {fmt(row['decision_regret_union_f1'], 100.0)}; "
195
+ f"ranking union {fmt(row['ranking_selected_union_f1'], 100.0)}; "
196
+ f"decision union {fmt(row['decision_selected_union_f1'], 100.0)}; "
197
+ f"failures {row['failure_count']}/{row['n']}"
198
+ )
199
+ else:
200
+ for label, row in sec.items():
201
+ pieces = [f"{key} {fmt(val, 100.0 if key.endswith('_f1') or key == 'ap' else 1.0)}" for key, val in row.items() if isinstance(val, dict)]
202
+ lines.append(f"- {label}: " + "; ".join(pieces))
203
+ lines.append("")
204
+ out.write_text("\n".join(lines), encoding="utf-8")
205
+
206
+
207
+ def main() -> None:
208
+ parser = argparse.ArgumentParser()
209
+ parser.add_argument("--run-root", type=Path, default=Path("${RUN_ROOT}"))
210
+ parser.add_argument("--out-json", type=Path, default=Path("${OUT_JSON}"))
211
+ parser.add_argument("--out-md", type=Path, default=Path("${OUT_MD}"))
212
+ args = parser.parse_args()
213
+
214
+ summary = {
215
+ "run_root": str(args.run_root),
216
+ "table2_headcontrol": collect_headcontrol(args.run_root),
217
+ "table3_occupancy": collect_occupancy(args.run_root),
218
+ "table3_spread": collect_spread(args.run_root),
219
+ "table4_final_area": collect_task(args.run_root, "table4_final_area_*_seed_*/run_*/summary.json", "table4_final_area_", ["headline_metrics"], ["log_rmse", "log_mae", "log_spearman"]),
220
+ "table4_analog": collect_task(args.run_root, "table4_analog_*_seed_*/run_*/summary.json", "table4_analog_", ["test_metrics"], ["ndcg_at_10", "log_rmse", "log_mae"]),
221
+ "table4_smoke": collect_task(args.run_root, "table4_smoke_*_seed_*/run_*/summary.json", "table4_smoke_", ["test_metrics"], ["rmse", "mae", "pearson_r"]),
222
+ "table4_heat": collect_task(args.run_root, "table4_heat_*_seed_*/run_*/summary.json", "table4_heat_", ["test_metrics"], ["rmse_c", "mae_c", "pearson_r"]),
223
+ }
224
+ args.out_json.parent.mkdir(parents=True, exist_ok=True)
225
+ args.out_json.write_text(json.dumps(summary, indent=2), encoding="utf-8")
226
+ write_markdown(args.out_md, summary)
227
+ print(f"wrote={args.out_json}")
228
+ print(f"wrote={args.out_md}")
229
+
230
+
231
+ if __name__ == "__main__":
232
+ main()
experiments/slurm/submit_template.sbatch ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #SBATCH --job-name=wildfire-contract-rerun
3
+ #SBATCH --cpus-per-task=4
4
+ #SBATCH --mem=24G
5
+ #SBATCH --time=02:00:00
6
+
7
+ # Template only. Set these paths for your environment after obtaining data.
8
+ PROJECT_ROOT=/path/to/this/repository
9
+ DATA_ROOT=/path/to/raw/or/processed/data
10
+ OUTPUT_ROOT=/path/to/output
11
+
12
+ cd "$PROJECT_ROOT"
13
+ python3 scripts/reproduce_paper_outputs.py
paper_outputs/figures/fig_fireprone_contract_progression_compact.pdf ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %PDF-1.4
2
+ %����
3
+ 1 0 obj
4
+ << /Type /Catalog /Pages 2 0 R >>
5
+ endobj
6
+ 2 0 obj
7
+ << /Type /Pages /Kids [3 0 R] /Count 1 >>
8
+ endobj
9
+ 3 0 obj
10
+ << /Type /Page /Parent 2 0 R /MediaBox [0 0 1320 470] /Resources << /Font << /F1 4 0 R /F2 5 0 R >> >> /Contents 6 0 R >>
11
+ endobj
12
+ 4 0 obj
13
+ << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
14
+ endobj
15
+ 5 0 obj
16
+ << /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>
17
+ endobj
18
+ 6 0 obj
19
+ << /Length 19373 >>
20
+ stream
21
+ 1.0000 1.0000 1.0000 rg 0.00 0.00 1320.00 470.00 re f
22
+ 0.80 w 0.1500 0.1500 0.1500 RG 72.00 132.00 m 72.00 400.00 l S
23
+ 0.80 w 0.1500 0.1500 0.1500 RG 72.00 132.00 m 1266.00 132.00 l S
24
+ 0.45 w 0.8600 0.8600 0.8600 RG 68.00 132.00 m 1266.00 132.00 l S
25
+ BT /F1 7.00 Tf 0.2500 0.2500 0.2500 rg 1 0 0 1 60.36 129.00 Tm (0) Tj ET
26
+ 0.45 w 0.8600 0.8600 0.8600 RG 68.00 199.00 m 1266.00 199.00 l S
27
+ BT /F1 7.00 Tf 0.2500 0.2500 0.2500 rg 1 0 0 1 56.72 196.00 Tm (20) Tj ET
28
+ 0.45 w 0.8600 0.8600 0.8600 RG 68.00 266.00 m 1266.00 266.00 l S
29
+ BT /F1 7.00 Tf 0.2500 0.2500 0.2500 rg 1 0 0 1 56.72 263.00 Tm (40) Tj ET
30
+ 0.45 w 0.8600 0.8600 0.8600 RG 68.00 333.00 m 1266.00 333.00 l S
31
+ BT /F1 7.00 Tf 0.2500 0.2500 0.2500 rg 1 0 0 1 56.72 330.00 Tm (60) Tj ET
32
+ 0.45 w 0.8600 0.8600 0.8600 RG 68.00 400.00 m 1266.00 400.00 l S
33
+ BT /F1 7.00 Tf 0.2500 0.2500 0.2500 rg 1 0 0 1 56.72 397.00 Tm (80) Tj ET
34
+ BT /F2 8.00 Tf 0.1500 0.1500 0.1500 rg 1 0 0 1 34.00 408.00 Tm (F1 \(%\)) Tj ET
35
+ BT /F2 15.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 202.93 417.00 Tm (global) Tj ET
36
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 126.00 m 380.50 133.00 l S
37
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 138.00 m 380.50 145.00 l S
38
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 150.00 m 380.50 157.00 l S
39
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 162.00 m 380.50 169.00 l S
40
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 174.00 m 380.50 181.00 l S
41
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 186.00 m 380.50 193.00 l S
42
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 198.00 m 380.50 205.00 l S
43
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 210.00 m 380.50 217.00 l S
44
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 222.00 m 380.50 229.00 l S
45
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 234.00 m 380.50 241.00 l S
46
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 246.00 m 380.50 253.00 l S
47
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 258.00 m 380.50 265.00 l S
48
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 270.00 m 380.50 277.00 l S
49
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 282.00 m 380.50 289.00 l S
50
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 294.00 m 380.50 301.00 l S
51
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 306.00 m 380.50 313.00 l S
52
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 318.00 m 380.50 325.00 l S
53
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 330.00 m 380.50 337.00 l S
54
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 342.00 m 380.50 349.00 l S
55
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 354.00 m 380.50 361.00 l S
56
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 366.00 m 380.50 373.00 l S
57
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 378.00 m 380.50 385.00 l S
58
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 390.00 m 380.50 397.00 l S
59
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 402.00 m 380.50 409.00 l S
60
+ 0.75 w 0.4200 0.4400 0.4600 RG 380.50 414.00 m 380.50 416.00 l S
61
+ BT /F2 15.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 515.07 417.00 Tm (top 5%) Tj ET
62
+ BT /F2 15.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 807.48 417.00 Tm (top 10%) Tj ET
63
+ BT /F2 15.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 1103.97 417.00 Tm (top 20%) Tj ET
64
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 84.02 132.00 18.00 1.52 re B
65
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 84.02 133.52 18.00 98.13 re B
66
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 84.02 231.66 18.00 98.21 re B
67
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 76.52 69.51 Tm (Ref.) Tj ET
68
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 110.07 132.00 18.00 0.19 re B
69
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 110.07 132.19 18.00 23.82 re B
70
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 110.07 156.00 18.00 43.62 re B
71
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 102.21 69.86 Tm (WxC) Tj ET
72
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 136.11 132.00 18.00 0.22 re B
73
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 136.11 132.22 18.00 28.26 re B
74
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 136.11 160.48 18.00 48.92 re B
75
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 119.34 78.77 Tm (Aurora) Tj ET
76
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 162.16 132.00 18.00 1.17 re B
77
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 162.16 133.17 18.00 98.51 re B
78
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 162.16 231.67 18.00 101.83 re B
79
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 146.38 77.78 Tm (ClimaX) Tj ET
80
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 188.20 132.00 18.00 0.21 re B
81
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 188.20 132.21 18.00 27.24 re B
82
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 188.20 159.45 18.00 47.52 re B
83
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 172.99 77.21 Tm (Storm) Tj ET
84
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 214.25 132.00 18.00 0.57 re B
85
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 214.25 132.57 18.00 49.40 re B
86
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 214.25 181.96 18.00 44.47 re B
87
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 201.30 74.95 Tm (DLWP) Tj ET
88
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 240.30 132.00 18.00 0.95 re B
89
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 240.30 132.95 18.00 64.40 re B
90
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 240.30 197.35 18.00 68.86 re B
91
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 233.14 69.15 Tm (FCN) Tj ET
92
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 266.34 132.00 18.00 0.88 re B
93
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 266.34 132.88 18.00 39.34 re B
94
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 266.34 172.22 18.00 40.53 re B
95
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 247.45 80.89 Tm (FengWu) Tj ET
96
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 292.39 132.00 18.00 1.26 re B
97
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 292.39 133.26 18.00 69.19 re B
98
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 292.39 202.46 18.00 54.46 re B
99
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 284.17 70.21 Tm (FuXi) Tj ET
100
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 318.43 132.00 18.00 0.92 re B
101
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 318.43 132.92 18.00 56.33 re B
102
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 318.43 189.25 18.00 62.13 re B
103
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 295.86 84.57 Tm (Pangu-W) Tj ET
104
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 344.48 132.00 18.00 6.90 re B
105
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 344.48 138.90 18.00 91.75 re B
106
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 344.48 230.65 18.00 26.74 re B
107
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 333.29 73.18 Tm (Alpha) Tj ET
108
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 398.52 132.00 18.00 11.93 re B
109
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 398.52 143.93 18.00 119.60 re B
110
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 398.52 263.53 18.00 112.45 re B
111
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 391.02 69.51 Tm (Ref.) Tj ET
112
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 424.57 132.00 18.00 4.73 re B
113
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 424.57 136.73 18.00 59.80 re B
114
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 424.57 196.53 18.00 78.11 re B
115
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 416.71 69.86 Tm (WxC) Tj ET
116
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 450.61 132.00 18.00 3.30 re B
117
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 450.61 135.30 18.00 47.40 re B
118
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 450.61 182.70 18.00 68.17 re B
119
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 433.84 78.77 Tm (Aurora) Tj ET
120
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 476.66 132.00 18.00 4.33 re B
121
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 476.66 136.33 18.00 111.51 re B
122
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 476.66 247.84 18.00 116.04 re B
123
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 460.88 77.78 Tm (ClimaX) Tj ET
124
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 502.70 132.00 18.00 3.21 re B
125
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 502.70 135.21 18.00 48.12 re B
126
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 502.70 183.33 18.00 69.89 re B
127
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 487.49 77.21 Tm (Storm) Tj ET
128
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 528.75 132.00 18.00 6.05 re B
129
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 528.75 138.05 18.00 100.22 re B
130
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 528.75 238.27 18.00 79.52 re B
131
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 515.80 74.95 Tm (DLWP) Tj ET
132
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 554.80 132.00 18.00 5.44 re B
133
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 554.80 137.44 18.00 92.98 re B
134
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 554.80 230.41 18.00 83.50 re B
135
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 547.64 69.15 Tm (FCN) Tj ET
136
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 580.84 132.00 18.00 5.26 re B
137
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 580.84 137.26 18.00 49.27 re B
138
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 580.84 186.53 18.00 46.33 re B
139
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 561.95 80.89 Tm (FengWu) Tj ET
140
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 606.89 132.00 18.00 6.80 re B
141
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 606.89 138.80 18.00 100.04 re B
142
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 606.89 238.85 18.00 73.82 re B
143
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 598.67 70.21 Tm (FuXi) Tj ET
144
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 632.93 132.00 18.00 4.57 re B
145
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 632.93 136.57 18.00 69.87 re B
146
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 632.93 206.44 18.00 71.02 re B
147
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 610.36 84.57 Tm (Pangu-W) Tj ET
148
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 658.98 132.00 18.00 23.16 re B
149
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 658.98 155.16 18.00 120.48 re B
150
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 658.98 275.64 18.00 29.70 re B
151
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 647.79 73.18 Tm (Alpha) Tj ET
152
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 695.02 132.00 18.00 11.92 re B
153
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 695.02 143.92 18.00 119.29 re B
154
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 695.02 263.21 18.00 111.74 re B
155
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 687.52 69.51 Tm (Ref.) Tj ET
156
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 721.07 132.00 18.00 4.15 re B
157
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 721.07 136.15 18.00 45.70 re B
158
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 721.07 181.84 18.00 59.67 re B
159
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 713.21 69.86 Tm (WxC) Tj ET
160
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 747.11 132.00 18.00 2.61 re B
161
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 747.11 134.61 18.00 40.06 re B
162
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 747.11 174.67 18.00 59.60 re B
163
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 730.34 78.77 Tm (Aurora) Tj ET
164
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 773.16 132.00 18.00 4.19 re B
165
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 773.16 136.19 18.00 110.82 re B
166
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 773.16 247.02 18.00 114.69 re B
167
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 757.38 77.78 Tm (ClimaX) Tj ET
168
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 799.20 132.00 18.00 2.44 re B
169
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 799.20 134.44 18.00 39.99 re B
170
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 799.20 174.43 18.00 59.66 re B
171
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 783.99 77.21 Tm (Storm) Tj ET
172
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 825.25 132.00 18.00 5.40 re B
173
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 825.25 137.40 18.00 87.26 re B
174
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 825.25 224.65 18.00 65.22 re B
175
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 812.30 74.95 Tm (DLWP) Tj ET
176
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 851.30 132.00 18.00 3.95 re B
177
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 851.30 135.95 18.00 71.17 re B
178
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 851.30 207.11 18.00 70.45 re B
179
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 844.14 69.15 Tm (FCN) Tj ET
180
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 877.34 132.00 18.00 4.16 re B
181
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 877.34 136.16 18.00 39.22 re B
182
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 877.34 175.38 18.00 37.64 re B
183
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 858.45 80.89 Tm (FengWu) Tj ET
184
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 903.39 132.00 18.00 5.54 re B
185
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 903.39 137.54 18.00 74.90 re B
186
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 903.39 212.44 18.00 54.27 re B
187
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 895.17 70.21 Tm (FuXi) Tj ET
188
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 929.43 132.00 18.00 3.66 re B
189
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 929.43 135.66 18.00 59.77 re B
190
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 929.43 195.43 18.00 65.66 re B
191
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 906.86 84.57 Tm (Pangu-W) Tj ET
192
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 955.48 132.00 18.00 22.23 re B
193
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 955.48 154.23 18.00 118.13 re B
194
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 955.48 272.36 18.00 29.05 re B
195
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 944.29 73.18 Tm (Alpha) Tj ET
196
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 991.52 132.00 18.00 11.83 re B
197
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 991.52 143.83 18.00 116.43 re B
198
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 991.52 260.25 18.00 105.32 re B
199
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 984.02 69.51 Tm (Ref.) Tj ET
200
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1017.57 132.00 18.00 3.86 re B
201
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1017.57 135.86 18.00 40.20 re B
202
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1017.57 176.06 18.00 50.19 re B
203
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1009.71 69.86 Tm (WxC) Tj ET
204
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1043.61 132.00 18.00 2.23 re B
205
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1043.61 134.23 18.00 33.05 re B
206
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1043.61 167.28 18.00 48.29 re B
207
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1026.84 78.77 Tm (Aurora) Tj ET
208
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1069.66 132.00 18.00 3.45 re B
209
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1069.66 135.45 18.00 97.77 re B
210
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1069.66 233.22 18.00 100.00 re B
211
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1053.88 77.78 Tm (ClimaX) Tj ET
212
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1095.70 132.00 18.00 1.94 re B
213
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1095.70 133.94 18.00 32.95 re B
214
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1095.70 166.89 18.00 47.72 re B
215
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1080.49 77.21 Tm (Storm) Tj ET
216
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1121.75 132.00 18.00 5.11 re B
217
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1121.75 137.11 18.00 65.04 re B
218
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1121.75 202.15 18.00 46.87 re B
219
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1108.80 74.95 Tm (DLWP) Tj ET
220
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1147.80 132.00 18.00 3.34 re B
221
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1147.80 135.34 18.00 53.54 re B
222
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1147.80 188.88 18.00 57.31 re B
223
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1140.64 69.15 Tm (FCN) Tj ET
224
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1173.84 132.00 18.00 3.75 re B
225
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1173.84 135.75 18.00 36.29 re B
226
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1173.84 172.04 18.00 36.30 re B
227
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1154.95 80.89 Tm (FengWu) Tj ET
228
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1199.89 132.00 18.00 4.57 re B
229
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1199.89 136.57 18.00 68.98 re B
230
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1199.89 205.55 18.00 49.50 re B
231
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1191.67 70.21 Tm (FuXi) Tj ET
232
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1225.93 132.00 18.00 2.96 re B
233
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1225.93 134.96 18.00 54.04 re B
234
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1225.93 189.01 18.00 58.80 re B
235
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1203.36 84.57 Tm (Pangu-W) Tj ET
236
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 1251.98 132.00 18.00 20.74 re B
237
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 1251.98 152.74 18.00 109.35 re B
238
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 1251.98 262.09 18.00 25.30 re B
239
+ BT /F1 10.00 Tf 0.0000 0.0000 0.0000 rg 0.70711 -0.70711 0.70711 0.70711 1240.79 73.18 Tm (Alpha) Tj ET
240
+ 0.45 w 0.9800 0.9800 0.9600 rg 0.7800 0.8000 0.7800 RG 77.00 362.00 304.00 23.00 re B
241
+ 0.35 w 0.0900 0.2200 0.3700 rg 1.0000 1.0000 1.0000 RG 90.00 371.00 24.00 9.00 re B
242
+ BT /F1 8.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 121.00 373.00 Tm (Strict) Tj ET
243
+ 0.35 w 0.3100 0.5500 0.8000 rg 1.0000 1.0000 1.0000 RG 188.00 371.00 24.00 9.00 re B
244
+ BT /F1 8.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 219.00 373.00 Tm (Tolerance) Tj ET
245
+ 0.35 w 0.7500 0.8400 0.9400 rg 1.0000 1.0000 1.0000 RG 286.00 371.00 24.00 9.00 re B
246
+ BT /F1 8.00 Tf 0.0000 0.0000 0.0000 rg 1 0 0 1 317.00 373.00 Tm (Union) Tj ET
247
+ endstream
248
+ endobj
249
+ xref
250
+ 0 7
251
+ 0000000000 65535 f
252
+ 0000000015 00000 n
253
+ 0000000064 00000 n
254
+ 0000000121 00000 n
255
+ 0000000258 00000 n
256
+ 0000000328 00000 n
257
+ 0000000403 00000 n
258
+ trailer
259
+ << /Size 7 /Root 1 0 R >>
260
+ startxref
261
+ 19829
262
+ %%EOF
paper_outputs/figures/fig_selection_regret_rq2.tikz ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ % Auto-generated by scripts/build_selection_regret_rq2_figure.py.
2
+ \begin{tikzpicture}[x=1cm,y=1cm]
3
+ \footnotesize
4
+ \draw[black!12, line width=0.35pt] (2.450,-0.350) -- (2.450,4.530);
5
+ \node[anchor=north, font=\scriptsize, text=black!70] at (2.450,-0.410) {-20};
6
+ \draw[black!12, line width=0.35pt] (3.243,-0.350) -- (3.243,4.530);
7
+ \node[anchor=north, font=\scriptsize, text=black!70] at (3.243,-0.410) {-10};
8
+ \draw[wfgray, line width=0.55pt] (4.036,-0.350) -- (4.036,4.530);
9
+ \node[anchor=north, font=\scriptsize, text=black!70] at (4.036,-0.410) {0};
10
+ \draw[black!12, line width=0.35pt] (4.829,-0.350) -- (4.829,4.530);
11
+ \node[anchor=north, font=\scriptsize, text=black!70] at (4.829,-0.410) {10};
12
+ \draw[black!12, line width=0.35pt] (5.621,-0.350) -- (5.621,4.530);
13
+ \node[anchor=north, font=\scriptsize, text=black!70] at (5.621,-0.410) {20};
14
+ \draw[black!12, line width=0.35pt] (6.414,-0.350) -- (6.414,4.530);
15
+ \node[anchor=north, font=\scriptsize, text=black!70] at (6.414,-0.410) {30};
16
+ \draw[black!12, line width=0.35pt] (7.207,-0.350) -- (7.207,4.530);
17
+ \node[anchor=north, font=\scriptsize, text=black!70] at (7.207,-0.410) {40};
18
+ \draw[black!12, line width=0.35pt] (8.000,-0.350) -- (8.000,4.530);
19
+ \node[anchor=north, font=\scriptsize, text=black!70] at (8.000,-0.410) {50};
20
+ \draw[black!45, line width=0.4pt] (2.450,-0.350) -- (8.000,-0.350);
21
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,4.350) {\textcolor{wfblue}{\textbf{FireWx-FM ref.}}};
22
+ \draw[wfslate, line width=0.72pt] (4.030,4.220) -- (5.212,4.220);
23
+ \draw[wfslate, line width=0.72pt] (4.030,4.185) -- (4.030,4.255);
24
+ \draw[wfslate, line width=0.72pt] (5.212,4.185) -- (5.212,4.255);
25
+ \filldraw[wfslate] (4.621,4.220) circle[radius=0.045];
26
+ \draw[wforange, line width=0.72pt] (4.051,4.480) -- (4.487,4.480);
27
+ \draw[wforange, line width=0.72pt] (4.051,4.445) -- (4.051,4.515);
28
+ \draw[wforange, line width=0.72pt] (4.487,4.445) -- (4.487,4.515);
29
+ \filldraw[wforange] (4.224,4.435) rectangle (4.314,4.525);
30
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,3.940) {Prithvi-WxC};
31
+ \draw[wfslate, line width=0.72pt] (4.036,3.810) -- (4.036,3.810);
32
+ \draw[wfslate, line width=0.72pt] (4.036,3.775) -- (4.036,3.845);
33
+ \draw[wfslate, line width=0.72pt] (4.036,3.775) -- (4.036,3.845);
34
+ \filldraw[wfslate] (4.036,3.810) circle[radius=0.045];
35
+ \draw[wforange, line width=0.72pt] (4.036,4.070) -- (4.036,4.070);
36
+ \draw[wforange, line width=0.72pt] (4.036,4.035) -- (4.036,4.105);
37
+ \draw[wforange, line width=0.72pt] (4.036,4.035) -- (4.036,4.105);
38
+ \filldraw[wforange] (3.991,4.025) rectangle (4.081,4.115);
39
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,3.530) {Aurora};
40
+ \draw[wfslate, line width=0.72pt] (3.580,3.400) -- (5.276,3.400);
41
+ \draw[wfslate, line width=0.72pt] (3.580,3.365) -- (3.580,3.435);
42
+ \draw[wfslate, line width=0.72pt] (5.276,3.365) -- (5.276,3.435);
43
+ \filldraw[wfslate] (4.428,3.400) circle[radius=0.045];
44
+ \draw[wforange, line width=0.72pt] (2.627,3.660) -- (7.723,3.660);
45
+ \draw[wforange, line width=0.72pt] (2.627,3.625) -- (2.627,3.695);
46
+ \draw[wforange, line width=0.72pt] (7.723,3.625) -- (7.723,3.695);
47
+ \filldraw[wforange] (5.130,3.615) rectangle (5.220,3.705);
48
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,3.120) {ClimaX};
49
+ \draw[wfslate, line width=0.72pt] (4.032,2.990) -- (4.060,2.990);
50
+ \draw[wfslate, line width=0.72pt] (4.032,2.955) -- (4.032,3.025);
51
+ \draw[wfslate, line width=0.72pt] (4.060,2.955) -- (4.060,3.025);
52
+ \filldraw[wfslate] (4.046,2.990) circle[radius=0.045];
53
+ \draw[wforange, line width=0.72pt] (4.036,3.250) -- (4.036,3.250);
54
+ \draw[wforange, line width=0.72pt] (4.036,3.215) -- (4.036,3.285);
55
+ \draw[wforange, line width=0.72pt] (4.036,3.215) -- (4.036,3.285);
56
+ \filldraw[wforange] (3.991,3.205) rectangle (4.081,3.295);
57
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,2.710) {StormCast};
58
+ \draw[wfslate, line width=0.72pt] (4.036,2.580) -- (4.036,2.580);
59
+ \draw[wfslate, line width=0.72pt] (4.036,2.545) -- (4.036,2.615);
60
+ \draw[wfslate, line width=0.72pt] (4.036,2.545) -- (4.036,2.615);
61
+ \filldraw[wfslate] (4.036,2.580) circle[radius=0.045];
62
+ \draw[wforange, line width=0.72pt] (4.036,2.840) -- (4.036,2.840);
63
+ \draw[wforange, line width=0.72pt] (4.036,2.805) -- (4.036,2.875);
64
+ \draw[wforange, line width=0.72pt] (4.036,2.805) -- (4.036,2.875);
65
+ \filldraw[wforange] (3.991,2.795) rectangle (4.081,2.885);
66
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,2.300) {DLWP};
67
+ \draw[wfslate, line width=0.72pt] (4.036,2.170) -- (4.036,2.170);
68
+ \draw[wfslate, line width=0.72pt] (4.036,2.135) -- (4.036,2.205);
69
+ \draw[wfslate, line width=0.72pt] (4.036,2.135) -- (4.036,2.205);
70
+ \filldraw[wfslate] (4.036,2.170) circle[radius=0.045];
71
+ \draw[wforange, line width=0.72pt] (4.044,2.430) -- (4.735,2.430);
72
+ \draw[wforange, line width=0.72pt] (4.044,2.395) -- (4.044,2.465);
73
+ \draw[wforange, line width=0.72pt] (4.735,2.395) -- (4.735,2.465);
74
+ \filldraw[wforange] (4.345,2.385) rectangle (4.435,2.475);
75
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,1.890) {FCN};
76
+ \draw[wfslate, line width=0.72pt] (4.036,1.760) -- (4.036,1.760);
77
+ \draw[wfslate, line width=0.72pt] (4.036,1.725) -- (4.036,1.795);
78
+ \draw[wfslate, line width=0.72pt] (4.036,1.725) -- (4.036,1.795);
79
+ \filldraw[wfslate] (4.036,1.760) circle[radius=0.045];
80
+ \draw[wforange, line width=0.72pt] (3.971,2.020) -- (4.286,2.020);
81
+ \draw[wforange, line width=0.72pt] (3.971,1.985) -- (3.971,2.055);
82
+ \draw[wforange, line width=0.72pt] (4.286,1.985) -- (4.286,2.055);
83
+ \filldraw[wforange] (4.083,1.975) rectangle (4.173,2.065);
84
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,1.480) {FengWu};
85
+ \draw[wfslate, line width=0.72pt] (4.036,1.350) -- (4.036,1.350);
86
+ \draw[wfslate, line width=0.72pt] (4.036,1.315) -- (4.036,1.385);
87
+ \draw[wfslate, line width=0.72pt] (4.036,1.315) -- (4.036,1.385);
88
+ \filldraw[wfslate] (4.036,1.350) circle[radius=0.045];
89
+ \draw[wforange, line width=0.72pt] (4.028,1.610) -- (4.127,1.610);
90
+ \draw[wforange, line width=0.72pt] (4.028,1.575) -- (4.028,1.645);
91
+ \draw[wforange, line width=0.72pt] (4.127,1.575) -- (4.127,1.645);
92
+ \filldraw[wforange] (4.032,1.565) rectangle (4.122,1.655);
93
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,1.070) {FuXi};
94
+ \draw[wfslate, line width=0.72pt] (4.036,0.940) -- (4.036,0.940);
95
+ \draw[wfslate, line width=0.72pt] (4.036,0.905) -- (4.036,0.975);
96
+ \draw[wfslate, line width=0.72pt] (4.036,0.905) -- (4.036,0.975);
97
+ \filldraw[wfslate] (4.036,0.940) circle[radius=0.045];
98
+ \draw[wforange, line width=0.72pt] (4.029,1.200) -- (4.087,1.200);
99
+ \draw[wforange, line width=0.72pt] (4.029,1.165) -- (4.029,1.235);
100
+ \draw[wforange, line width=0.72pt] (4.087,1.165) -- (4.087,1.235);
101
+ \filldraw[wforange] (4.013,1.155) rectangle (4.103,1.245);
102
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,0.660) {Pangu-Weather};
103
+ \draw[wfslate, line width=0.72pt] (4.036,0.530) -- (4.036,0.530);
104
+ \draw[wfslate, line width=0.72pt] (4.036,0.495) -- (4.036,0.565);
105
+ \draw[wfslate, line width=0.72pt] (4.036,0.495) -- (4.036,0.565);
106
+ \filldraw[wfslate] (4.036,0.530) circle[radius=0.045];
107
+ \draw[wforange, line width=0.72pt] (4.025,0.790) -- (4.076,0.790);
108
+ \draw[wforange, line width=0.72pt] (4.025,0.755) -- (4.025,0.825);
109
+ \draw[wforange, line width=0.72pt] (4.076,0.755) -- (4.076,0.825);
110
+ \filldraw[wforange] (4.006,0.745) rectangle (4.096,0.835);
111
+ \node[anchor=east, font=\scriptsize, text=black!82] at (2.320,0.250) {AlphaEarth};
112
+ \draw[wfslate, line width=0.72pt] (4.700,0.120) -- (6.103,0.120);
113
+ \draw[wfslate, line width=0.72pt] (4.700,0.085) -- (4.700,0.155);
114
+ \draw[wfslate, line width=0.72pt] (6.103,0.085) -- (6.103,0.155);
115
+ \filldraw[wfslate] (5.401,0.120) circle[radius=0.045];
116
+ \draw[wforange, line width=0.72pt] (3.872,0.380) -- (4.815,0.380);
117
+ \draw[wforange, line width=0.72pt] (3.872,0.345) -- (3.872,0.415);
118
+ \draw[wforange, line width=0.72pt] (4.815,0.345) -- (4.815,0.415);
119
+ \filldraw[wforange] (4.298,0.335) rectangle (4.388,0.425);
120
+ \end{tikzpicture}
paper_outputs/figures/fig_task_contract_tiles.pdf ADDED
Binary file (49.6 kB). View file
 
paper_outputs/figures/fig_task_rank_map.pdf ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %PDF-1.4
2
+ %����
3
+ 1 0 obj
4
+ << /Type /Catalog /Pages 2 0 R >>
5
+ endobj
6
+ 2 0 obj
7
+ << /Type /Pages /Kids [3 0 R] /Count 1 >>
8
+ endobj
9
+ 3 0 obj
10
+ << /Type /Page /Parent 2 0 R /MediaBox [0 0 1120 430] /Resources << /Font << /F1 4 0 R /F2 5 0 R >> >> /Contents 6 0 R >>
11
+ endobj
12
+ 4 0 obj
13
+ << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
14
+ endobj
15
+ 5 0 obj
16
+ << /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>
17
+ endobj
18
+ 6 0 obj
19
+ << /Length 22799 >>
20
+ stream
21
+ 1.0000 1.0000 1.0000 rg 0.00 0.00 1120.00 430.00 re f
22
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 117.66 376.00 Tm (FireWx-FM ref.) Tj ET
23
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 211.06 376.00 Tm (Prithvi-WxC) Tj ET
24
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 308.29 376.00 Tm (Aurora) Tj ET
25
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 394.93 376.00 Tm (ClimaX) Tj ET
26
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 471.34 376.00 Tm (StormCast) Tj ET
27
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 568.76 376.00 Tm (DLWP) Tj ET
28
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 658.50 376.00 Tm (FCN) Tj ET
29
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 736.92 376.00 Tm (FengWu) Tj ET
30
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 829.82 376.00 Tm (FuXi) Tj ET
31
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 892.30 376.00 Tm (Pangu-Weather) Tj ET
32
+ BT /F2 8.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 987.57 376.00 Tm (AlphaEarth) Tj ET
33
+ BT /F2 7.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 325.00 Tm (Occupancy) Tj ET
34
+ BT /F2 7.10 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 314.00 Tm (Union F1 \(%\)) Tj ET
35
+ BT /F1 6.40 Tf 0.4200 0.4400 0.4600 rg 1 0 0 1 12.00 303.00 Tm (higher better) Tj ET
36
+ 0.80 w 0.1500 0.4760 0.4860 rg 1.0000 1.0000 1.0000 RG 108.00 300.00 86.00 42.00 re B
37
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.88 324.00 Tm (#2) Tj ET
38
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 142.84 309.00 Tm (59.07) Tj ET
39
+ 0.80 w 0.9300 0.9500 0.9400 rg 1.0000 1.0000 1.0000 RG 194.00 300.00 86.00 42.00 re B
40
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 227.83 324.00 Tm (#11) Tj ET
41
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 228.84 309.00 Tm (20.19) Tj ET
42
+ 0.80 w 0.7780 0.8820 0.8640 rg 1.0000 1.0000 1.0000 RG 280.00 300.00 86.00 42.00 re B
43
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 316.88 324.00 Tm (#9) Tj ET
44
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 314.85 309.00 Tm (23.10) Tj ET
45
+ 0.80 w 0.0500 0.4000 0.4200 rg 1.0000 1.0000 1.0000 RG 366.00 300.00 86.00 42.00 re B
46
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 402.88 324.00 Tm (#1) Tj ET
47
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 400.85 309.00 Tm (60.15) Tj ET
48
+ 0.80 w 0.8540 0.9160 0.9020 rg 1.0000 1.0000 1.0000 RG 452.00 300.00 86.00 42.00 re B
49
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 485.83 324.00 Tm (#10) Tj ET
50
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 486.85 309.00 Tm (22.38) Tj ET
51
+ 0.80 w 0.6260 0.8140 0.7880 rg 1.0000 1.0000 1.0000 RG 538.00 300.00 86.00 42.00 re B
52
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.88 324.00 Tm (#7) Tj ET
53
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 572.85 309.00 Tm (28.19) Tj ET
54
+ 0.80 w 0.2500 0.5520 0.5520 rg 1.0000 1.0000 1.0000 RG 624.00 300.00 86.00 42.00 re B
55
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.88 324.00 Tm (#3) Tj ET
56
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 658.85 309.00 Tm (40.06) Tj ET
57
+ 0.80 w 0.7020 0.8480 0.8260 rg 1.0000 1.0000 1.0000 RG 710.00 300.00 86.00 42.00 re B
58
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.88 324.00 Tm (#8) Tj ET
59
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 744.85 309.00 Tm (24.10) Tj ET
60
+ 0.80 w 0.4500 0.7040 0.6840 rg 1.0000 1.0000 1.0000 RG 796.00 300.00 86.00 42.00 re B
61
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.88 324.00 Tm (#5) Tj ET
62
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 830.85 309.00 Tm (37.29) Tj ET
63
+ 0.80 w 0.5500 0.7800 0.7500 rg 1.0000 1.0000 1.0000 RG 882.00 300.00 86.00 42.00 re B
64
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.88 324.00 Tm (#6) Tj ET
65
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 916.85 309.00 Tm (35.64) Tj ET
66
+ 0.80 w 0.3500 0.6280 0.6180 rg 1.0000 1.0000 1.0000 RG 968.00 300.00 86.00 42.00 re B
67
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 1004.88 324.00 Tm (#4) Tj ET
68
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 1002.85 309.00 Tm (37.43) Tj ET
69
+ BT /F2 7.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 283.00 Tm (Fire spread) Tj ET
70
+ BT /F2 7.10 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 272.00 Tm (AP \(%\)) Tj ET
71
+ BT /F1 6.40 Tf 0.4200 0.4400 0.4600 rg 1 0 0 1 12.00 261.00 Tm (higher better) Tj ET
72
+ 0.80 w 0.0500 0.4000 0.4200 rg 1.0000 1.0000 1.0000 RG 108.00 258.00 86.00 42.00 re B
73
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.88 282.00 Tm (#1) Tj ET
74
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 142.84 267.00 Tm (30.09) Tj ET
75
+ 0.80 w 0.7780 0.8820 0.8640 rg 1.0000 1.0000 1.0000 RG 194.00 258.00 86.00 42.00 re B
76
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.88 282.00 Tm (#9) Tj ET
77
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.66 267.00 Tm (5.00) Tj ET
78
+ 0.80 w 0.1500 0.4760 0.4860 rg 1.0000 1.0000 1.0000 RG 280.00 258.00 86.00 42.00 re B
79
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 316.88 282.00 Tm (#2) Tj ET
80
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 314.85 267.00 Tm (16.62) Tj ET
81
+ 0.80 w 0.6260 0.8140 0.7880 rg 1.0000 1.0000 1.0000 RG 366.00 258.00 86.00 42.00 re B
82
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 402.88 282.00 Tm (#7) Tj ET
83
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 400.85 267.00 Tm (11.17) Tj ET
84
+ 0.80 w 0.8540 0.9160 0.9020 rg 1.0000 1.0000 1.0000 RG 452.00 258.00 86.00 42.00 re B
85
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 485.83 282.00 Tm (#10) Tj ET
86
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.67 267.00 Tm (2.81) Tj ET
87
+ 0.80 w 0.7020 0.8480 0.8260 rg 1.0000 1.0000 1.0000 RG 538.00 258.00 86.00 42.00 re B
88
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.88 282.00 Tm (#8) Tj ET
89
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.66 267.00 Tm (5.94) Tj ET
90
+ 0.80 w 0.9300 0.9500 0.9400 rg 1.0000 1.0000 1.0000 RG 624.00 258.00 86.00 42.00 re B
91
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 657.83 282.00 Tm (#11) Tj ET
92
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.66 267.00 Tm (2.39) Tj ET
93
+ 0.80 w 0.3500 0.6280 0.6180 rg 1.0000 1.0000 1.0000 RG 710.00 258.00 86.00 42.00 re B
94
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.88 282.00 Tm (#4) Tj ET
95
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 744.85 267.00 Tm (13.17) Tj ET
96
+ 0.80 w 0.2500 0.5520 0.5520 rg 1.0000 1.0000 1.0000 RG 796.00 258.00 86.00 42.00 re B
97
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.88 282.00 Tm (#3) Tj ET
98
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 830.85 267.00 Tm (14.35) Tj ET
99
+ 0.80 w 0.4500 0.7040 0.6840 rg 1.0000 1.0000 1.0000 RG 882.00 258.00 86.00 42.00 re B
100
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.88 282.00 Tm (#5) Tj ET
101
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 916.85 267.00 Tm (12.69) Tj ET
102
+ 0.80 w 0.5500 0.7800 0.7500 rg 1.0000 1.0000 1.0000 RG 968.00 258.00 86.00 42.00 re B
103
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 1004.88 282.00 Tm (#6) Tj ET
104
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 1002.85 267.00 Tm (11.83) Tj ET
105
+ BT /F2 7.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 241.00 Tm (Burned area) Tj ET
106
+ BT /F2 7.10 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 230.00 Tm (log-RMSE) Tj ET
107
+ BT /F1 6.40 Tf 0.4200 0.4400 0.4600 rg 1 0 0 1 12.00 219.00 Tm (lower better) Tj ET
108
+ 0.80 w 0.0500 0.4000 0.4200 rg 1.0000 1.0000 1.0000 RG 108.00 216.00 86.00 42.00 re B
109
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.88 240.00 Tm (#1) Tj ET
110
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.66 225.00 Tm (1.17) Tj ET
111
+ 0.80 w 0.3500 0.6280 0.6180 rg 1.0000 1.0000 1.0000 RG 194.00 216.00 86.00 42.00 re B
112
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.88 240.00 Tm (#4) Tj ET
113
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.66 225.00 Tm (1.36) Tj ET
114
+ 0.80 w 0.7780 0.8820 0.8640 rg 1.0000 1.0000 1.0000 RG 280.00 216.00 86.00 42.00 re B
115
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 316.88 240.00 Tm (#9) Tj ET
116
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 316.67 225.00 Tm (1.87) Tj ET
117
+ 0.80 w 0.8540 0.9160 0.9020 rg 1.0000 1.0000 1.0000 RG 366.00 216.00 86.00 42.00 re B
118
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 399.83 240.00 Tm (#10) Tj ET
119
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 402.67 225.00 Tm (2.03) Tj ET
120
+ 0.80 w 0.7020 0.8480 0.8260 rg 1.0000 1.0000 1.0000 RG 452.00 216.00 86.00 42.00 re B
121
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.88 240.00 Tm (#8) Tj ET
122
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.67 225.00 Tm (1.67) Tj ET
123
+ 0.80 w 0.1500 0.4760 0.4860 rg 1.0000 1.0000 1.0000 RG 538.00 216.00 86.00 42.00 re B
124
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 574.88 240.00 Tm (#2) Tj ET
125
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 574.66 225.00 Tm (1.31) Tj ET
126
+ 0.80 w 0.4500 0.7040 0.6840 rg 1.0000 1.0000 1.0000 RG 624.00 216.00 86.00 42.00 re B
127
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.88 240.00 Tm (#5) Tj ET
128
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.66 225.00 Tm (1.37) Tj ET
129
+ 0.80 w 0.5500 0.7800 0.7500 rg 1.0000 1.0000 1.0000 RG 710.00 216.00 86.00 42.00 re B
130
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.88 240.00 Tm (#6) Tj ET
131
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.66 225.00 Tm (1.37) Tj ET
132
+ 0.80 w 0.6260 0.8140 0.7880 rg 1.0000 1.0000 1.0000 RG 796.00 216.00 86.00 42.00 re B
133
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.88 240.00 Tm (#7) Tj ET
134
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.66 225.00 Tm (1.41) Tj ET
135
+ 0.80 w 0.2500 0.5520 0.5520 rg 1.0000 1.0000 1.0000 RG 882.00 216.00 86.00 42.00 re B
136
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.88 240.00 Tm (#3) Tj ET
137
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.66 225.00 Tm (1.33) Tj ET
138
+ 0.80 w 0.9300 0.9500 0.9400 rg 1.0000 1.0000 1.0000 RG 968.00 216.00 86.00 42.00 re B
139
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 1001.83 240.00 Tm (#11) Tj ET
140
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 1004.66 225.00 Tm (2.41) Tj ET
141
+ BT /F2 7.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 199.00 Tm (Analog retrieval) Tj ET
142
+ BT /F2 7.10 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 188.00 Tm (nDCG@10) Tj ET
143
+ BT /F1 6.40 Tf 0.4200 0.4400 0.4600 rg 1 0 0 1 12.00 177.00 Tm (higher better) Tj ET
144
+ 0.80 w 0.0500 0.4000 0.4200 rg 1.0000 1.0000 1.0000 RG 108.00 174.00 86.00 42.00 re B
145
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.88 198.00 Tm (#1) Tj ET
146
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 142.84 183.00 Tm (0.510) Tj ET
147
+ 0.80 w 0.9300 0.9500 0.9400 rg 1.0000 1.0000 1.0000 RG 194.00 174.00 86.00 42.00 re B
148
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 227.83 198.00 Tm (#11) Tj ET
149
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 228.84 183.00 Tm (0.386) Tj ET
150
+ 0.80 w 0.7020 0.8480 0.8260 rg 1.0000 1.0000 1.0000 RG 280.00 174.00 86.00 42.00 re B
151
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 316.88 198.00 Tm (#8) Tj ET
152
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 314.85 183.00 Tm (0.405) Tj ET
153
+ 0.80 w 0.5500 0.7800 0.7500 rg 1.0000 1.0000 1.0000 RG 366.00 174.00 86.00 42.00 re B
154
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 402.88 198.00 Tm (#6) Tj ET
155
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 400.85 183.00 Tm (0.414) Tj ET
156
+ 0.80 w 0.6260 0.8140 0.7880 rg 1.0000 1.0000 1.0000 RG 452.00 174.00 86.00 42.00 re B
157
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.88 198.00 Tm (#7) Tj ET
158
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 486.85 183.00 Tm (0.408) Tj ET
159
+ 0.80 w 0.8540 0.9160 0.9020 rg 1.0000 1.0000 1.0000 RG 538.00 174.00 86.00 42.00 re B
160
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 571.83 198.00 Tm (#10) Tj ET
161
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 572.85 183.00 Tm (0.397) Tj ET
162
+ 0.80 w 0.2500 0.5520 0.5520 rg 1.0000 1.0000 1.0000 RG 624.00 174.00 86.00 42.00 re B
163
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.88 198.00 Tm (#3) Tj ET
164
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 658.85 183.00 Tm (0.432) Tj ET
165
+ 0.80 w 0.4500 0.7040 0.6840 rg 1.0000 1.0000 1.0000 RG 710.00 174.00 86.00 42.00 re B
166
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.88 198.00 Tm (#5) Tj ET
167
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 744.85 183.00 Tm (0.425) Tj ET
168
+ 0.80 w 0.3500 0.6280 0.6180 rg 1.0000 1.0000 1.0000 RG 796.00 174.00 86.00 42.00 re B
169
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.88 198.00 Tm (#4) Tj ET
170
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 830.85 183.00 Tm (0.428) Tj ET
171
+ 0.80 w 0.7780 0.8820 0.8640 rg 1.0000 1.0000 1.0000 RG 882.00 174.00 86.00 42.00 re B
172
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.88 198.00 Tm (#9) Tj ET
173
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 916.85 183.00 Tm (0.402) Tj ET
174
+ 0.80 w 0.1500 0.4760 0.4860 rg 1.0000 1.0000 1.0000 RG 968.00 174.00 86.00 42.00 re B
175
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 1004.88 198.00 Tm (#2) Tj ET
176
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 1002.85 183.00 Tm (0.509) Tj ET
177
+ BT /F2 7.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 157.00 Tm (Smoke PM2.5) Tj ET
178
+ BT /F2 7.10 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 146.00 Tm (RMSE) Tj ET
179
+ BT /F1 6.40 Tf 0.4200 0.4400 0.4600 rg 1 0 0 1 12.00 135.00 Tm (lower better) Tj ET
180
+ 0.80 w 0.1500 0.4760 0.4860 rg 1.0000 1.0000 1.0000 RG 108.00 132.00 86.00 42.00 re B
181
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.88 156.00 Tm (#2) Tj ET
182
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.66 141.00 Tm (4.46) Tj ET
183
+ 0.80 w 0.7020 0.8480 0.8260 rg 1.0000 1.0000 1.0000 RG 194.00 132.00 86.00 42.00 re B
184
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.88 156.00 Tm (#8) Tj ET
185
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.66 141.00 Tm (6.04) Tj ET
186
+ 0.80 w 0.7780 0.8820 0.8640 rg 1.0000 1.0000 1.0000 RG 280.00 132.00 86.00 42.00 re B
187
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 316.88 156.00 Tm (#9) Tj ET
188
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 316.67 141.00 Tm (6.04) Tj ET
189
+ 0.80 w 0.8540 0.9160 0.9020 rg 1.0000 1.0000 1.0000 RG 366.00 132.00 86.00 42.00 re B
190
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 399.83 156.00 Tm (#10) Tj ET
191
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 402.67 141.00 Tm (6.04) Tj ET
192
+ 0.80 w 0.9300 0.9500 0.9400 rg 1.0000 1.0000 1.0000 RG 452.00 132.00 86.00 42.00 re B
193
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 485.83 156.00 Tm (#11) Tj ET
194
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.67 141.00 Tm (6.12) Tj ET
195
+ 0.80 w 0.4500 0.7040 0.6840 rg 1.0000 1.0000 1.0000 RG 538.00 132.00 86.00 42.00 re B
196
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.88 156.00 Tm (#5) Tj ET
197
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.66 141.00 Tm (5.93) Tj ET
198
+ 0.80 w 0.3500 0.6280 0.6180 rg 1.0000 1.0000 1.0000 RG 624.00 132.00 86.00 42.00 re B
199
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.88 156.00 Tm (#4) Tj ET
200
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.66 141.00 Tm (5.93) Tj ET
201
+ 0.80 w 0.5500 0.7800 0.7500 rg 1.0000 1.0000 1.0000 RG 710.00 132.00 86.00 42.00 re B
202
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.88 156.00 Tm (#6) Tj ET
203
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.66 141.00 Tm (5.93) Tj ET
204
+ 0.80 w 0.6260 0.8140 0.7880 rg 1.0000 1.0000 1.0000 RG 796.00 132.00 86.00 42.00 re B
205
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.88 156.00 Tm (#7) Tj ET
206
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.66 141.00 Tm (5.93) Tj ET
207
+ 0.80 w 0.2500 0.5520 0.5520 rg 1.0000 1.0000 1.0000 RG 882.00 132.00 86.00 42.00 re B
208
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.88 156.00 Tm (#3) Tj ET
209
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.66 141.00 Tm (5.93) Tj ET
210
+ 0.80 w 0.0500 0.4000 0.4200 rg 1.0000 1.0000 1.0000 RG 968.00 132.00 86.00 42.00 re B
211
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 1004.88 156.00 Tm (#1) Tj ET
212
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 1004.66 141.00 Tm (4.44) Tj ET
213
+ BT /F2 7.70 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 115.00 Tm (Extreme heat) Tj ET
214
+ BT /F2 7.10 Tf 0.1200 0.1400 0.1600 rg 1 0 0 1 12.00 104.00 Tm (RMSE-C) Tj ET
215
+ BT /F1 6.40 Tf 0.4200 0.4400 0.4600 rg 1 0 0 1 12.00 93.00 Tm (lower better) Tj ET
216
+ 0.80 w 0.0500 0.4000 0.4200 rg 1.0000 1.0000 1.0000 RG 108.00 90.00 86.00 42.00 re B
217
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 144.88 114.00 Tm (#1) Tj ET
218
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 142.84 99.00 Tm (0.218) Tj ET
219
+ 0.80 w 0.7780 0.8820 0.8640 rg 1.0000 1.0000 1.0000 RG 194.00 90.00 86.00 42.00 re B
220
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.88 114.00 Tm (#9) Tj ET
221
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 230.66 99.00 Tm (4.62) Tj ET
222
+ 0.80 w 0.9300 0.9500 0.9400 rg 1.0000 1.0000 1.0000 RG 280.00 90.00 86.00 42.00 re B
223
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 313.83 114.00 Tm (#11) Tj ET
224
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 314.85 99.00 Tm (18.05) Tj ET
225
+ 0.80 w 0.8540 0.9160 0.9020 rg 1.0000 1.0000 1.0000 RG 366.00 90.00 86.00 42.00 re B
226
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 399.83 114.00 Tm (#10) Tj ET
227
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 400.85 99.00 Tm (17.65) Tj ET
228
+ 0.80 w 0.2500 0.5520 0.5520 rg 1.0000 1.0000 1.0000 RG 452.00 90.00 86.00 42.00 re B
229
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.88 114.00 Tm (#3) Tj ET
230
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 488.67 99.00 Tm (1.77) Tj ET
231
+ 0.80 w 0.7020 0.8480 0.8260 rg 1.0000 1.0000 1.0000 RG 538.00 90.00 86.00 42.00 re B
232
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.88 114.00 Tm (#8) Tj ET
233
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 574.66 99.00 Tm (2.27) Tj ET
234
+ 0.80 w 0.5500 0.7800 0.7500 rg 1.0000 1.0000 1.0000 RG 624.00 90.00 86.00 42.00 re B
235
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.88 114.00 Tm (#6) Tj ET
236
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 660.66 99.00 Tm (2.17) Tj ET
237
+ 0.80 w 0.3500 0.6280 0.6180 rg 1.0000 1.0000 1.0000 RG 710.00 90.00 86.00 42.00 re B
238
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.88 114.00 Tm (#4) Tj ET
239
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 746.66 99.00 Tm (2.13) Tj ET
240
+ 0.80 w 0.4500 0.7040 0.6840 rg 1.0000 1.0000 1.0000 RG 796.00 90.00 86.00 42.00 re B
241
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.88 114.00 Tm (#5) Tj ET
242
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 832.66 99.00 Tm (2.13) Tj ET
243
+ 0.80 w 0.6260 0.8140 0.7880 rg 1.0000 1.0000 1.0000 RG 882.00 90.00 86.00 42.00 re B
244
+ BT /F2 11.20 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.88 114.00 Tm (#7) Tj ET
245
+ BT /F1 7.00 Tf 0.0700 0.0900 0.1100 rg 1 0 0 1 918.66 99.00 Tm (2.20) Tj ET
246
+ 0.80 w 0.1500 0.4760 0.4860 rg 1.0000 1.0000 1.0000 RG 968.00 90.00 86.00 42.00 re B
247
+ BT /F2 11.20 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 1004.88 114.00 Tm (#2) Tj ET
248
+ BT /F1 7.00 Tf 1.0000 1.0000 1.0000 rg 1 0 0 1 1002.85 99.00 Tm (0.219) Tj ET
249
+ 0.80 w 0.2000 0.2200 0.2400 RG 108.00 90.00 946.00 252.00 re S
250
+ BT /F2 9.00 Tf 0.2400 0.2500 0.2600 rg 1 0 0 1 908.00 63.00 Tm (within-row rank) Tj ET
251
+ 0.9300 0.9500 0.9400 rg 834.00 46.00 2.60 10.00 re f
252
+ 0.9300 0.9500 0.9400 rg 836.50 46.00 2.60 10.00 re f
253
+ 0.9300 0.9500 0.9400 rg 839.00 46.00 2.60 10.00 re f
254
+ 0.9300 0.9500 0.9400 rg 841.50 46.00 2.60 10.00 re f
255
+ 0.9300 0.9500 0.9400 rg 844.00 46.00 2.60 10.00 re f
256
+ 0.9300 0.9500 0.9400 rg 846.50 46.00 2.60 10.00 re f
257
+ 0.9300 0.9500 0.9400 rg 849.00 46.00 2.60 10.00 re f
258
+ 0.9300 0.9500 0.9400 rg 851.50 46.00 2.60 10.00 re f
259
+ 0.8540 0.9160 0.9020 rg 854.00 46.00 2.60 10.00 re f
260
+ 0.8540 0.9160 0.9020 rg 856.50 46.00 2.60 10.00 re f
261
+ 0.8540 0.9160 0.9020 rg 859.00 46.00 2.60 10.00 re f
262
+ 0.8540 0.9160 0.9020 rg 861.50 46.00 2.60 10.00 re f
263
+ 0.8540 0.9160 0.9020 rg 864.00 46.00 2.60 10.00 re f
264
+ 0.8540 0.9160 0.9020 rg 866.50 46.00 2.60 10.00 re f
265
+ 0.8540 0.9160 0.9020 rg 869.00 46.00 2.60 10.00 re f
266
+ 0.8540 0.9160 0.9020 rg 871.50 46.00 2.60 10.00 re f
267
+ 0.7780 0.8820 0.8640 rg 874.00 46.00 2.60 10.00 re f
268
+ 0.7780 0.8820 0.8640 rg 876.50 46.00 2.60 10.00 re f
269
+ 0.7780 0.8820 0.8640 rg 879.00 46.00 2.60 10.00 re f
270
+ 0.7780 0.8820 0.8640 rg 881.50 46.00 2.60 10.00 re f
271
+ 0.7780 0.8820 0.8640 rg 884.00 46.00 2.60 10.00 re f
272
+ 0.7780 0.8820 0.8640 rg 886.50 46.00 2.60 10.00 re f
273
+ 0.7780 0.8820 0.8640 rg 889.00 46.00 2.60 10.00 re f
274
+ 0.7780 0.8820 0.8640 rg 891.50 46.00 2.60 10.00 re f
275
+ 0.7020 0.8480 0.8260 rg 894.00 46.00 2.60 10.00 re f
276
+ 0.7020 0.8480 0.8260 rg 896.50 46.00 2.60 10.00 re f
277
+ 0.7020 0.8480 0.8260 rg 899.00 46.00 2.60 10.00 re f
278
+ 0.7020 0.8480 0.8260 rg 901.50 46.00 2.60 10.00 re f
279
+ 0.7020 0.8480 0.8260 rg 904.00 46.00 2.60 10.00 re f
280
+ 0.7020 0.8480 0.8260 rg 906.50 46.00 2.60 10.00 re f
281
+ 0.7020 0.8480 0.8260 rg 909.00 46.00 2.60 10.00 re f
282
+ 0.7020 0.8480 0.8260 rg 911.50 46.00 2.60 10.00 re f
283
+ 0.6260 0.8140 0.7880 rg 914.00 46.00 2.60 10.00 re f
284
+ 0.6260 0.8140 0.7880 rg 916.50 46.00 2.60 10.00 re f
285
+ 0.6260 0.8140 0.7880 rg 919.00 46.00 2.60 10.00 re f
286
+ 0.6260 0.8140 0.7880 rg 921.50 46.00 2.60 10.00 re f
287
+ 0.6260 0.8140 0.7880 rg 924.00 46.00 2.60 10.00 re f
288
+ 0.6260 0.8140 0.7880 rg 926.50 46.00 2.60 10.00 re f
289
+ 0.6260 0.8140 0.7880 rg 929.00 46.00 2.60 10.00 re f
290
+ 0.6260 0.8140 0.7880 rg 931.50 46.00 2.60 10.00 re f
291
+ 0.5500 0.7800 0.7500 rg 934.00 46.00 2.60 10.00 re f
292
+ 0.5500 0.7800 0.7500 rg 936.50 46.00 2.60 10.00 re f
293
+ 0.5500 0.7800 0.7500 rg 939.00 46.00 2.60 10.00 re f
294
+ 0.5500 0.7800 0.7500 rg 941.50 46.00 2.60 10.00 re f
295
+ 0.5500 0.7800 0.7500 rg 944.00 46.00 2.60 10.00 re f
296
+ 0.5500 0.7800 0.7500 rg 946.50 46.00 2.60 10.00 re f
297
+ 0.5500 0.7800 0.7500 rg 949.00 46.00 2.60 10.00 re f
298
+ 0.5500 0.7800 0.7500 rg 951.50 46.00 2.60 10.00 re f
299
+ 0.4500 0.7040 0.6840 rg 954.00 46.00 2.60 10.00 re f
300
+ 0.4500 0.7040 0.6840 rg 956.50 46.00 2.60 10.00 re f
301
+ 0.4500 0.7040 0.6840 rg 959.00 46.00 2.60 10.00 re f
302
+ 0.4500 0.7040 0.6840 rg 961.50 46.00 2.60 10.00 re f
303
+ 0.4500 0.7040 0.6840 rg 964.00 46.00 2.60 10.00 re f
304
+ 0.4500 0.7040 0.6840 rg 966.50 46.00 2.60 10.00 re f
305
+ 0.4500 0.7040 0.6840 rg 969.00 46.00 2.60 10.00 re f
306
+ 0.4500 0.7040 0.6840 rg 971.50 46.00 2.60 10.00 re f
307
+ 0.3500 0.6280 0.6180 rg 974.00 46.00 2.60 10.00 re f
308
+ 0.3500 0.6280 0.6180 rg 976.50 46.00 2.60 10.00 re f
309
+ 0.3500 0.6280 0.6180 rg 979.00 46.00 2.60 10.00 re f
310
+ 0.3500 0.6280 0.6180 rg 981.50 46.00 2.60 10.00 re f
311
+ 0.3500 0.6280 0.6180 rg 984.00 46.00 2.60 10.00 re f
312
+ 0.3500 0.6280 0.6180 rg 986.50 46.00 2.60 10.00 re f
313
+ 0.3500 0.6280 0.6180 rg 989.00 46.00 2.60 10.00 re f
314
+ 0.3500 0.6280 0.6180 rg 991.50 46.00 2.60 10.00 re f
315
+ 0.2500 0.5520 0.5520 rg 994.00 46.00 2.60 10.00 re f
316
+ 0.2500 0.5520 0.5520 rg 996.50 46.00 2.60 10.00 re f
317
+ 0.2500 0.5520 0.5520 rg 999.00 46.00 2.60 10.00 re f
318
+ 0.2500 0.5520 0.5520 rg 1001.50 46.00 2.60 10.00 re f
319
+ 0.2500 0.5520 0.5520 rg 1004.00 46.00 2.60 10.00 re f
320
+ 0.2500 0.5520 0.5520 rg 1006.50 46.00 2.60 10.00 re f
321
+ 0.2500 0.5520 0.5520 rg 1009.00 46.00 2.60 10.00 re f
322
+ 0.2500 0.5520 0.5520 rg 1011.50 46.00 2.60 10.00 re f
323
+ 0.1500 0.4760 0.4860 rg 1014.00 46.00 2.60 10.00 re f
324
+ 0.1500 0.4760 0.4860 rg 1016.50 46.00 2.60 10.00 re f
325
+ 0.1500 0.4760 0.4860 rg 1019.00 46.00 2.60 10.00 re f
326
+ 0.1500 0.4760 0.4860 rg 1021.50 46.00 2.60 10.00 re f
327
+ 0.1500 0.4760 0.4860 rg 1024.00 46.00 2.60 10.00 re f
328
+ 0.1500 0.4760 0.4860 rg 1026.50 46.00 2.60 10.00 re f
329
+ 0.1500 0.4760 0.4860 rg 1029.00 46.00 2.60 10.00 re f
330
+ 0.1500 0.4760 0.4860 rg 1031.50 46.00 2.60 10.00 re f
331
+ BT /F1 7.00 Tf 0.2500 0.2600 0.2700 rg 1 0 0 1 834.00 34.00 Tm (rank 11) Tj ET
332
+ BT /F1 7.00 Tf 0.2500 0.2600 0.2700 rg 1 0 0 1 1013.84 34.00 Tm (rank 1) Tj ET
333
+ endstream
334
+ endobj
335
+ xref
336
+ 0 7
337
+ 0000000000 65535 f
338
+ 0000000015 00000 n
339
+ 0000000064 00000 n
340
+ 0000000121 00000 n
341
+ 0000000258 00000 n
342
+ 0000000328 00000 n
343
+ 0000000403 00000 n
344
+ trailer
345
+ << /Size 7 /Root 1 0 R >>
346
+ startxref
347
+ 23255
348
+ %%EOF
paper_outputs/figures/matching.pdf ADDED
Binary file (42.8 kB). View file
 
paper_outputs/tables/tab_app_analog_rank_depth.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{3pt}
5
+ \caption{For fixed retrieval \(\mathcal{T}\) and \(\Omega\), this table reports nDCG@5, best log gap, and rank \(\rho\) in addition to the main nDCG@10/log-error metrics. Cells report mean with small std.}
6
+ \label{tab:app_analog_rank_depth}
7
+ \begin{tabular}{lccc}
8
+ \toprule
9
+ Backbone & nDCG@5 & best log gap & rank $\rho$ \\
10
+ \midrule
11
+ FireWx-FM ref. & \ms{0.5175}{0.0445} & \ms{0.1868}{0.0285} & \ms{0.6019}{0.1460} \\
12
+ Prithvi-WxC & \ms{0.3591}{0.0107} & \ms{0.2151}{0.0594} & \ms{0.1514}{0.1489} \\
13
+ Aurora & \ms{0.4423}{0.0210} & \ms{0.1551}{0.0437} & \ms{0.2162}{0.1856} \\
14
+ ClimaX & \ms{0.4151}{0.0293} & \ms{0.2129}{0.0653} & \ms{0.1587}{0.2831} \\
15
+ StormCast & \ms{0.3960}{0.0240} & \ms{0.1714}{0.0310} & \ms{0.1258}{0.1625} \\
16
+ DLWP & \ms{0.3795}{0.0274} & \ms{0.1944}{0.0807} & \ms{-0.3865}{0.2802} \\
17
+ FCN & \ms{0.4250}{0.0112} & \ms{0.1856}{0.0846} & \ms{-0.1357}{0.2571} \\
18
+ FengWu & \ms{0.4228}{0.0310} & \ms{0.1870}{0.0858} & \ms{-0.1926}{0.2194} \\
19
+ FuXi & \ms{0.4544}{0.0356} & \ms{0.2171}{0.0806} & \ms{-0.1367}{0.2885} \\
20
+ Pangu-Weather & \ms{0.3988}{0.0506} & \ms{0.1901}{0.0838} & \ms{-0.1970}{0.2216} \\
21
+ AlphaEarth & \ms{0.5276}{0.0531} & \ms{0.1782}{0.0454} & \ms{0.4639}{0.2802} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
paper_outputs/tables/tab_app_burned_area_median_acre.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{3pt}
5
+ \caption{For fixed final-area \(\mathcal{T}\) and \(\Omega\), this table reports median log error and acre-scale errors in addition to the main log-RMSE/log-MAE/Spearman metrics. Cells report mean with small std.}
6
+ \label{tab:app_burned_area_median_acre}
7
+ \begin{tabular}{lccc}
8
+ \toprule
9
+ Backbone & log median AE & acre median AE & acre MAPE \\
10
+ \midrule
11
+ FireWx-FM ref. & \ms{1.0235}{0.0982} & \ms{4504.0692}{459.0483} & \ms{1.4525}{0.0254} \\
12
+ Prithvi-WxC & \ms{1.2184}{0.2107} & \ms{5375.8770}{788.7906} & \ms{1.9517}{0.2875} \\
13
+ Aurora & \ms{1.4547}{0.0301} & \ms{9904.9483}{457.4260} & \ms{6.8728}{3.0026} \\
14
+ ClimaX & \ms{1.6841}{0.1818} & \ms{18130.4820}{3248.3873} & \ms{8.2373}{2.8540} \\
15
+ StormCast & \ms{1.4522}{0.1519} & \ms{11155.7881}{2020.8656} & \ms{4.6142}{1.1500} \\
16
+ DLWP & \ms{1.0952}{0.1306} & \ms{4406.9315}{303.0944} & \ms{1.7357}{0.3625} \\
17
+ FCN & \ms{1.1688}{0.1139} & \ms{5166.9993}{213.0333} & \ms{2.0800}{0.4004} \\
18
+ FengWu & \ms{1.1589}{0.1772} & \ms{5137.2822}{628.7543} & \ms{2.0944}{0.4545} \\
19
+ FuXi & \ms{1.1855}{0.0612} & \ms{5697.7117}{796.8785} & \ms{2.4411}{0.5567} \\
20
+ Pangu-Weather & \ms{1.1221}{0.1470} & \ms{5092.3621}{483.8243} & \ms{1.9571}{0.3113} \\
21
+ AlphaEarth & \ms{1.7459}{0.6057} & \ms{15110.7573}{7106.3417} & \ms{9.7398}{2.7425} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
paper_outputs/tables/tab_app_contract_params_full.tex ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table}[h]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{3.5pt}
5
+ \renewcommand{\arraystretch}{1.2}
6
+ \caption{Fixed scoring values used by each task-form contract.}
7
+ \label{tab:app_contract_params_full}
8
+ \begin{adjustbox}{max width=\textwidth}
9
+ \begin{tabular}{llll}
10
+ \toprule
11
+ \textbf{\(\mathcal{T}\)} & \textbf{Scoring} & \textbf{Validation} & \textbf{\(\Omega\)} \\
12
+ \midrule
13
+ Occupancy & \(k=8,\Delta t=3\); exact/tol./union \(F_1\) & val. strict \(F_1\) & global; top-5/10/20\% fire-prone \\
14
+ Fire spread & \(k=4,\Delta t=0\); exact/spatial \(F_1\), AP & val. spatial \(F_1\) & spread-region cells \\
15
+ Final burned area & log-RMSE, log-MAE, Spearman \(\rho\) & val. log-RMSE & test events \\
16
+ Analog retrieval & nDCG@10; retrieved-event log error & val. nDCG@10 & test events \\
17
+ Smoke PM\(_{2.5}\) & RMSE, MAE, Pearson \(r\); exceedance 35 & val. RMSE & test stations \\
18
+ Extreme heat & RMSE-C, MAE-C, exceedance \(F_1\) & val. threshold 27/30/33\(^{\circ}\)C & heat-region stations \\
19
+ \bottomrule
20
+ \end{tabular}
21
+ \end{adjustbox}
22
+ \end{table}
paper_outputs/tables/tab_app_head_architectures.tex ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table}[h]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{5pt}
5
+ \renewcommand{\arraystretch}{1.3}
6
+ \caption{Lightweight head architectures used in the fixed-contract transfer comparisons.
7
+ All heads are trained from random initialisation on the frozen backbone features.
8
+ Parameter counts are approximate and depend on the feature dimensionality of each backbone.}
9
+ \label{tab:app_head_architectures}
10
+ \begin{tabular}{p{0.15\textwidth}p{0.30\textwidth}p{0.12\textwidth}p{0.33\textwidth}}
11
+ \toprule
12
+ \textbf{$\mathcal{A}$ head} & \textbf{Architecture} & \textbf{Approx.\ params} & \textbf{Notes} \\
13
+ \midrule
14
+ Constant prior &
15
+ Outputs a fixed bias vector, ignoring input features. &
16
+ Output dimension only &
17
+ Provides a degenerate baseline; selected when backbone features carry no useful signal. \\
18
+ Linear probe &
19
+ Single linear layer mapping backbone features to output. No nonlinearity. &
20
+ $d\times c + c$ &
21
+ Standard frozen-representation baseline. \\
22
+ Pixel MLP &
23
+ Two-layer MLP applied independently per spatial unit. &
24
+ $d\times h + h\times c$ &
25
+ Captures per-pixel nonlinearity; ignores spatial context. \\
26
+ Shallow adapter &
27
+ Two-layer MLP with a spatial context window; uses $3\times3$ convolution before the linear output. &
28
+ $9dh + hc$ &
29
+ Balances local spatial context with parameter efficiency. \\
30
+ Wide adapter &
31
+ Shallow adapter with wider hidden dimension. &
32
+ $9dH + Hc$ &
33
+ Higher capacity variant; can overfit on small fire-event sets. \\
34
+ \bottomrule
35
+ \end{tabular}
36
+ \end{table}
paper_outputs/tables/tab_app_heat_event_pr.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{3pt}
5
+ \caption{For fixed heat \(\mathcal{T}\) and heat-region \(\Omega\), this table reports precision and recall for the exceedance label used by the main \(F_1\). Cells report mean with small std.}
6
+ \label{tab:app_heat_event_pr}
7
+ \begin{tabular}{lcc}
8
+ \toprule
9
+ Backbone & precision & recall \\
10
+ \midrule
11
+ FireWx-FM ref. & \ms{0.9767}{0.0117} & \ms{0.9330}{0.0299} \\
12
+ Prithvi-WxC & \ms{0.8260}{0.0030} & \ms{0.9173}{0.0033} \\
13
+ Aurora & \ms{0.5920}{0.0347} & \ms{0.0517}{0.0020} \\
14
+ ClimaX & \ms{0.7397}{0.0099} & \ms{0.7994}{0.0051} \\
15
+ StormCast & \ms{0.8840}{0.0237} & \ms{0.9320}{0.0165} \\
16
+ DLWP & \ms{0.9429}{0.0085} & \ms{0.8899}{0.0167} \\
17
+ FCN & \ms{0.9408}{0.0097} & \ms{0.9111}{0.0127} \\
18
+ FengWu & \ms{0.3808}{0.2719} & \ms{0.0266}{0.0267} \\
19
+ FuXi & \ms{0.3262}{0.1262} & \ms{0.1810}{0.0481} \\
20
+ Pangu-Weather & \ms{0.1159}{0.0743} & \ms{0.0112}{0.0032} \\
21
+ AlphaEarth & \ms{0.9824}{0.0040} & \ms{0.9278}{0.0178} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
paper_outputs/tables/tab_app_matching_rule_params.tex ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table}[h]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{10pt}
5
+ \renewcommand{\arraystretch}{1.2}
6
+ \caption{Matching-rule values used in the evaluation contracts.}
7
+ \label{tab:app_matching_rule_params}
8
+ \begin{tabular}{lll}
9
+ \toprule
10
+ \textbf{Parameter} & \textbf{Occupancy} & \textbf{Fire spread} \\
11
+ \midrule
12
+ \(k\) & 8 cells & 4 cells \\
13
+ \(\Delta t\) & 3 for union; 0 spatial-only & 0 \\
14
+ \(\tau\) & val. strict \(F_1\) & val. spatial \(F_1\) \\
15
+ \bottomrule
16
+ \end{tabular}
17
+ \end{table}
paper_outputs/tables/tab_app_occupancy_ppr_scope.tex ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{4pt}
5
+ \renewcommand{\arraystretch}{1.18}
6
+ \caption{For fixed occupancy \(\mathcal{T}\), this table reports predicted-positive rate.
7
+ Values are percentages under the same validation-selected strict threshold.
8
+ Scopes \(\Omega\) are fixed before test scoring; cells report five-seed mean with std in small type.}
9
+ \label{tab:app_occupancy_ppr_scope}
10
+ \begin{tabular}{lcccc}
11
+ \toprule
12
+ \textbf{Backbone} & \textbf{\(\Omega=\)global} & \textbf{\(\Omega=\)top 5\%} & \textbf{\(\Omega=\)top 10\%} & \textbf{\(\Omega=\)top 20\%} \\
13
+ \midrule
14
+ FireWx-FM ref. & \ms{1.6808}{0.3684} & \ms{3.0619}{1.0925} & \ms{1.5310}{0.5463} & \ms{0.7655}{0.2732} \\
15
+ Prithvi-WxC & \ms{61.9711}{30.9101} & \ms{57.4117}{47.8987} & \ms{58.4565}{51.0897} & \ms{58.9788}{52.6991} \\
16
+ Aurora & \ms{55.5849}{19.7524} & \ms{57.2238}{35.3400} & \ms{68.7942}{37.6958} & \ms{67.2891}{38.3991} \\
17
+ ClimaX & \ms{5.6763}{3.9261} & \ms{24.0091}{9.2816} & \ms{11.8450}{4.5067} & \ms{5.7442}{4.1341} \\
18
+ StormCast & \ms{60.6507}{17.4895} & \ms{57.6017}{35.2921} & \ms{68.0766}{37.3899} & \ms{67.8397}{39.2410} \\
19
+ DLWP & \ms{4.3221}{1.5619} & \ms{9.4001}{5.0807} & \ms{4.9700}{3.6849} & \ms{1.9198}{1.4678} \\
20
+ FCN & \ms{1.5202}{1.3446} & \ms{4.7856}{2.9409} & \ms{2.7257}{1.6353} & \ms{0.8368}{0.2358} \\
21
+ FengWu & \ms{0.4277}{0.4830} & \ms{0.6004}{0.3041} & \ms{0.2609}{0.1935} & \ms{0.1501}{0.1206} \\
22
+ FuXi & \ms{0.4505}{0.2773} & \ms{2.9315}{2.6392} & \ms{0.5197}{0.6074} & \ms{0.3621}{0.4346} \\
23
+ Pangu-Weather & \ms{1.0801}{1.1308} & \ms{2.0549}{2.1893} & \ms{1.4029}{1.4739} & \ms{1.0103}{1.1084} \\
24
+ AlphaEarth & \ms{0.0691}{0.0499} & \ms{0.2826}{0.1497} & \ms{0.1524}{0.0770} & \ms{0.0656}{0.0414} \\
25
+ \bottomrule
26
+ \end{tabular}
27
+ \end{table*}
paper_outputs/tables/tab_app_scope_params.tex ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table}[h]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{8pt}
5
+ \renewcommand{\arraystretch}{1.2}
6
+ \caption{Scope values used in the evaluation contracts.}
7
+ \label{tab:app_scope_params}
8
+ \begin{tabular}{lcc}
9
+ \toprule
10
+ \textbf{\(\Omega\)} & \textbf{Definition} & \textbf{Units} \\
11
+ \midrule
12
+ Global & full domain & 8,085,000 test cells \\
13
+ Fire-prone top-5\% & top 5\% by training-period fire frequency & 404,280 test cells \\
14
+ Fire-prone top-10\% & top 10\% by training-period fire frequency & 808,560 test cells \\
15
+ Fire-prone top-20\% & top 20\% by training-period fire frequency & 1,617,000 test cells \\
16
+ Spread region & union of \(\widehat{B}\) and \(B\) & event-specific cells \\
17
+ \bottomrule
18
+ \end{tabular}
19
+ \end{table}
paper_outputs/tables/tab_app_seed_robustness.tex ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table}[h]
2
+ \centering
3
+ \small
4
+ \setlength{\tabcolsep}{5pt}
5
+ \renewcommand{\arraystretch}{1.2}
6
+ \caption{Seed summaries for stochastic checks. Values report mean with small std over completed seeds.}
7
+ \label{tab:app_seed_robustness}
8
+ \begin{adjustbox}{max width=\textwidth}
9
+ \begin{tabular}{p{0.28\textwidth}cllp{0.18\textwidth}}
10
+ \toprule
11
+ \textbf{\(\mathcal{T}\) check} & \textbf{Seeds} & \textbf{Primary value} & \textbf{Other value(s)} & \textbf{Reading} \\
12
+ \midrule
13
+ Final burned area &
14
+ 5 & log-RMSE \ms{1.1657}{0.0126} &
15
+ log-MAE \ms{1.0423}{0.0081}; Spear.\ \ms{0.6298}{0.0338} &
16
+ stable across seeds \\
17
+ Smoke PM\(_{2.5}\) &
18
+ 5 & RMSE \ms{4.4646}{0.0060} &
19
+ MAE \ms{2.4108}{0.0016}; \(r\) \ms{0.6368}{0.0013} &
20
+ stable at table precision \\
21
+ Extreme heat &
22
+ 5 & RMSE-C \ms{0.2179}{0.0043} &
23
+ MAE-C \ms{0.1787}{0.0018}; exceed.\ \(F_1\) \ms{0.9541}{0.0164} &
24
+ stable across seeds \\
25
+ Fire spread &
26
+ 5 & exact \(F_1\) \ms{37.6700}{0.9800} &
27
+ spatial \(F_1\) \ms{80.9700}{2.0200}; AP \ms{30.0900}{1.2500} &
28
+ stable across seeds \\
29
+ Aurora paired-head check &
30
+ 5 & fire-prone score diff.\ \ms{6.3500}{13.2800} &
31
+ PR-AUC and union choices differ in 2/5 seeds &
32
+ variable across seeds \\
33
+ \bottomrule
34
+ \end{tabular}
35
+ \end{adjustbox}
36
+ \end{table}
paper_outputs/tables/tab_app_smoke_high_event.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{3pt}
5
+ \caption{For fixed smoke \(\mathcal{T}\) and station \(\Omega\), this table reports RMSE, MAE, and 90th-percentile absolute error on test rows with observed PM$_{2.5}\ge35$; std uses a row bootstrap over those rows. Cells report mean with small std.}
6
+ \label{tab:app_smoke_high_event}
7
+ \begin{tabular}{lccc}
8
+ \toprule
9
+ Backbone & high-smoke RMSE & high-smoke MAE & high-smoke 90th AE \\
10
+ \midrule
11
+ FireWx-FM ref. & \ms{47.4870}{0.6346} & \ms{34.3954}{0.7654} & \ms{65.6213}{3.8778} \\
12
+ Prithvi-WxC & \ms{57.2224}{1.7268} & \ms{47.3871}{0.3153} & \ms{74.9666}{3.2381} \\
13
+ Aurora & \ms{57.2752}{1.7248} & \ms{47.4368}{0.3149} & \ms{75.0755}{3.1074} \\
14
+ ClimaX & \ms{57.2828}{1.7239} & \ms{47.4407}{0.3140} & \ms{75.1012}{3.0777} \\
15
+ StormCast & \ms{56.6512}{1.7517} & \ms{46.7914}{0.3281} & \ms{74.0794}{3.4707} \\
16
+ DLWP & \ms{57.0075}{1.7359} & \ms{47.1971}{0.3198} & \ms{74.4936}{3.3826} \\
17
+ FCN & \ms{57.0582}{1.7339} & \ms{47.2401}{0.3187} & \ms{74.6431}{3.1982} \\
18
+ FengWu & \ms{57.0158}{1.7357} & \ms{47.1957}{0.3194} & \ms{74.5652}{3.2871} \\
19
+ FuXi & \ms{56.9622}{1.7371} & \ms{47.1508}{0.3201} & \ms{74.3278}{3.4435} \\
20
+ Pangu-Weather & \ms{57.1282}{1.7307} & \ms{47.3050}{0.3170} & \ms{74.6830}{3.2375} \\
21
+ AlphaEarth & \ms{48.0665}{0.7904} & \ms{35.6088}{0.7341} & \ms{66.7613}{3.9235} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
paper_outputs/tables/tab_app_spread_ap_by_scope.tex ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{3pt}
5
+ \caption{For fixed spread \(\mathcal{T}\) and strict \(\Lambda\), this table reports AP under three \(\Omega\) scopes: full test, top-5\% train-fire area, and top-10\% train-fire area. Values are percentages; cells report mean with small std.}
6
+ \label{tab:app_spread_ap_by_scope}
7
+ \begin{tabular}{lccc}
8
+ \toprule
9
+ Backbone & full \(\Omega\) AP & top-5\% \(\Omega\) AP & top-10\% \(\Omega\) AP \\
10
+ \midrule
11
+ FireWx-FM ref. & \ms{30.0197}{1.5651} & \ms{40.7452}{2.0542} & \ms{37.4096}{1.8731} \\
12
+ Prithvi-WxC & \ms{4.8319}{0.1731} & \ms{12.6086}{0.4468} & \ms{8.7051}{0.1889} \\
13
+ Aurora & \ms{17.7723}{0.4293} & \ms{30.3106}{0.9404} & \ms{26.4732}{0.6932} \\
14
+ ClimaX & \ms{11.1726}{0.2337} & \ms{25.7871}{1.2896} & \ms{19.9977}{1.2217} \\
15
+ StormCast & \ms{8.1147}{1.1569} & \ms{18.5461}{1.1727} & \ms{14.1286}{1.2956} \\
16
+ DLWP & \ms{9.2142}{2.6587} & \ms{19.3346}{2.3922} & \ms{14.9788}{2.6696} \\
17
+ FCN & \ms{6.6774}{1.3001} & \ms{16.7396}{3.2955} & \ms{11.9308}{2.3881} \\
18
+ FengWu & \ms{11.0046}{2.7092} & \ms{21.1506}{1.2163} & \ms{17.0113}{1.5778} \\
19
+ FuXi & \ms{13.5507}{0.3840} & \ms{22.5434}{0.4100} & \ms{19.1964}{0.3943} \\
20
+ Pangu-Weather & \ms{10.6250}{1.4643} & \ms{19.8294}{1.3044} & \ms{15.8013}{1.1602} \\
21
+ AlphaEarth & \ms{12.2847}{1.3562} & \ms{22.8692}{0.4915} & \ms{18.2992}{1.2110} \\
22
+ \bottomrule
23
+ \end{tabular}
24
+ \end{table*}
paper_outputs/tables/tab_appendix_selection_regret_tolerance.tex ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{table*}[!t]
2
+ \centering
3
+ \scriptsize
4
+ \setlength{\tabcolsep}{4pt}
5
+ \caption{Selection-regret values under exact, tolerated, and union matching. Values are percentage-point regret from selecting \(h_R\) by PR-AUC instead of \(h_D\) by the decision metric. Rows report mean with small std over five seeds; \(0.0000\) denotes exact zero regret.}
6
+ \label{tab:appendix_selection_regret_tolerance}
7
+ \begin{adjustbox}{max width=\textwidth}
8
+ \begin{tabular}{llccc}
9
+ \toprule
10
+ \textbf{Feature} & \textbf{\(\Omega\)} & \textbf{Exact regret} & \textbf{Tolerated regret} & \textbf{Union regret} \\
11
+ \midrule
12
+ FireWx-FM ref. & global & 0.0000 & \ms{8.7830}{9.6705} & \ms{8.7830}{9.6705} \\
13
+ FireWx-FM ref. & fire-prone & 0.0000 & \ms{3.4027}{3.2045} & \ms{3.4027}{3.2045} \\
14
+ Prithvi-WxC & global & 0.0000 & 0.0000 & 0.0000 \\
15
+ Prithvi-WxC & fire-prone & 0.0000 & 0.0000 & 0.0000 \\
16
+ Aurora & global & \ms{0.0200}{0.0267} & \ms{9.8520}{12.9878} & \ms{9.8520}{12.9878} \\
17
+ Aurora & fire-prone & \ms{0.8203}{1.8341} & \ms{14.3919}{32.1219} & \ms{14.3919}{32.1219} \\
18
+ ClimaX & global & \ms{0.0003}{0.0004} & \ms{0.1296}{0.1775} & \ms{0.1296}{0.1775} \\
19
+ ClimaX & fire-prone & 0.0000 & 0.0000 & 0.0000 \\
20
+ StormCast & global & 0.0000 & 0.0000 & 0.0000 \\
21
+ StormCast & fire-prone & 0.0000 & 0.0000 & 0.0000 \\
22
+ DLWP & global & 0.0000 & 0.0000 & 0.0000 \\
23
+ DLWP & fire-prone & \ms{0.0770}{0.1100} & \ms{4.3266}{4.3323} & \ms{4.3266}{4.3323} \\
24
+ FCN & global & 0.0000 & 0.0000 & 0.0000 \\
25
+ FCN & fire-prone & \ms{0.0006}{0.0013} & \ms{1.1680}{1.9872} & \ms{1.1680}{1.9872} \\
26
+ FengWu & global & 0.0000 & 0.0000 & 0.0000 \\
27
+ FengWu & fire-prone & \ms{0.0691}{0.1191} & \ms{0.5222}{0.6239} & \ms{0.5222}{0.6239} \\
28
+ FuXi & global & 0.0000 & 0.0000 & 0.0000 \\
29
+ FuXi & fire-prone & 0.0000 & \ms{0.1084}{0.1729} & \ms{0.1084}{0.1729} \\
30
+ Pangu-Weather & global & 0.0000 & 0.0000 & 0.0000 \\
31
+ Pangu-Weather & fire-prone & \ms{0.0728}{0.1179} & \ms{0.1849}{0.3263} & \ms{0.1849}{0.3263} \\
32
+ AlphaEarth & global & 0.0000 & \ms{17.2217}{8.8492} & \ms{17.2217}{8.8492} \\
33
+ AlphaEarth & fire-prone & 0.0000 & \ms{3.8804}{5.9483} & \ms{3.8804}{5.9483} \\
34
+ \bottomrule
35
+ \end{tabular}
36
+ \end{adjustbox}
37
+ \end{table*}