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.
Files changed (127) hide show
  1. {autoemulate-2.0.0 → autoemulate-2.1.1}/PKG-INFO +8 -2
  2. {autoemulate-2.0.0 → autoemulate-2.1.1}/README.md +5 -1
  3. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/__init__.py +3 -0
  4. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/base.py +17 -18
  5. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/bayes.py +157 -22
  6. autoemulate-2.1.1/autoemulate/calibration/evidence.py +526 -0
  7. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/history_matching.py +11 -10
  8. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/interval_excursion_set.py +15 -16
  9. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/compare.py +49 -42
  10. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/device.py +57 -3
  11. autoemulate-2.1.1/autoemulate/core/logging_config.py +77 -0
  12. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/model_selection.py +16 -11
  13. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/plotting.py +14 -2
  14. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/reinitialize.py +4 -1
  15. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/results.py +2 -3
  16. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/sensitivity_analysis.py +2 -2
  17. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/tuner.py +13 -10
  18. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/data/utils.py +7 -9
  19. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/base.py +9 -1
  20. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/ensemble.py +5 -1
  21. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/nn/mlp.py +6 -1
  22. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/polynomials.py +4 -1
  23. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/nn/gaussian_mlp.py +4 -1
  24. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/nn/zoib.py +4 -0
  25. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/base.py +12 -11
  26. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/stream.py +6 -1
  27. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/advection_diffusion.py +12 -4
  28. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/base.py +44 -24
  29. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/double_pendulum.py +19 -17
  30. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/epidemic.py +8 -2
  31. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/flow_problem.py +12 -10
  32. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/projectile.py +16 -4
  33. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/reaction_diffusion.py +12 -10
  34. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/seir.py +8 -2
  35. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/utils.py +40 -29
  36. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/vae.py +10 -5
  37. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/PKG-INFO +8 -2
  38. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/SOURCES.txt +1 -0
  39. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/requires.txt +2 -0
  40. {autoemulate-2.0.0 → autoemulate-2.1.1}/pyproject.toml +33 -1
  41. autoemulate-2.0.0/autoemulate/core/logging_config.py +0 -125
  42. {autoemulate-2.0.0 → autoemulate-2.1.1}/LICENSE +0 -0
  43. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/__init__.py +0 -0
  44. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/calibration/history_matching_dashboard.py +0 -0
  45. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/callbacks/early_stopping.py +0 -0
  46. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/__init__.py +0 -0
  47. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/metrics.py +0 -0
  48. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/save.py +0 -0
  49. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/core/types.py +0 -0
  50. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/__init__.py +0 -0
  51. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/description.md +0 -0
  52. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/processed/outputs.csv +0 -0
  53. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/processed/parameters.csv +0 -0
  54. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/raw/outputs.csv +0 -0
  55. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac1/raw/parameters.csv +0 -0
  56. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/description.md +0 -0
  57. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/processed/outputs.csv +0 -0
  58. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/processed/parameters.csv +0 -0
  59. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/raw/outputs.csv +0 -0
  60. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac2/raw/parameters.csv +0 -0
  61. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/description.md +0 -0
  62. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/processed/outputs.csv +0 -0
  63. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/processed/parameters.csv +0 -0
  64. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/raw/outputs.csv +0 -0
  65. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac3/raw/parameters.csv +0 -0
  66. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/description.md +0 -0
  67. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/processed/outputs.csv +0 -0
  68. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/processed/parameters.csv +0 -0
  69. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/raw/outputs.csv +0 -0
  70. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac4/raw/parameters.csv +0 -0
  71. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/description.md +0 -0
  72. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/processed/outputs.csv +0 -0
  73. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/processed/parameters.csv +0 -0
  74. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/raw/passive_mechanics/outputs.csv +0 -0
  75. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/raw/passive_mechanics/parameters.csv +0 -0
  76. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac5/raw/passive_mechanics.tar.xz +0 -0
  77. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/description.md +0 -0
  78. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/processed/outputs.csv +0 -0
  79. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/processed/parameters.csv +0 -0
  80. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/raw/outputs.csv +0 -0
  81. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/cardiac6/raw/parameters.csv +0 -0
  82. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/description.md +0 -0
  83. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/processed/outputs.csv +0 -0
  84. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/processed/parameters.csv +0 -0
  85. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/climate1/raw/Holden2018.csv +0 -0
  86. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/datasets.py +0 -0
  87. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/description.md +0 -0
  88. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/processed/outputs.csv +0 -0
  89. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/processed/parameters.csv +0 -0
  90. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/raw/Nervi_truss_datasets.zip +0 -0
  91. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/engineering1/raw/datasets/Sobol_CSV_data_1000.csv +0 -0
  92. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/reactiondiffusion1/processed/outputs.csv +0 -0
  93. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/datasets/reactiondiffusion1/processed/parameters.csv +0 -0
  94. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/__init__.py +0 -0
  95. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/__init__.py +0 -0
  96. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/exact.py +0 -0
  97. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/kernel.py +0 -0
  98. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/mean.py +0 -0
  99. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gaussian_process/poly_mean.py +0 -0
  100. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/gradient_boosting.py +0 -0
  101. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/lightgbm.py +0 -0
  102. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/nn/__init__.py +0 -0
  103. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/radial_basis_functions.py +0 -0
  104. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/random_forest.py +0 -0
  105. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/registry.py +0 -0
  106. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/svm.py +0 -0
  107. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/transformed/__init__.py +0 -0
  108. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/transformed/base.py +0 -0
  109. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/emulators/transformed/delta_method.py +0 -0
  110. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/data/gaussian.py +0 -0
  111. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/data/spatiotemporal_dataset.py +0 -0
  112. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/fno.py +0 -0
  113. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/emulators/spatiotemporal.py +0 -0
  114. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/experimental/neural_processes/conditional_neural_process.py +0 -0
  115. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/feature_generation/polynomial_features.py +0 -0
  116. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/__init__.py +0 -0
  117. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/membership.py +0 -0
  118. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/learners/pool.py +0 -0
  119. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/simulations/__init__.py +0 -0
  120. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/__init__.py +0 -0
  121. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/base.py +0 -0
  122. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/pca.py +0 -0
  123. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate/transforms/standardize.py +0 -0
  124. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/dependency_links.txt +0 -0
  125. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/not-zip-safe +0 -0
  126. {autoemulate-2.0.0 → autoemulate-2.1.1}/autoemulate.egg-info/top_level.txt +0 -0
  127. {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.0.0
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/AE_logo_final.png" align="right" height="138" /></a>
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
  ![CI](https://github.com/alan-turing-institute/autoemulate/actions/workflows/ci.yaml/badge.svg)
70
72
  [![codecov](https://codecov.io/gh/alan-turing-institute/autoemulate/graph/badge.svg?token=XD1HXQUIGK)](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/AE_logo_final.png" align="right" height="138" /></a>
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
  ![CI](https://github.com/alan-turing-institute/autoemulate/actions/workflows/ci.yaml/badge.svg)
4
4
  [![codecov](https://codecov.io/gh/alan-turing-institute/autoemulate/graph/badge.svg?token=XD1HXQUIGK)](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,3 +1,6 @@
1
1
  from .core.compare import AutoEmulate
2
+ from .core.logging_config import get_logger
3
+
4
+ get_logger()
2
5
 
3
6
  __all__ = ["AutoEmulate"]
@@ -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
- self.logger.debug("Using NUTS kernel.")
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
- self.logger.debug(
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
- self.logger.debug("Using Metropolis (RandomWalkKernel).")
49
+ logger.debug("Using Metropolis (RandomWalkKernel).")
49
50
  return RandomWalkKernel(self.model, **sampler_kwargs)
50
- self.logger.error("Unknown sampler: %s", sampler)
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
- self.logger.error(msg)
100
+ logger.error(msg)
100
101
  raise ValueError(msg)
101
- self.logger.debug(
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
- self.logger.info("Starting MCMC run.")
116
+ logger.info("Starting MCMC run.")
118
117
  mcmc.run()
119
- self.logger.info("MCMC run completed.")
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
- self.logger.debug("Posterior predictive samples generated.")
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
- self.logger.info("Including posterior predictive samples in Arviz output.")
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
- self.logger.debug(
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
- self.logger.error(msg)
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
- self.logger.debug("Using az.from_pyro for conversion.")
191
+ logger.debug("Using az.from_pyro for conversion.")
193
192
  az_data = az.from_pyro(mcmc, posterior_predictive=pp_samples)
194
193
 
195
- self.logger.info("Arviz InferenceData conversion complete.")
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 get_configured_logger
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 = "progress_bar",
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
- Logging level for the calibration. Can be one of:
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.device = self.device
84
+ self.emulator.to(self.device)
85
85
  self.output_names = list(observations.keys())
86
- self.logger, self.progress_bar = get_configured_logger(log_level)
87
- self.logger.info(
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
- self.logger.debug(
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
- self.logger.error("Tensor for output '%s' is not 1D.", output)
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
- self.logger.error(msg)
111
+ logger.error(msg)
112
112
  raise ValueError(msg)
113
113
  self.observations = processed_observations
114
114
  self.n_observations = obs_lengths[0]
115
- self.logger.info("Processed observations for outputs: %s", self.output_names)
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
- self.logger.debug(
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
- self.logger.debug(
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
- self.logger.error(msg)
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.tensor(observation_variance + self.model_discrepancy).to(
190
- self.device
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