autoemulate 2.0.0__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autoemulate-2.0.0 → autoemulate-2.1.1}/PKG-INFO +8 -2
- {autoemulate-2.0.0 → autoemulate-2.1.1}/README.md +5 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/__init__.py +3 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/base.py +17 -18
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/bayes.py +157 -22
- autoemulate-2.1.1/autoemulate/calibration/evidence.py +526 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/history_matching.py +11 -10
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/interval_excursion_set.py +15 -16
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/compare.py +49 -42
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/device.py +57 -3
- autoemulate-2.1.1/autoemulate/core/logging_config.py +77 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/model_selection.py +16 -11
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/plotting.py +14 -2
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/reinitialize.py +4 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/results.py +2 -3
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/sensitivity_analysis.py +2 -2
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/tuner.py +13 -10
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/data/utils.py +7 -9
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/base.py +9 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/ensemble.py +5 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/nn/mlp.py +6 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/polynomials.py +4 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/nn/gaussian_mlp.py +4 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/nn/zoib.py +4 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/base.py +12 -11
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/stream.py +6 -1
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/advection_diffusion.py +12 -4
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/base.py +44 -24
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/double_pendulum.py +19 -17
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/epidemic.py +8 -2
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/flow_problem.py +12 -10
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/projectile.py +16 -4
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/reaction_diffusion.py +12 -10
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/seir.py +8 -2
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/utils.py +40 -29
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/vae.py +10 -5
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/PKG-INFO +8 -2
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/SOURCES.txt +1 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/requires.txt +2 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/pyproject.toml +33 -1
- autoemulate-2.0.0/autoemulate/core/logging_config.py +0 -125
- {autoemulate-2.0.0 → autoemulate-2.1.1}/LICENSE +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/history_matching_dashboard.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/callbacks/early_stopping.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/metrics.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/save.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/types.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/raw/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/raw/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/raw/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/raw/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/raw/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/raw/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/raw/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/raw/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/raw/passive_mechanics/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/raw/passive_mechanics/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/raw/passive_mechanics.tar.xz +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/raw/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/raw/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/raw/Holden2018.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/datasets.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/description.md +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/raw/Nervi_truss_datasets.zip +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/raw/datasets/Sobol_CSV_data_1000.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/reactiondiffusion1/processed/outputs.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/reactiondiffusion1/processed/parameters.csv +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/exact.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/kernel.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/mean.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/poly_mean.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gradient_boosting.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/lightgbm.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/nn/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/radial_basis_functions.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/random_forest.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/registry.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/svm.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/transformed/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/transformed/base.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/transformed/delta_method.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/data/gaussian.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/data/spatiotemporal_dataset.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/fno.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/spatiotemporal.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/neural_processes/conditional_neural_process.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/feature_generation/polynomial_features.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/membership.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/pool.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/__init__.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/base.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/pca.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/standardize.py +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/dependency_links.txt +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/not-zip-safe +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/top_level.txt +0 -0
- {autoemulate-2.0.0 → autoemulate-2.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autoemulate
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: A python package for semi-automated emulation
|
|
5
5
|
Author: AutoEmulate contributors (see our GitHub page)
|
|
6
6
|
License-Expression: MIT
|
|
@@ -39,6 +39,7 @@ Requires-Dist: torchrbf>=0.0.1
|
|
|
39
39
|
Requires-Dist: arviz>=0.21.0
|
|
40
40
|
Requires-Dist: getdist>=1.7.2
|
|
41
41
|
Requires-Dist: einops>=0.8.1
|
|
42
|
+
Requires-Dist: harmonic>=1.3.0
|
|
42
43
|
Provides-Extra: docs
|
|
43
44
|
Requires-Dist: furo>=2023.9.10; extra == "docs"
|
|
44
45
|
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "docs"
|
|
@@ -59,12 +60,13 @@ Requires-Dist: coverage>=7.6.4; extra == "dev"
|
|
|
59
60
|
Requires-Dist: plotnine>=0.13.6; extra == "dev"
|
|
60
61
|
Requires-Dist: pyright==1.1.405; extra == "dev"
|
|
61
62
|
Requires-Dist: ruff==0.12.11; extra == "dev"
|
|
63
|
+
Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
|
|
62
64
|
Provides-Extra: spatiotemporal
|
|
63
65
|
Requires-Dist: the-well>=1.1.0; extra == "spatiotemporal"
|
|
64
66
|
Requires-Dist: neuraloperator>=1.0.2; extra == "spatiotemporal"
|
|
65
67
|
Dynamic: license-file
|
|
66
68
|
|
|
67
|
-
# AutoEmulate <a href="https://alan-turing-institute.github.io/autoemulate/"><img src="https://raw.githubusercontent.com/alan-turing-institute/autoemulate/refs/heads/main/misc/
|
|
69
|
+
# AutoEmulate <a href="https://alan-turing-institute.github.io/autoemulate/"><img src="https://raw.githubusercontent.com/alan-turing-institute/autoemulate/refs/heads/main/misc/AE.png" align="right" height="138" /></a>
|
|
68
70
|
|
|
69
71
|

|
|
70
72
|
[](https://codecov.io/gh/alan-turing-institute/autoemulate)
|
|
@@ -139,6 +141,10 @@ You can find the project documentation [here](https://alan-turing-institute.gith
|
|
|
139
141
|
</tr>
|
|
140
142
|
<tr>
|
|
141
143
|
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/lcm98/"><img src="https://avatars.githubusercontent.com/u/102757707?v=4?s=100" width="100px;" alt="Loh Chun Mun"/><br /><sub><b>Loh Chun Mun</b></sub></a><br /><a href="#doc-cmsamaaa" title="Documentation">📖</a> <a href="#code-cmsamaaa" title="Code">💻</a></td>
|
|
144
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/z7076"><img src="https://avatars.githubusercontent.com/u/69681924?v=4?s=100" width="100px;" alt="Jichen Zhang"/><br /><sub><b>Jichen Zhang</b></sub></a><br /><a href="#code-z7076" title="Code">💻</a></td>
|
|
145
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RemDelaporteMathurin"><img src="https://avatars.githubusercontent.com/u/40028739?v=4?s=100" width="100px;" alt="Rémi Delaporte-Mathurin"/><br /><sub><b>Rémi Delaporte-Mathurin</b></sub></a><br /><a href="#bug-RemDelaporteMathurin" title="Bug reports">🐛</a></td>
|
|
146
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pmeier"><img src="https://avatars.githubusercontent.com/u/6849766?v=4?s=100" width="100px;" alt="Philip Meier"/><br /><sub><b>Philip Meier</b></sub></a><br /><a href="#bug-pmeier" title="Bug reports">🐛</a></td>
|
|
147
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/penelopeysm"><img src="https://avatars.githubusercontent.com/u/122629585?v=4?s=100" width="100px;" alt="Penelope Yong"/><br /><sub><b>Penelope Yong</b></sub></a><br /><a href="#ideas-penelopeysm" title="Ideas, Planning, & Feedback">🤔</a> <a href="#review-penelopeysm" title="Reviewed Pull Requests">👀</a> <a href="#code-penelopeysm" title="Code">💻</a> <a href="#doc-penelopeysm" title="Documentation">📖</a></td>
|
|
142
148
|
</tr>
|
|
143
149
|
</tbody>
|
|
144
150
|
</table>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# AutoEmulate <a href="https://alan-turing-institute.github.io/autoemulate/"><img src="https://raw.githubusercontent.com/alan-turing-institute/autoemulate/refs/heads/main/misc/
|
|
1
|
+
# AutoEmulate <a href="https://alan-turing-institute.github.io/autoemulate/"><img src="https://raw.githubusercontent.com/alan-turing-institute/autoemulate/refs/heads/main/misc/AE.png" align="right" height="138" /></a>
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
[](https://codecov.io/gh/alan-turing-institute/autoemulate)
|
|
@@ -73,6 +73,10 @@ You can find the project documentation [here](https://alan-turing-institute.gith
|
|
|
73
73
|
</tr>
|
|
74
74
|
<tr>
|
|
75
75
|
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/lcm98/"><img src="https://avatars.githubusercontent.com/u/102757707?v=4?s=100" width="100px;" alt="Loh Chun Mun"/><br /><sub><b>Loh Chun Mun</b></sub></a><br /><a href="#doc-cmsamaaa" title="Documentation">📖</a> <a href="#code-cmsamaaa" title="Code">💻</a></td>
|
|
76
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/z7076"><img src="https://avatars.githubusercontent.com/u/69681924?v=4?s=100" width="100px;" alt="Jichen Zhang"/><br /><sub><b>Jichen Zhang</b></sub></a><br /><a href="#code-z7076" title="Code">💻</a></td>
|
|
77
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RemDelaporteMathurin"><img src="https://avatars.githubusercontent.com/u/40028739?v=4?s=100" width="100px;" alt="Rémi Delaporte-Mathurin"/><br /><sub><b>Rémi Delaporte-Mathurin</b></sub></a><br /><a href="#bug-RemDelaporteMathurin" title="Bug reports">🐛</a></td>
|
|
78
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pmeier"><img src="https://avatars.githubusercontent.com/u/6849766?v=4?s=100" width="100px;" alt="Philip Meier"/><br /><sub><b>Philip Meier</b></sub></a><br /><a href="#bug-pmeier" title="Bug reports">🐛</a></td>
|
|
79
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/penelopeysm"><img src="https://avatars.githubusercontent.com/u/122629585?v=4?s=100" width="100px;" alt="Penelope Yong"/><br /><sub><b>Penelope Yong</b></sub></a><br /><a href="#ideas-penelopeysm" title="Ideas, Planning, & Feedback">🤔</a> <a href="#review-penelopeysm" title="Reviewed Pull Requests">👀</a> <a href="#code-penelopeysm" title="Code">💻</a> <a href="#doc-penelopeysm" title="Documentation">📖</a></td>
|
|
76
80
|
</tr>
|
|
77
81
|
</tbody>
|
|
78
82
|
</table>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
from collections.abc import Callable
|
|
3
2
|
|
|
4
3
|
import arviz as az
|
|
@@ -7,13 +6,15 @@ from getdist import MCSamples
|
|
|
7
6
|
from pyro.infer import HMC, MCMC, NUTS, Predictive
|
|
8
7
|
from pyro.infer.mcmc import RandomWalkKernel
|
|
9
8
|
|
|
9
|
+
from autoemulate.core.logging_config import get_logger
|
|
10
10
|
from autoemulate.core.types import TensorLike
|
|
11
11
|
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
class BayesianMixin:
|
|
14
16
|
"""Mixin class for Bayesian calibration methods."""
|
|
15
17
|
|
|
16
|
-
logger: logging.Logger
|
|
17
18
|
model: Callable
|
|
18
19
|
observations: dict[str, TensorLike] | None
|
|
19
20
|
|
|
@@ -28,12 +29,12 @@ class BayesianMixin:
|
|
|
28
29
|
model_kwargs = model_kwargs or {}
|
|
29
30
|
sampler = sampler.lower()
|
|
30
31
|
if sampler == "nuts":
|
|
31
|
-
|
|
32
|
+
logger.debug("Using NUTS kernel.")
|
|
32
33
|
return NUTS(self.model, **sampler_kwargs)
|
|
33
34
|
if sampler == "hmc":
|
|
34
35
|
step_size = sampler_kwargs.pop("step_size", 0.01)
|
|
35
36
|
trajectory_length = sampler_kwargs.pop("trajectory_length", 1.0)
|
|
36
|
-
|
|
37
|
+
logger.debug(
|
|
37
38
|
"Using HMC kernel with step_size=%s, trajectory_length=%s",
|
|
38
39
|
step_size,
|
|
39
40
|
trajectory_length,
|
|
@@ -45,9 +46,9 @@ class BayesianMixin:
|
|
|
45
46
|
**sampler_kwargs,
|
|
46
47
|
)
|
|
47
48
|
if sampler == "metropolis":
|
|
48
|
-
|
|
49
|
+
logger.debug("Using Metropolis (RandomWalkKernel).")
|
|
49
50
|
return RandomWalkKernel(self.model, **sampler_kwargs)
|
|
50
|
-
|
|
51
|
+
logger.error("Unknown sampler: %s", sampler)
|
|
51
52
|
raise ValueError(f"Unknown sampler: {sampler}")
|
|
52
53
|
|
|
53
54
|
def run_mcmc(
|
|
@@ -96,11 +97,9 @@ class BayesianMixin:
|
|
|
96
97
|
"An initial value must be provided for each chain, parameter "
|
|
97
98
|
f"{param} tensor only has {init_vals.shape[0]} values."
|
|
98
99
|
)
|
|
99
|
-
|
|
100
|
+
logger.error(msg)
|
|
100
101
|
raise ValueError(msg)
|
|
101
|
-
|
|
102
|
-
"Initial parameters provided for MCMC: %s", initial_params
|
|
103
|
-
)
|
|
102
|
+
logger.debug("Initial parameters provided for MCMC: %s", initial_params)
|
|
104
103
|
|
|
105
104
|
# Run NUTS
|
|
106
105
|
kernel = self._get_kernel(sampler, model_kwargs=model_kwargs, **sampler_kwargs)
|
|
@@ -114,9 +113,9 @@ class BayesianMixin:
|
|
|
114
113
|
# Multiprocessing
|
|
115
114
|
mp_context="spawn" if num_chains > 1 else None,
|
|
116
115
|
)
|
|
117
|
-
|
|
116
|
+
logger.info("Starting MCMC run.")
|
|
118
117
|
mcmc.run()
|
|
119
|
-
|
|
118
|
+
logger.info("MCMC run completed.")
|
|
120
119
|
return mcmc
|
|
121
120
|
|
|
122
121
|
def posterior_predictive(self, mcmc: MCMC) -> dict[str, TensorLike]:
|
|
@@ -136,7 +135,7 @@ class BayesianMixin:
|
|
|
136
135
|
posterior_samples = mcmc.get_samples()
|
|
137
136
|
posterior_predictive = Predictive(self.model, posterior_samples)
|
|
138
137
|
samples = posterior_predictive(predict=True)
|
|
139
|
-
|
|
138
|
+
logger.debug("Posterior predictive samples generated.")
|
|
140
139
|
return samples
|
|
141
140
|
|
|
142
141
|
def to_arviz(
|
|
@@ -158,13 +157,13 @@ class BayesianMixin:
|
|
|
158
157
|
"""
|
|
159
158
|
pp_samples = None
|
|
160
159
|
if posterior_predictive:
|
|
161
|
-
|
|
160
|
+
logger.info("Including posterior predictive samples in Arviz output.")
|
|
162
161
|
pp_samples = self.posterior_predictive(mcmc)
|
|
163
162
|
|
|
164
163
|
# Need to create dataset manually for Metropolis Hastings
|
|
165
164
|
# This is because az.from_pyro expects kernel with `divergences`
|
|
166
165
|
if isinstance(mcmc.kernel, RandomWalkKernel):
|
|
167
|
-
|
|
166
|
+
logger.debug(
|
|
168
167
|
"Using manual conversion for Metropolis (RandomWalkKernel) kernel."
|
|
169
168
|
)
|
|
170
169
|
if posterior_predictive:
|
|
@@ -173,7 +172,7 @@ class BayesianMixin:
|
|
|
173
172
|
"Observations must be provided to include observed_data in "
|
|
174
173
|
"Arviz InferenceData."
|
|
175
174
|
)
|
|
176
|
-
|
|
175
|
+
logger.error(msg)
|
|
177
176
|
raise ValueError(msg)
|
|
178
177
|
az_data = az.InferenceData(
|
|
179
178
|
posterior=az.convert_to_dataset(
|
|
@@ -189,10 +188,10 @@ class BayesianMixin:
|
|
|
189
188
|
),
|
|
190
189
|
)
|
|
191
190
|
else:
|
|
192
|
-
|
|
191
|
+
logger.debug("Using az.from_pyro for conversion.")
|
|
193
192
|
az_data = az.from_pyro(mcmc, posterior_predictive=pp_samples)
|
|
194
193
|
|
|
195
|
-
|
|
194
|
+
logger.info("Arviz InferenceData conversion complete.")
|
|
196
195
|
return az_data
|
|
197
196
|
|
|
198
197
|
@staticmethod
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
1
3
|
import pyro
|
|
2
4
|
import pyro.distributions as dist
|
|
3
5
|
import torch
|
|
6
|
+
from pyro import poutine
|
|
7
|
+
from pyro.infer import MCMC
|
|
4
8
|
|
|
5
9
|
from autoemulate.calibration.base import BayesianMixin
|
|
6
10
|
from autoemulate.core.device import TorchDeviceMixin
|
|
7
|
-
from autoemulate.core.logging_config import
|
|
11
|
+
from autoemulate.core.logging_config import _warn_deprecated_log_level, get_logger
|
|
8
12
|
from autoemulate.core.types import DeviceLike, TensorLike
|
|
9
13
|
from autoemulate.emulators.base import Emulator
|
|
10
14
|
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
11
17
|
|
|
12
18
|
class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
13
19
|
"""
|
|
@@ -27,7 +33,7 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
27
33
|
model_discrepancy: float = 0.0,
|
|
28
34
|
calibration_params: list[str] | None = None,
|
|
29
35
|
device: DeviceLike | None = None,
|
|
30
|
-
log_level: str =
|
|
36
|
+
log_level: str | None = None,
|
|
31
37
|
):
|
|
32
38
|
"""
|
|
33
39
|
Initialize the HMC calibration object.
|
|
@@ -58,14 +64,8 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
58
64
|
The device to use. If None, the default torch device is returned.
|
|
59
65
|
TODO: do we need to do anything more to ensure the device is correctly
|
|
60
66
|
handled for the pyro model?
|
|
61
|
-
log_level: str
|
|
62
|
-
|
|
63
|
-
- "progress_bar": shows a progress bar during batch simulations
|
|
64
|
-
- "debug": shows debug messages
|
|
65
|
-
- "info": shows informational messages
|
|
66
|
-
- "warning": shows warning messages
|
|
67
|
-
- "error": shows error messages
|
|
68
|
-
- "critical": shows critical messages
|
|
67
|
+
log_level: str | None
|
|
68
|
+
Deprecated. Configure logging in the calling application instead.
|
|
69
69
|
|
|
70
70
|
Notes
|
|
71
71
|
-----
|
|
@@ -81,10 +81,10 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
81
81
|
calibration_params = list(parameter_range.keys())
|
|
82
82
|
self.calibration_params = calibration_params
|
|
83
83
|
self.emulator = emulator
|
|
84
|
-
self.emulator.
|
|
84
|
+
self.emulator.to(self.device)
|
|
85
85
|
self.output_names = list(observations.keys())
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
_warn_deprecated_log_level(log_level)
|
|
87
|
+
logger.info(
|
|
88
88
|
"Initializing BayesianCalibration with parameters: %s",
|
|
89
89
|
self.calibration_params,
|
|
90
90
|
)
|
|
@@ -95,12 +95,12 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
95
95
|
for output, obs in observations.items():
|
|
96
96
|
if obs.ndim == 0:
|
|
97
97
|
corrected_obs = obs.unsqueeze(0)
|
|
98
|
-
|
|
98
|
+
logger.debug(
|
|
99
99
|
"Observation for output '%s' converted from 0D to 1D.",
|
|
100
100
|
output,
|
|
101
101
|
)
|
|
102
102
|
elif obs.ndim > 1:
|
|
103
|
-
|
|
103
|
+
logger.error("Tensor for output '%s' is not 1D.", output)
|
|
104
104
|
raise ValueError(f"Tensor for output '{output}' is not 1D.")
|
|
105
105
|
else:
|
|
106
106
|
corrected_obs = obs
|
|
@@ -108,11 +108,11 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
108
108
|
obs_lengths.append(corrected_obs.shape[0])
|
|
109
109
|
if len(set(obs_lengths)) != 1:
|
|
110
110
|
msg = "All outputs must have the same number of observations."
|
|
111
|
-
|
|
111
|
+
logger.error(msg)
|
|
112
112
|
raise ValueError(msg)
|
|
113
113
|
self.observations = processed_observations
|
|
114
114
|
self.n_observations = obs_lengths[0]
|
|
115
|
-
|
|
115
|
+
logger.info("Processed observations for outputs: %s", self.output_names)
|
|
116
116
|
|
|
117
117
|
# Save observation noise as {output: value} dictionary
|
|
118
118
|
if isinstance(observation_noise, float):
|
|
@@ -120,12 +120,12 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
120
120
|
self.output_names,
|
|
121
121
|
torch.tensor(observation_noise).to(self.device),
|
|
122
122
|
)
|
|
123
|
-
|
|
123
|
+
logger.debug(
|
|
124
124
|
"Observation noise (variance) set as float: %s", observation_noise
|
|
125
125
|
)
|
|
126
126
|
elif isinstance(observation_noise, dict):
|
|
127
127
|
self.observation_noise = observation_noise
|
|
128
|
-
|
|
128
|
+
logger.debug(
|
|
129
129
|
"Observation noise (variance) set as dict: %s", observation_noise
|
|
130
130
|
)
|
|
131
131
|
else:
|
|
@@ -133,7 +133,7 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
133
133
|
"Observation noise (variance) must be either a float or a dictionary "
|
|
134
134
|
"of floats."
|
|
135
135
|
)
|
|
136
|
-
|
|
136
|
+
logger.error(msg)
|
|
137
137
|
raise ValueError(msg)
|
|
138
138
|
|
|
139
139
|
self.model_uncertainty = model_uncertainty
|
|
@@ -186,8 +186,9 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
186
186
|
total_variance = (
|
|
187
187
|
observation_variance + self.model_discrepancy + variance[0, i]
|
|
188
188
|
if self.model_uncertainty and variance is not None
|
|
189
|
-
else torch.
|
|
190
|
-
self.
|
|
189
|
+
else torch.as_tensor(
|
|
190
|
+
observation_variance + self.model_discrepancy,
|
|
191
|
+
device=self.device,
|
|
191
192
|
)
|
|
192
193
|
)
|
|
193
194
|
# Take sqrt for final scale (stddev)
|
|
@@ -207,3 +208,137 @@ class BayesianCalibration(TorchDeviceMixin, BayesianMixin):
|
|
|
207
208
|
output,
|
|
208
209
|
dist.Normal(mean[0, i], scale),
|
|
209
210
|
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def extract_log_probabilities(
|
|
214
|
+
mcmc: MCMC,
|
|
215
|
+
model: Callable,
|
|
216
|
+
device: DeviceLike | None = None,
|
|
217
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
218
|
+
"""
|
|
219
|
+
Extract log probabilities from MCMC samples for evidence computation.
|
|
220
|
+
|
|
221
|
+
This function extracts posterior samples from a Pyro MCMC object and computes
|
|
222
|
+
the log probability of each sample under the given probabilistic model. The
|
|
223
|
+
results are formatted for use with evidence estimation methods like Harmonic.
|
|
224
|
+
|
|
225
|
+
Parameters
|
|
226
|
+
----------
|
|
227
|
+
mcmc : MCMC
|
|
228
|
+
Fitted Pyro MCMC object containing posterior samples. The MCMC object
|
|
229
|
+
should have been run with multiple chains for best results.
|
|
230
|
+
model : Callable
|
|
231
|
+
The Pyro probabilistic model used in MCMC sampling. This should be the
|
|
232
|
+
same model function passed to the MCMC kernel during sampling.
|
|
233
|
+
device : DeviceLike | None, optional
|
|
234
|
+
Device for tensor operations (e.g., 'cpu', 'cuda'). If None, uses the
|
|
235
|
+
default device. Default is None.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
tuple[torch.Tensor, torch.Tensor]
|
|
240
|
+
A tuple containing:
|
|
241
|
+
- samples: Tensor of shape (num_chains, num_samples_per_chain, ndim)
|
|
242
|
+
containing the posterior samples with parameters stacked in the last
|
|
243
|
+
dimension.
|
|
244
|
+
- log_probs: Tensor of shape (num_chains, num_samples_per_chain)
|
|
245
|
+
containing the log probability of each sample under the model.
|
|
246
|
+
|
|
247
|
+
Raises
|
|
248
|
+
------
|
|
249
|
+
ValueError
|
|
250
|
+
If the MCMC object has no samples or if sample extraction fails.
|
|
251
|
+
RuntimeError
|
|
252
|
+
If log probability computation fails for any sample.
|
|
253
|
+
|
|
254
|
+
Notes
|
|
255
|
+
-----
|
|
256
|
+
This function performs the following steps:
|
|
257
|
+
1. Extracts samples from the MCMC object grouped by chain
|
|
258
|
+
2. For each sample, conditions the model on the sampled parameter values
|
|
259
|
+
3. Traces the conditioned model to compute log probabilities
|
|
260
|
+
4. Returns samples and log probabilities in a format suitable for Harmonic
|
|
261
|
+
|
|
262
|
+
The log probabilities include contributions from both the prior and likelihood,
|
|
263
|
+
representing the unnormalized posterior density at each sample point.
|
|
264
|
+
|
|
265
|
+
Examples
|
|
266
|
+
--------
|
|
267
|
+
>>> from autoemulate.calibration import (
|
|
268
|
+
... BayesianCalibration,
|
|
269
|
+
... extract_log_probabilities,
|
|
270
|
+
... )
|
|
271
|
+
>>> # After running MCMC calibration
|
|
272
|
+
>>> bc = BayesianCalibration(emulator, param_range, observations)
|
|
273
|
+
>>> mcmc = bc.run_mcmc(num_samples=1000, num_chains=4)
|
|
274
|
+
>>> samples, log_probs = extract_log_probabilities(mcmc, bc.model)
|
|
275
|
+
>>> print(samples.shape) # (4, 1000, 2) for 2 parameters
|
|
276
|
+
>>> print(log_probs.shape) # (4, 1000)
|
|
277
|
+
|
|
278
|
+
See Also
|
|
279
|
+
--------
|
|
280
|
+
BayesianCalibration : Class for Bayesian calibration with MCMC
|
|
281
|
+
EvidenceComputation : Class for computing Bayesian evidence
|
|
282
|
+
|
|
283
|
+
"""
|
|
284
|
+
# Set device
|
|
285
|
+
if device is None:
|
|
286
|
+
device = torch.device("cpu")
|
|
287
|
+
elif isinstance(device, str):
|
|
288
|
+
device = torch.device(device)
|
|
289
|
+
|
|
290
|
+
# Extract samples grouped by chain
|
|
291
|
+
try:
|
|
292
|
+
samples = mcmc.get_samples(group_by_chain=True)
|
|
293
|
+
except Exception as e:
|
|
294
|
+
msg = f"Failed to extract samples from MCMC object: {e}"
|
|
295
|
+
raise ValueError(msg) from e
|
|
296
|
+
|
|
297
|
+
if not samples:
|
|
298
|
+
msg = "The MCMC object contains no samples"
|
|
299
|
+
raise ValueError(msg)
|
|
300
|
+
|
|
301
|
+
# Get dimensions
|
|
302
|
+
first_param = next(iter(samples.keys()))
|
|
303
|
+
num_chains = samples[first_param].shape[0]
|
|
304
|
+
num_samples_per_chain = samples[first_param].shape[1]
|
|
305
|
+
|
|
306
|
+
# Compute log probabilities for each sample
|
|
307
|
+
log_probs_list = []
|
|
308
|
+
|
|
309
|
+
for chain_idx in range(num_chains):
|
|
310
|
+
chain_log_probs = []
|
|
311
|
+
for sample_idx in range(num_samples_per_chain):
|
|
312
|
+
# Extract parameter values for this sample
|
|
313
|
+
sample_params = {k: v[chain_idx, sample_idx] for k, v in samples.items()}
|
|
314
|
+
|
|
315
|
+
# Condition the model on these parameter values
|
|
316
|
+
conditioned_model = pyro.condition(model, data=sample_params)
|
|
317
|
+
|
|
318
|
+
# Trace the model to get log probabilities
|
|
319
|
+
try:
|
|
320
|
+
trace = poutine.trace(conditioned_model).get_trace()
|
|
321
|
+
log_prob = trace.log_prob_sum()
|
|
322
|
+
# Convert to float, handling both tensor and scalar cases
|
|
323
|
+
if isinstance(log_prob, torch.Tensor):
|
|
324
|
+
log_prob_value = log_prob.item()
|
|
325
|
+
else:
|
|
326
|
+
log_prob_value = float(log_prob)
|
|
327
|
+
chain_log_probs.append(log_prob_value)
|
|
328
|
+
except Exception as e:
|
|
329
|
+
msg = (
|
|
330
|
+
f"Failed to compute log probability for chain {chain_idx}, "
|
|
331
|
+
f"sample {sample_idx}: {e}"
|
|
332
|
+
)
|
|
333
|
+
raise RuntimeError(msg) from e
|
|
334
|
+
|
|
335
|
+
log_probs_list.append(chain_log_probs)
|
|
336
|
+
|
|
337
|
+
# Convert samples to tensor: (num_chains, num_samples_per_chain, ndim)
|
|
338
|
+
# Stack parameters in the order they appear in the dictionary
|
|
339
|
+
samples_tensor = torch.stack([samples[k] for k in samples], dim=-1).to(device)
|
|
340
|
+
|
|
341
|
+
# Convert log probabilities to tensor: (num_chains, num_samples_per_chain)
|
|
342
|
+
log_probs_tensor = torch.tensor(log_probs_list, device=device)
|
|
343
|
+
|
|
344
|
+
return samples_tensor, log_probs_tensor
|