From 142ff2f7510e2572823dc7d41fc0699aacca8f28 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Tue, 22 Apr 2025 10:53:55 +0000 Subject: [PATCH 01/46] [no ci] Add advice regarding moving from v1 to v2 to README. Raise awareness regarding missing features and incompatibility between the versions. Similar changes can be made for the docs. --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 3f3269754..64bccaff0 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,33 @@ It provides users and researchers with: BayesFlow (version 2+) is designed to be a flexible and efficient tool that enables rapid statistical inference fueled by continuous progress in generative AI and Bayesian inference. +## Migrating from BayesFlow 1.x to BayesFlow 2.0+ + +You are currently looking at BayesFlow 2.0+, which is a complete rewrite of the library. +While it shares the same overall goals with the 1.x versions, the API is not compatible. +You can find the most recent version of BayesFlow 1.x on the `stable-legacy` branch. +The latest [BayesFlow 1.x documentation](https://bayesflow.org/stable-legacy/index.html) can be accessed by selecting the "stable-legacy" entry in the version picker of the documentation. + +> [!CAUTION] +> You should not upgrade (yet) if one of the following applies: +> +> - You have an ongoing project that uses BayesFlow 1.x, and you do not want to allocate time for migrating to the new API. +> - You require a feature that was not ported to BayesFlow 2.0+ yet. To our knowledge, this applies to: +> * Two-level/Hierarchical models: `TwoLevelGenerativeModel`, `TwoLevelPrior`. +> * Sensitivity analysis: functionality from the `bayesflow.sensitivity` module. +> * MCMC (discontinued): The `bayesflow.mcmc` module. We are considering other options to enable the use of BayesFlow in an MCMC setting. +> * Networks: `EvidentialNetwork`. +> * Model misspecification detection: MMD test in the summary space (see #384). +> - You have already trained models in BayesFlow 1.x, that you do not want to re-train with the new version. Loading models from version 1.x in version 2.0+ is not supported. +> +> If you encounter any functionality that is missing and not listed here, please let us know by opening an issue. + +The new version brings many features, like multi-backend support via Keras3, and improved modularity and extensibility. +We recommend to upgrade if none of the above conditions apply. +Continue reading below for installation instructions and examples to get started. +The [Moving from BayesFlow v1.1 to v2.0](examples/From_BayesFlow_1.1_to_2.0.ipynb) guide highlights how concepts and classes relate between the two versions. +For additional information, please refer to the [FAQ](#faq) below. + ## Conceptual Overview
From 9a2f338fcd012fddd33a6e1b923d8b2dd451f496 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Tue, 22 Apr 2025 11:20:22 +0000 Subject: [PATCH 02/46] [no ci] Add link to migration advice to docs, add info about v1 --- docsrc/source/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docsrc/source/index.md b/docsrc/source/index.md index 36387c8fd..1dd1d5869 100644 --- a/docsrc/source/index.md +++ b/docsrc/source/index.md @@ -10,6 +10,9 @@ It provides users and researchers with: BayesFlow (version 2+) is designed to be a flexible and efficient tool that enables rapid statistical inference fueled by continuous progress in generative AI and Bayesian inference. +To access the documentation for [BayesFlow version 1.x](https://github.com/bayesflow-org/bayesflow/tree/stable-legacy), select `stable-legacy` in the version picker above. +For advice on the migration from version 1.x to version 2+, please refer to the [README](https://github.com/bayesflow-org/bayesflow/blob/main/README.md). + ## Conceptual Overview
From 688f22ca31d404c09a84976ed754073c9a960fba Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Wed, 23 Apr 2025 11:48:01 +0000 Subject: [PATCH 03/46] [no ci] notebook tests: increase timeout, fix platform/backend dependent code Torch is very slow, so I had to increase the timeout accordingly. --- examples/From_ABC_to_BayesFlow.ipynb | 9 +++++++-- examples/SIR_Posterior_Estimation.ipynb | 6 +++++- examples/Two_Moons_Starter.ipynb | 6 +++++- tests/utils/jupyter.py | 4 ++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/From_ABC_to_BayesFlow.ipynb b/examples/From_ABC_to_BayesFlow.ipynb index 334447555..b9757d9c4 100644 --- a/examples/From_ABC_to_BayesFlow.ipynb +++ b/examples/From_ABC_to_BayesFlow.ipynb @@ -38,7 +38,10 @@ "outputs": [], "source": [ "import numpy as np\n", - "import matplotlib.pyplot as plt" + "import matplotlib.pyplot as plt\n", + "import tempfile\n", + "from pathlib import Path\n", + "import platform" ] }, { @@ -322,7 +325,9 @@ ")\n", "\n", "# generate a temporary SQLite DB\n", - "abc_id = abc.new(\"sqlite:////tmp/mjp.db\", observations)" + "prefix = \"sqlite:///\" if platform.system() == \"Windows\" else \"sqlite:////\"\n", + "db_path = (Path(tempfile.gettempdir()).absolute() / \"mjp.db\").as_uri().replace(\"file:///\", prefix)\n", + "abc_id = abc.new(db_path, observations)" ] }, { diff --git a/examples/SIR_Posterior_Estimation.ipynb b/examples/SIR_Posterior_Estimation.ipynb index cadc597aa..c7dafa37f 100644 --- a/examples/SIR_Posterior_Estimation.ipynb +++ b/examples/SIR_Posterior_Estimation.ipynb @@ -19,7 +19,11 @@ "source": [ "import os\n", "# Set to your favorite backend\n", - "os.environ[\"KERAS_BACKEND\"] = \"jax\"" + "if \"KERAS_BACKEND\" not in os.environ:\n", + " # set this to \"torch\", \"tensorflow\", or \"jax\"\n", + " os.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\n", + "else:\n", + " print(f\"Using '{os.environ['KERAS_BACKEND']}' backend\")" ] }, { diff --git a/examples/Two_Moons_Starter.ipynb b/examples/Two_Moons_Starter.ipynb index 8fbb1d179..0d87f99c2 100644 --- a/examples/Two_Moons_Starter.ipynb +++ b/examples/Two_Moons_Starter.ipynb @@ -24,7 +24,11 @@ "source": [ "import os\n", "# Set to your favorite backend\n", - "os.environ[\"KERAS_BACKEND\"] = \"jax\"" + "if \"KERAS_BACKEND\" not in os.environ:\n", + " # set this to \"torch\", \"tensorflow\", or \"jax\"\n", + " os.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\n", + "else:\n", + " print(f\"Using '{os.environ['KERAS_BACKEND']}' backend\")" ] }, { diff --git a/tests/utils/jupyter.py b/tests/utils/jupyter.py index f905e1a0c..9a3b8d699 100644 --- a/tests/utils/jupyter.py +++ b/tests/utils/jupyter.py @@ -10,10 +10,10 @@ def run_notebook(path): checkpoint_path = path.parent / "checkpoints" # only clean up if the directory did not exist before the test cleanup_checkpoints = not checkpoint_path.exists() - with open(str(path)) as f: + with open(str(path), encoding="utf-8") as f: nb = nbformat.read(f, nbformat.NO_CONVERT) - kernel = ExecutePreprocessor(timeout=600, kernel_name="python3", resources={"metadata": {"path": path.parent}}) + kernel = ExecutePreprocessor(timeout=3600, kernel_name="python3", resources={"metadata": {"path": path.parent}}) try: result = kernel.preprocess(nb) From de300092875ff824283fef23fa6df8070fcd8940 Mon Sep 17 00:00:00 2001 From: Valentin Pratz <112951103+vpratz@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:39:02 +0200 Subject: [PATCH 04/46] Enable use of summary networks with functional API again (#434) * summary networks: add tests for using functional API * fix build functions for use with functional API --- bayesflow/links/ordered.py | 2 ++ bayesflow/networks/summary_network.py | 1 + bayesflow/networks/transformers/mab.py | 3 +++ bayesflow/networks/transformers/pma.py | 2 ++ bayesflow/networks/transformers/sab.py | 3 +++ bayesflow/utils/decorators.py | 7 +++++-- tests/test_networks/test_summary_networks.py | 22 ++++++++++++++++++++ 7 files changed, 38 insertions(+), 2 deletions(-) diff --git a/bayesflow/links/ordered.py b/bayesflow/links/ordered.py index 47be02317..77545b6f8 100644 --- a/bayesflow/links/ordered.py +++ b/bayesflow/links/ordered.py @@ -2,6 +2,7 @@ from keras.saving import register_keras_serializable as serializable from bayesflow.utils import layer_kwargs +from bayesflow.utils.decorators import sanitize_input_shape @serializable(package="links.ordered") @@ -49,5 +50,6 @@ def call(self, inputs): x = keras.ops.concatenate([below, anchor_input, above], self.axis) return x + @sanitize_input_shape def compute_output_shape(self, input_shape): return input_shape diff --git a/bayesflow/networks/summary_network.py b/bayesflow/networks/summary_network.py index 316df39e6..6e97c618f 100644 --- a/bayesflow/networks/summary_network.py +++ b/bayesflow/networks/summary_network.py @@ -21,6 +21,7 @@ def build(self, input_shape): if self.base_distribution is not None: self.base_distribution.build(keras.ops.shape(z)) + @sanitize_input_shape def compute_output_shape(self, input_shape): return keras.ops.shape(self.call(keras.ops.zeros(input_shape))) diff --git a/bayesflow/networks/transformers/mab.py b/bayesflow/networks/transformers/mab.py index a2e22da16..8f0e3f881 100644 --- a/bayesflow/networks/transformers/mab.py +++ b/bayesflow/networks/transformers/mab.py @@ -4,6 +4,7 @@ from bayesflow.networks import MLP from bayesflow.types import Tensor from bayesflow.utils import layer_kwargs +from bayesflow.utils.decorators import sanitize_input_shape from bayesflow.utils.serialization import serializable @@ -122,8 +123,10 @@ def call(self, seq_x: Tensor, seq_y: Tensor, training: bool = False, **kwargs) - return out # noinspection PyMethodOverriding + @sanitize_input_shape def build(self, seq_x_shape, seq_y_shape): self.call(keras.ops.zeros(seq_x_shape), keras.ops.zeros(seq_y_shape)) + @sanitize_input_shape def compute_output_shape(self, seq_x_shape, seq_y_shape): return keras.ops.shape(self.call(keras.ops.zeros(seq_x_shape), keras.ops.zeros(seq_y_shape))) diff --git a/bayesflow/networks/transformers/pma.py b/bayesflow/networks/transformers/pma.py index 5eb6a269d..956c85b48 100644 --- a/bayesflow/networks/transformers/pma.py +++ b/bayesflow/networks/transformers/pma.py @@ -4,6 +4,7 @@ from bayesflow.networks import MLP from bayesflow.types import Tensor from bayesflow.utils import layer_kwargs +from bayesflow.utils.decorators import sanitize_input_shape from bayesflow.utils.serialization import serializable from .mab import MultiHeadAttentionBlock @@ -125,5 +126,6 @@ def call(self, input_set: Tensor, training: bool = False, **kwargs) -> Tensor: summaries = self.mab(seed_tiled, set_x_transformed, training=training, **kwargs) return ops.reshape(summaries, (ops.shape(summaries)[0], -1)) + @sanitize_input_shape def compute_output_shape(self, input_shape): return keras.ops.shape(self.call(keras.ops.zeros(input_shape))) diff --git a/bayesflow/networks/transformers/sab.py b/bayesflow/networks/transformers/sab.py index a69dc5fa4..a447d92a2 100644 --- a/bayesflow/networks/transformers/sab.py +++ b/bayesflow/networks/transformers/sab.py @@ -1,6 +1,7 @@ import keras from bayesflow.types import Tensor +from bayesflow.utils.decorators import sanitize_input_shape from bayesflow.utils.serialization import serializable from .mab import MultiHeadAttentionBlock @@ -16,6 +17,7 @@ class SetAttentionBlock(MultiHeadAttentionBlock): """ # noinspection PyMethodOverriding + @sanitize_input_shape def build(self, input_set_shape): self.call(keras.ops.zeros(input_set_shape)) @@ -42,5 +44,6 @@ def call(self, input_set: Tensor, training: bool = False, **kwargs) -> Tensor: return super().call(input_set, input_set, training=training, **kwargs) # noinspection PyMethodOverriding + @sanitize_input_shape def compute_output_shape(self, input_set_shape): return keras.ops.shape(self.call(keras.ops.zeros(input_set_shape))) diff --git a/bayesflow/utils/decorators.py b/bayesflow/utils/decorators.py index 91afc9fb7..7fd32edc9 100644 --- a/bayesflow/utils/decorators.py +++ b/bayesflow/utils/decorators.py @@ -114,7 +114,7 @@ def callback(x): def sanitize_input_shape(fn: Callable): - """Decorator to replace the first dimension in input_shape with a dummy batch size if it is None""" + """Decorator to replace the first dimension in ..._shape arguments with a dummy batch size if it is None""" # The Keras functional API passes input_shape = (None, second_dim, third_dim, ...), which # causes problems when constructions like self.call(keras.ops.zeros(input_shape)) are used @@ -126,5 +126,8 @@ def callback(input_shape: Shape) -> Shape: return tuple(input_shape) return input_shape - fn = argument_callback("input_shape", callback)(fn) + args = inspect.getfullargspec(fn).args + for arg in args: + if arg.endswith("_shape"): + fn = argument_callback(arg, callback)(fn) return fn diff --git a/tests/test_networks/test_summary_networks.py b/tests/test_networks/test_summary_networks.py index 082ce4d25..50e1726c1 100644 --- a/tests/test_networks/test_summary_networks.py +++ b/tests/test_networks/test_summary_networks.py @@ -25,6 +25,28 @@ def test_build(automatic, summary_network, random_set): assert summary_network.variables, "Model has no variables." +@pytest.mark.parametrize("automatic", [True, False]) +def test_build_functional_api(automatic, summary_network, random_set): + if summary_network is None: + pytest.skip(reason="Nothing to do, because there is no summary network.") + + assert summary_network.built is False + + inputs = keras.layers.Input(shape=keras.ops.shape(random_set)[1:]) + outputs = summary_network(inputs) + model = keras.Model(inputs=inputs, outputs=outputs) + + if automatic: + model(random_set) + else: + model.build(keras.ops.shape(random_set)) + + assert model.built is True + + # check the model has variables + assert summary_network.variables, "Model has no variables." + + def test_variable_batch_size(summary_network, random_set): if summary_network is None: pytest.skip(reason="Nothing to do, because there is no summary network.") From 0eefa695751511e0e7690e56942bfd53e41d1efa Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Fri, 25 Apr 2025 08:25:36 +0000 Subject: [PATCH 05/46] [no ci] docs: add GitHub and Discourse links, reorder navbar --- docsrc/source/conf.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docsrc/source/conf.py b/docsrc/source/conf.py index cfbc931b9..4a21b3711 100644 --- a/docsrc/source/conf.py +++ b/docsrc/source/conf.py @@ -141,7 +141,29 @@ "image_light": "_static/bayesflow_hor.png", "image_dark": "_static/bayesflow_hor_dark.png", }, - "navbar_center": ["version-switcher", "navbar-nav"], + "icon_links_label": "Icon Links", + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/bayesflow-org/bayesflow", + "icon": "fa-brands fa-square-github", + "type": "fontawesome", + }, + { + "name": "Discourse Forum", + "url": "https://discuss.bayesflow.org/", + "icon": "fa-brands fa-discourse", + "type": "fontawesome", + }, + ], + "navbar_align": "left", + # -- Template placement in theme layouts ---------------------------------- + "navbar_start": ["navbar-logo"], + # Note that the alignment of navbar_center is controlled by navbar_align + "navbar_center": ["navbar-nav"], + "navbar_end": ["theme-switcher", "navbar-icon-links", "version-switcher"], + # navbar_persistent is persistent right (even when on mobiles) + "navbar_persistent": ["search-button"], "switcher": { "json_url": "/versions.json", "version_match": current, From a97b5a2fc5f120505e43d0d9c8e55e12a8a1ebf6 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Fri, 25 Apr 2025 08:27:47 +0000 Subject: [PATCH 06/46] [no ci] docs: acknowledge scikit-learn website --- docsrc/source/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docsrc/source/index.md b/docsrc/source/index.md index f377318db..ef0675f78 100644 --- a/docsrc/source/index.md +++ b/docsrc/source/index.md @@ -237,6 +237,8 @@ If you are interested in a curated list of resources, including reviews, softwar This project is currently managed by researchers from Rensselaer Polytechnic Institute, TU Dortmund University, and Heidelberg University. It is partially funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation, Project 528702768). The project is further supported by Germany's Excellence Strategy -- EXC-2075 - 390740016 (Stuttgart Cluster of Excellence SimTech) and EXC-2181 - 390900948 (Heidelberg Cluster of Excellence STRUCTURES), as well as the Informatics for Life initiative funded by the Klaus Tschira Foundation. +The [scikit-learn](https://scikit-learn.org/) website was a great resource and inspration for this site and the API documentation. We thank the scikit-learn community for sharing their configurations, which allowed us to include many nice features into this site as well. + ## License \& Source Code BayesFlow is released under {mainbranch}`MIT License `. From 8ac8aa314fb4a74bedf64a02533411052abb1935 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Fri, 25 Apr 2025 08:33:37 +0000 Subject: [PATCH 07/46] [no ci] docs: capitalize navigation headings --- docsrc/source/about.rst | 2 +- docsrc/source/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docsrc/source/about.rst b/docsrc/source/about.rst index e42c2e0b8..67ca0e102 100644 --- a/docsrc/source/about.rst +++ b/docsrc/source/about.rst @@ -1,4 +1,4 @@ -About us +About Us ======== Core maintainers diff --git a/docsrc/source/index.md b/docsrc/source/index.md index ef0675f78..f89c5ff3f 100644 --- a/docsrc/source/index.md +++ b/docsrc/source/index.md @@ -260,5 +260,5 @@ examples api/bayesflow about Contributing -Developer docs +Developer Docs ``` From e590a4313896864c75d6e6e9712a4cc8022970fc Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Fri, 25 Apr 2025 11:15:52 +0200 Subject: [PATCH 08/46] [no ci] README: move details on migration to FAQ --- README.md | 62 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 64bccaff0..12162dfa3 100644 --- a/README.md +++ b/README.md @@ -18,28 +18,14 @@ fueled by continuous progress in generative AI and Bayesian inference. You are currently looking at BayesFlow 2.0+, which is a complete rewrite of the library. While it shares the same overall goals with the 1.x versions, the API is not compatible. -You can find the most recent version of BayesFlow 1.x on the `stable-legacy` branch. -The latest [BayesFlow 1.x documentation](https://bayesflow.org/stable-legacy/index.html) can be accessed by selecting the "stable-legacy" entry in the version picker of the documentation. > [!CAUTION] -> You should not upgrade (yet) if one of the following applies: -> -> - You have an ongoing project that uses BayesFlow 1.x, and you do not want to allocate time for migrating to the new API. -> - You require a feature that was not ported to BayesFlow 2.0+ yet. To our knowledge, this applies to: -> * Two-level/Hierarchical models: `TwoLevelGenerativeModel`, `TwoLevelPrior`. -> * Sensitivity analysis: functionality from the `bayesflow.sensitivity` module. -> * MCMC (discontinued): The `bayesflow.mcmc` module. We are considering other options to enable the use of BayesFlow in an MCMC setting. -> * Networks: `EvidentialNetwork`. -> * Model misspecification detection: MMD test in the summary space (see #384). -> - You have already trained models in BayesFlow 1.x, that you do not want to re-train with the new version. Loading models from version 1.x in version 2.0+ is not supported. -> -> If you encounter any functionality that is missing and not listed here, please let us know by opening an issue. - -The new version brings many features, like multi-backend support via Keras3, and improved modularity and extensibility. -We recommend to upgrade if none of the above conditions apply. -Continue reading below for installation instructions and examples to get started. -The [Moving from BayesFlow v1.1 to v2.0](examples/From_BayesFlow_1.1_to_2.0.ipynb) guide highlights how concepts and classes relate between the two versions. -For additional information, please refer to the [FAQ](#faq) below. +> A few features, most notably hierarchical models, have not been ported to BayesFlow 2.0+ +> yet. We are working on those features and plan to add them soon. You can find the complete +> list in the [FAQ](#faq) below. + +The [Moving from BayesFlow v1.1 to v2.0](examples/From_BayesFlow_1.1_to_2.0.ipynb) guide +highlights how concepts and classes relate between the two versions. ## Conceptual Overview @@ -242,11 +228,47 @@ while the old version was based on TensorFlow. ------------- +**Question:** +Should I switch to BayesFlow 2.0+ now? Are there features that are still missing? + +**Answer:** +In general, we recommend to switch, as the new version is easier to use and will continue +to receive improvements and new features. However, a few features are still missing, so you +might want to wait until everything you need has been ported to BayesFlow 2.0+. + +Depending on your needs, you might not want to upgrade yet if one of the following applies: + +- You have an ongoing project that uses BayesFlow 1.x, and you do not want to allocate + time for migrating it to the new API. +- You have already trained models in BayesFlow 1.x, that you do not want to re-train + with the new version. Loading models from version 1.x in version 2.0+ is not supported. +- You require a feature that was not ported to BayesFlow 2.0+ yet. To our knowledge, + this applies to: + * Two-level/Hierarchical models: `TwoLevelGenerativeModel`, `TwoLevelPrior`. + * Sensitivity analysis: functionality from the `bayesflow.sensitivity` module. + * MCMC (discontinued): The `bayesflow.mcmc` module. We are considering other options + to enable the use of BayesFlow in an MCMC setting. + * Networks: `EvidentialNetwork`. + * Model misspecification detection: MMD test in the summary space (see #384). + +If you encounter any functionality that is missing and not listed here, please let us +know by opening an issue. + +------------- + **Question:** I still need the old BayesFlow for some of my projects. How can I install it? **Answer:** You can find and install the old Bayesflow version via the `stable-legacy` branch on GitHub. +The corresponding [documentation](https://bayesflow.org/stable-legacy/index.html) can be +accessed by selecting the "stable-legacy" entry in the version picker of the documentation. + +You can also install the latest version of BayesFlow v1.x from PyPI using + +``` +$ pip install "bayesflow<2.0" +``` ------------- From 076ceafe8049f127bc132355e4baf763eb95779f Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Fri, 25 Apr 2025 09:19:25 +0000 Subject: [PATCH 09/46] [no ci] Change heading on migration section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12162dfa3..68ea950da 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ It provides users and researchers with: BayesFlow (version 2+) is designed to be a flexible and efficient tool that enables rapid statistical inference fueled by continuous progress in generative AI and Bayesian inference. -## Migrating from BayesFlow 1.x to BayesFlow 2.0+ +## Important Note for Existing Users You are currently looking at BayesFlow 2.0+, which is a complete rewrite of the library. While it shares the same overall goals with the 1.x versions, the API is not compatible. From 53dda82c7849b1ccfa21431ad1d440f9b7d01b39 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Fri, 25 Apr 2025 09:20:58 +0000 Subject: [PATCH 10/46] [no ci] docs: remove $ prefix from v1 install command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68ea950da..a147dbf4d 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ accessed by selecting the "stable-legacy" entry in the version picker of the doc You can also install the latest version of BayesFlow v1.x from PyPI using ``` -$ pip install "bayesflow<2.0" +pip install "bayesflow<2.0" ``` ------------- From 7ea287f08f452a0a0202c60e1fecd7c4ae505c5c Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 25 Apr 2025 08:36:13 -0400 Subject: [PATCH 11/46] More tests (#437) * fix docs of coupling flow * add additional tests --- .../networks/coupling_flow/coupling_flow.py | 2 +- .../test_coupling_flow/test_permutations.py | 117 ++++++++++++++++++ tests/test_networks/test_embeddings.py | 85 +++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 tests/test_networks/test_coupling_flow/test_permutations.py create mode 100644 tests/test_networks/test_embeddings.py diff --git a/bayesflow/networks/coupling_flow/coupling_flow.py b/bayesflow/networks/coupling_flow/coupling_flow.py index c7b528987..ee78f180e 100644 --- a/bayesflow/networks/coupling_flow/coupling_flow.py +++ b/bayesflow/networks/coupling_flow/coupling_flow.py @@ -77,7 +77,7 @@ def __init__( The type of transformation used in the coupling layers, such as "affine". Default is "affine". permutation : str or None, optional - The type of permutation applied between layers. Can be "random" or None + The type of permutation applied between layers. Can be "orthogonal", "random", "swap", or None (no permutation). Default is "random". use_actnorm : bool, optional Whether to apply ActNorm before each coupling layer. Default is True. diff --git a/tests/test_networks/test_coupling_flow/test_permutations.py b/tests/test_networks/test_coupling_flow/test_permutations.py new file mode 100644 index 000000000..63c50ae2c --- /dev/null +++ b/tests/test_networks/test_coupling_flow/test_permutations.py @@ -0,0 +1,117 @@ +import pytest +import keras +import numpy as np + +from bayesflow.networks.coupling_flow.permutations import ( + FixedPermutation, + OrthogonalPermutation, + RandomPermutation, + Swap, +) + + +@pytest.fixture(params=[FixedPermutation, OrthogonalPermutation, RandomPermutation, Swap]) +def permutation_class(request): + return request.param + + +@pytest.fixture +def input_tensor(): + return keras.random.normal((2, 5)) + + +def test_fixed_permutation_build_and_call(): + # Since FixedPermutation is abstract, create a subclass for testing build. + class TestPerm(FixedPermutation): + def build(self, xz_shape, **kwargs): + length = xz_shape[-1] + self.forward_indices = keras.ops.arange(length - 1, -1, -1) + self.inverse_indices = keras.ops.arange(length - 1, -1, -1) + + layer = TestPerm() + input_shape = (2, 4) + layer.build(input_shape) + + x = keras.ops.convert_to_tensor(np.arange(8).reshape(input_shape).astype("float32")) + z, log_det = layer(x, inverse=False) + x_inv, log_det_inv = layer(z, inverse=True) + + # Check shape preservation + assert z.shape == x.shape + assert x_inv.shape == x.shape + # Forward then inverse recovers input + np.testing.assert_allclose(keras.ops.convert_to_numpy(x_inv), keras.ops.convert_to_numpy(x), atol=1e-5) + # log_det values should be zero tensors with the correct shape + assert tuple(log_det.shape) == input_shape[:-1] + assert tuple(log_det_inv.shape) == input_shape[:-1] + + +def test_orthogonal_permutation_build_and_call(input_tensor): + layer = OrthogonalPermutation() + input_shape = keras.ops.shape(input_tensor) + layer.build(input_shape) + + z, log_det = layer(input_tensor) + x_inv, log_det_inv = layer(z, inverse=True) + + # Check output shapes + assert z.shape == input_tensor.shape + assert x_inv.shape == input_tensor.shape + + # Forward + inverse should approximately recover input (allow some numeric tolerance) + np.testing.assert_allclose( + keras.ops.convert_to_numpy(x_inv), keras.ops.convert_to_numpy(input_tensor), rtol=1e-5, atol=1e-5 + ) + + # log_det should be scalar or batched scalar + if len(log_det.shape) > 0: + assert log_det.shape[0] == input_tensor.shape[0] # batch dim + else: + assert log_det.shape == () + + # log_det_inv should be negative of log_det (det(inv) = 1/det) + log_det_np = keras.ops.convert_to_numpy(log_det) + log_det_inv_np = keras.ops.convert_to_numpy(log_det_inv) + np.testing.assert_allclose(log_det_inv_np, -log_det_np, rtol=1e-5, atol=1e-5) + + +def test_random_permutation_build_and_call(input_tensor): + layer = RandomPermutation() + input_shape = keras.ops.shape(input_tensor) + layer.build(input_shape) + + # Assert forward_indices and inverse_indices are set and consistent + fwd = keras.ops.convert_to_numpy(layer.forward_indices) + inv = keras.ops.convert_to_numpy(layer.inverse_indices) + # Applying inv on fwd must yield ordered indices + reordered = fwd[inv] + np.testing.assert_array_equal(np.arange(len(fwd)), reordered) + + z, log_det = layer(input_tensor) + x_inv, log_det_inv = layer(z, inverse=True) + + assert z.shape == input_tensor.shape + assert x_inv.shape == input_tensor.shape + np.testing.assert_allclose(keras.ops.convert_to_numpy(x_inv), keras.ops.convert_to_numpy(input_tensor), atol=1e-5) + assert tuple(log_det.shape) == input_shape[:-1] + assert tuple(log_det_inv.shape) == input_shape[:-1] + + +def test_swap_build_and_call(input_tensor): + layer = Swap() + input_shape = keras.ops.shape(input_tensor) + layer.build(input_shape) + + fwd = keras.ops.convert_to_numpy(layer.forward_indices) + inv = keras.ops.convert_to_numpy(layer.inverse_indices) + reordered = fwd[inv] + np.testing.assert_array_equal(np.arange(len(fwd)), reordered) + + z, log_det = layer(input_tensor) + x_inv, log_det_inv = layer(z, inverse=True) + + assert z.shape == input_tensor.shape + assert x_inv.shape == input_tensor.shape + np.testing.assert_allclose(keras.ops.convert_to_numpy(x_inv), keras.ops.convert_to_numpy(input_tensor), atol=1e-5) + assert tuple(log_det.shape) == input_shape[:-1] + assert tuple(log_det_inv.shape) == input_shape[:-1] diff --git a/tests/test_networks/test_embeddings.py b/tests/test_networks/test_embeddings.py new file mode 100644 index 000000000..7385d94c0 --- /dev/null +++ b/tests/test_networks/test_embeddings.py @@ -0,0 +1,85 @@ +import pytest +import keras + +from bayesflow.networks.embeddings import ( + FourierEmbedding, + RecurrentEmbedding, + Time2Vec, +) + + +def test_fourier_embedding_output_shape_and_type(): + embed_dim = 8 + batch_size = 4 + + emb_layer = FourierEmbedding(embed_dim=embed_dim, include_identity=True) + # use keras.ops.zeros with shape (batch_size, 1) and float32 dtype + t = keras.ops.zeros((batch_size, 1), dtype="float32") + + emb = emb_layer(t) + # Expected shape is (batch_size, embed_dim + 1) if include_identity else (batch_size, embed_dim) + expected_dim = embed_dim + 1 + assert emb.shape[0] == batch_size + assert emb.shape[1] == expected_dim + # Check type - it should be a Keras tensor, convert to numpy for checking + np_emb = keras.ops.convert_to_numpy(emb) + assert np_emb.shape == (batch_size, expected_dim) + + +def test_fourier_embedding_without_identity(): + embed_dim = 8 + batch_size = 3 + + emb_layer = FourierEmbedding(embed_dim=embed_dim, include_identity=False) + t = keras.ops.zeros((batch_size, 1), dtype="float32") + + emb = emb_layer(t) + expected_dim = embed_dim + assert emb.shape[0] == batch_size + assert emb.shape[1] == expected_dim + + +def test_fourier_embedding_raises_for_odd_embed_dim(): + with pytest.raises(ValueError): + FourierEmbedding(embed_dim=7) + + +def test_recurrent_embedding_lstm_and_gru_shapes(): + batch_size = 2 + seq_len = 5 + dim = 3 + embed_dim = 6 + + # Dummy input + x = keras.ops.zeros((batch_size, seq_len, dim), dtype="float32") + + # lstm + lstm_layer = RecurrentEmbedding(embed_dim=embed_dim, embedding="lstm") + emb_lstm = lstm_layer(x) + # Check the concatenated shape: last dimension = original dim + embed_dim + assert emb_lstm.shape == (batch_size, seq_len, dim + embed_dim) + + # gru + gru_layer = RecurrentEmbedding(embed_dim=embed_dim, embedding="gru") + emb_gru = gru_layer(x) + assert emb_gru.shape == (batch_size, seq_len, dim + embed_dim) + + +def test_recurrent_embedding_raises_unknown_embedding(): + with pytest.raises(ValueError): + RecurrentEmbedding(embed_dim=4, embedding="unknown") + + +def test_time2vec_shapes_and_output(): + batch_size = 3 + seq_len = 7 + dim = 2 + num_periodic_features = 4 + + x = keras.ops.zeros((batch_size, seq_len, dim), dtype="float32") + time2vec_layer = Time2Vec(num_periodic_features=num_periodic_features) + + emb = time2vec_layer(x) + # The last dimension should be dim + num_periodic_features + 1 (trend + periodic) + expected_dim = dim + num_periodic_features + 1 + assert emb.shape == (batch_size, seq_len, expected_dim) From 42fa0358b1ceca48c3f6fdf868c6908ed2d77f0b Mon Sep 17 00:00:00 2001 From: Valentin Pratz <112951103+vpratz@users.noreply.github.com> Date: Fri, 25 Apr 2025 19:37:03 +0200 Subject: [PATCH 12/46] Automatically run slow tests when main is involved. (#438) In addition, this PR limits the slow test to Windows and Python 3.10. The choices are somewhat arbitrary, my thought was to test the setup not covered as much through use by the devs. --- .github/workflows/tests.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 41a254d1f..f90389f69 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -73,8 +73,11 @@ jobs: pytest -x -m "not slow" - name: Run Slow Tests - # run all slow tests only on manual trigger - if: github.event_name == 'workflow_dispatch' + # Run slow tests on manual trigger and pushes/PRs to main. + # Limit to one OS and Python version to save compute. + # Multiline if statements are weird, https://github.com/orgs/community/discussions/25641, + # but feel free to convert it. + if: ${{ ((github.event_name == 'workflow_dispatch') || (github.event_name == 'push' && github.ref_name == 'main') || (github.event_name == 'pull_request' && github.base_ref == 'main')) && ((matrix.os == 'windows-latest') && (matrix.python-version == '3.10')) }} run: | pytest -m "slow" From 86f2f5b31cb2f62847b0f5322bc4730ef8344c01 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 14:18:12 -0400 Subject: [PATCH 13/46] reintroduce symbolic tensor check in log_sinkhorn --- bayesflow/utils/optimal_transport/log_sinkhorn.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bayesflow/utils/optimal_transport/log_sinkhorn.py b/bayesflow/utils/optimal_transport/log_sinkhorn.py index 2a65d039e..3538eaeff 100644 --- a/bayesflow/utils/optimal_transport/log_sinkhorn.py +++ b/bayesflow/utils/optimal_transport/log_sinkhorn.py @@ -1,6 +1,7 @@ import keras from .. import logging +from ..tensor_utils import is_symbolic_tensor from .euclidean import euclidean @@ -26,6 +27,9 @@ def log_sinkhorn_plan(x1, x2, regularization: float = 1.0, rtol=1e-5, atol=1e-8, log_plan = cost / -(regularization * keras.ops.mean(cost) + 1e-16) + if is_symbolic_tensor(log_plan): + return log_plan + def contains_nans(plan): return keras.ops.any(keras.ops.isnan(plan)) From 206d70617057ae7763a25fec380c785c82b6333c Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 14:58:31 -0400 Subject: [PATCH 14/46] Update dispatch --- bayesflow/distributions/diagonal_normal.py | 47 ++++++++-------- bayesflow/distributions/diagonal_student_t.py | 54 +++++++++---------- bayesflow/distributions/mixture.py | 28 ++++++---- bayesflow/utils/dispatch/__init__.py | 3 ++ .../dispatch}/find_distribution.py | 17 ++++-- .../utils/dispatch/find_inference_network.py | 39 ++++++++++++++ .../utils/dispatch/find_summary_network.py | 49 +++++++++++++++++ bayesflow/utils/workflow_utils.py | 44 --------------- 8 files changed, 170 insertions(+), 111 deletions(-) rename bayesflow/{distributions => utils/dispatch}/find_distribution.py (60%) create mode 100644 bayesflow/utils/dispatch/find_inference_network.py create mode 100644 bayesflow/utils/dispatch/find_summary_network.py delete mode 100644 bayesflow/utils/workflow_utils.py diff --git a/bayesflow/distributions/diagonal_normal.py b/bayesflow/distributions/diagonal_normal.py index 0d439f704..98a127b1c 100644 --- a/bayesflow/distributions/diagonal_normal.py +++ b/bayesflow/distributions/diagonal_normal.py @@ -3,6 +3,7 @@ import numpy as np import keras +from keras import ops from bayesflow.types import Shape, Tensor from bayesflow.utils.decorators import allow_batch_size @@ -19,7 +20,7 @@ def __init__( self, mean: int | float | np.ndarray | Tensor = 0.0, std: int | float | np.ndarray | Tensor = 1.0, - use_learnable_parameters: bool = False, + trainable_parameters: bool = False, seed_generator: keras.random.SeedGenerator = None, **kwargs, ): @@ -39,7 +40,7 @@ def __init__( std : int, float, np.ndarray, or Tensor, optional The standard deviation of the Gaussian distribution. Can be a scalar or a tensor. Default is 1.0. - use_learnable_parameters : bool, optional + trainable_parameters : bool, optional Whether to treat the mean and standard deviation as learnable parameters. Default is False. seed_generator : keras.random.SeedGenerator, optional A Keras seed generator for reproducible random sampling. If None, a new seed @@ -53,47 +54,41 @@ def __init__( self.mean = mean self.std = std + self.trainable_parameters = trainable_parameters + self.seed_generator = seed_generator or keras.random.SeedGenerator() + self.dim = None self.log_normalization_constant = None - - self.use_learnable_parameters = use_learnable_parameters - - if seed_generator is None: - seed_generator = keras.random.SeedGenerator() - - self.seed_generator = seed_generator + self._mean = None + self._std = None def build(self, input_shape: Shape) -> None: + if self.built: + return + self.dim = int(input_shape[-1]) - self.mean = keras.ops.broadcast_to(self.mean, (self.dim,)) - self.mean = keras.ops.cast(self.mean, "float32") - self.std = keras.ops.broadcast_to(self.std, (self.dim,)) - self.std = keras.ops.cast(self.std, "float32") + self.mean = ops.cast(ops.broadcast_to(self.mean, (self.dim,)), "float32") + self.std = ops.cast(ops.broadcast_to(self.std, (self.dim,)), "float32") - self.log_normalization_constant = -0.5 * self.dim * math.log(2.0 * math.pi) - keras.ops.sum( - keras.ops.log(self.std) - ) + self.log_normalization_constant = -0.5 * self.dim * math.log(2.0 * math.pi) - ops.sum(ops.log(self.std)) - if self.use_learnable_parameters: + if self.trainable_parameters: self._mean = self.add_weight( - shape=keras.ops.shape(self.mean), - # Initializing with const tensor https://github.com/keras-team/keras/pull/20457#discussion_r1832081248 - initializer=keras.initializers.get(value=self.mean), + shape=ops.shape(self.mean), + initializer=keras.initializers.get(self.mean), dtype="float32", + trainable=True, ) self._std = self.add_weight( - shape=keras.ops.shape(self.std), - # Initializing with const tensor https://github.com/keras-team/keras/pull/20457#discussion_r1832081248 - initializer=keras.initializers.get(self.std), - dtype="float32", + shape=ops.shape(self.std), initializer=keras.initializers.get(self.std), dtype="float32", trainable=True ) else: self._mean = self.mean self._std = self.std def log_prob(self, samples: Tensor, *, normalize: bool = True) -> Tensor: - result = -0.5 * keras.ops.sum((samples - self._mean) ** 2 / self.std**2, axis=-1) + result = -0.5 * ops.sum((samples - self._mean) ** 2 / self._std**2, axis=-1) if normalize: result += self.log_normalization_constant @@ -110,7 +105,7 @@ def get_config(self): config = { "mean": self.mean, "std": self.std, - "use_learnable_parameters": self.use_learnable_parameters, + "trainable_parameters": self.trainable_parameters, "seed_generator": self.seed_generator, } diff --git a/bayesflow/distributions/diagonal_student_t.py b/bayesflow/distributions/diagonal_student_t.py index 977fa057b..cd32a67fb 100644 --- a/bayesflow/distributions/diagonal_student_t.py +++ b/bayesflow/distributions/diagonal_student_t.py @@ -1,8 +1,10 @@ -import keras - import math + import numpy as np +import keras +from keras import ops + from bayesflow.types import Shape, Tensor from bayesflow.utils import expand_tile from bayesflow.utils.decorators import allow_batch_size @@ -20,7 +22,7 @@ def __init__( df: int | float, loc: int | float | np.ndarray | Tensor = 0.0, scale: int | float | np.ndarray | Tensor = 1.0, - use_learnable_parameters: bool = False, + trainable_parameters: bool = False, seed_generator: keras.random.SeedGenerator = None, **kwargs, ): @@ -42,8 +44,8 @@ def __init__( The location parameter (mean) of the distribution. Default is 0.0. scale : int, float, np.ndarray, or Tensor, optional The scale parameter (standard deviation) of the distribution. Default is 1.0. - use_learnable_parameters : bool, optional - Whether to treat `loc` and `scale` as learnable parameters. Default is False. + trainable_parameters : bool, optional + Whether to treat `loc` and `scale` as trainable parameters. Default is False. seed_generator : keras.random.SeedGenerator, optional A Keras seed generator for reproducible random sampling. If None, a new seed generator is created. Default is None. @@ -57,52 +59,50 @@ def __init__( self.loc = loc self.scale = scale - self.dim = None - self.log_normalization_constant = None + self.trainable_parameters = trainable_parameters - self.use_learnable_parameters = use_learnable_parameters + self.seed_generator = seed_generator or keras.random.SeedGenerator() - if seed_generator is None: - seed_generator = keras.random.SeedGenerator() - - self.seed_generator = seed_generator + self.log_normalization_constant = None + self.dim = None + self._loc = None + self._scale = None def build(self, input_shape: Shape) -> None: + if self.built: + return + self.dim = int(input_shape[-1]) # convert to tensor and broadcast if necessary - self.loc = keras.ops.broadcast_to(self.loc, (self.dim,)) - self.loc = keras.ops.cast(self.loc, "float32") - - self.scale = keras.ops.broadcast_to(self.scale, (self.dim,)) - self.scale = keras.ops.cast(self.scale, "float32") + self.loc = ops.cast(ops.broadcast_to(self.loc, (self.dim,)), "float32") + self.scale = ops.cast(ops.broadcast_to(self.scale, (self.dim,)), "float32") self.log_normalization_constant = ( -0.5 * self.dim * math.log(self.df) - 0.5 * self.dim * math.log(math.pi) - math.lgamma(0.5 * self.df) + math.lgamma(0.5 * (self.df + self.dim)) - - keras.ops.sum(keras.ops.log(self.scale)) + - ops.sum(keras.ops.log(self.scale)) ) - if self.use_learnable_parameters: + if self.trainable_parameters: self._loc = self.add_weight( - shape=keras.ops.shape(self.loc), - initializer=keras.initializers.get(self.loc), - dtype="float32", + shape=ops.shape(self.loc), initializer=keras.initializers.get(self.loc), dtype="float32", trainable=True ) self._scale = self.add_weight( - shape=keras.ops.shape(self.scale), + shape=ops.shape(self.scale), initializer=keras.initializers.get(self.scale), dtype="float32", + trainable=True, ) else: self._loc = self.loc self._scale = self.scale def log_prob(self, samples: Tensor, *, normalize: bool = True) -> Tensor: - mahalanobis_term = keras.ops.sum((samples - self._loc) ** 2 / self._scale**2, axis=-1) - result = -0.5 * (self.df + self.dim) * keras.ops.log1p(mahalanobis_term / self.df) + mahalanobis_term = ops.sum((samples - self._loc) ** 2 / self._scale**2, axis=-1) + result = -0.5 * (self.df + self.dim) * ops.log1p(mahalanobis_term / self.df) if normalize: result += self.log_normalization_constant @@ -122,7 +122,7 @@ def sample(self, batch_shape: Shape) -> Tensor: normal_samples = keras.random.normal(batch_shape + (self.dim,), seed=self.seed_generator) - return self._loc + self._scale * normal_samples * keras.ops.sqrt(self.df / chi2_samples) + return self._loc + self._scale * normal_samples * ops.sqrt(self.df / chi2_samples) def get_config(self): base_config = super().get_config() @@ -131,7 +131,7 @@ def get_config(self): "df": self.df, "loc": self.loc, "scale": self.scale, - "use_learnable_parameters": self.use_learnable_parameters, + "trainable_parameters": self.trainable_parameters, "seed_generator": self.seed_generator, } diff --git a/bayesflow/distributions/mixture.py b/bayesflow/distributions/mixture.py index 0946f72b7..d7f6bd758 100644 --- a/bayesflow/distributions/mixture.py +++ b/bayesflow/distributions/mixture.py @@ -50,22 +50,18 @@ def __init__( super().__init__(**kwargs) - self.dim = None self.distributions = distributions if mixture_logits is None: - mixture_logits = keras.ops.ones(shape=len(distributions)) - - self.mixture_logits = mixture_logits - self._mixture_logits = self.add_weight( - shape=(len(distributions),), - initializer=keras.initializers.Constant(value=mixture_logits), - dtype="float32", - trainable=trainable_mixture, - ) + self.mixture_logits = ops.ones(shape=len(distributions)) + else: + self.mixture_logits = ops.convert_to_tensor(mixture_logits) self.trainable_mixture = trainable_mixture + self.dim = None + self._mixture_logits = None + @allow_batch_size def sample(self, batch_shape: Shape) -> Tensor: """ @@ -138,10 +134,20 @@ def log_prob(self, samples: Tensor, *, normalize: bool = True) -> Tensor: return log_prob def build(self, input_shape: Shape) -> None: + if self.built: + return + + self.dim = input_shape[-1] + for distribution in self.distributions: distribution.build(input_shape) - self.dim = input_shape[-1] + self._mixture_logits = self.add_weight( + shape=(len(self.distributions),), + initializer=keras.initializers.get(self.mixture_logits), + dtype="float32", + trainable=self.trainable_mixture, + ) def get_config(self): base_config = super().get_config() diff --git a/bayesflow/utils/dispatch/__init__.py b/bayesflow/utils/dispatch/__init__.py index 422f014e0..852756780 100644 --- a/bayesflow/utils/dispatch/__init__.py +++ b/bayesflow/utils/dispatch/__init__.py @@ -3,3 +3,6 @@ from .find_permutation import find_permutation from .find_pooling import find_pooling from .find_recurrent_net import find_recurrent_net +from .find_inference_network import find_inference_network +from .find_summary_network import find_summary_network +from .find_distribution import find_distribution diff --git a/bayesflow/distributions/find_distribution.py b/bayesflow/utils/dispatch/find_distribution.py similarity index 60% rename from bayesflow/distributions/find_distribution.py rename to bayesflow/utils/dispatch/find_distribution.py index 84ef56c15..f94a9f262 100644 --- a/bayesflow/distributions/find_distribution.py +++ b/bayesflow/utils/dispatch/find_distribution.py @@ -1,6 +1,5 @@ from functools import singledispatch - -from bayesflow.distributions import Distribution +import keras @singledispatch @@ -15,8 +14,20 @@ def _(name: str, *args, **kwargs): from bayesflow.distributions import DiagonalNormal distribution = DiagonalNormal(*args, **kwargs) + + case "student" | "student-t" | "student_t": + from bayesflow.distributions import DiagonalStudentT + + distribution = DiagonalStudentT(*args, **kwargs) + + case "mixture": + raise ValueError( + "Mixture distributions need to be explicitly defined as bf.distributions.Mixture(...) " + "and passed to the constructor." + ) case "none": distribution = None + case other: raise ValueError(f"Unsupported distribution name '{other}'.") @@ -29,5 +40,5 @@ def _(none: None, *args, **kwargs): @find_distribution.register -def _(distribution: Distribution, *args, **kwargs): +def _(distribution: keras.Layer, *args, **kwargs): return distribution diff --git a/bayesflow/utils/dispatch/find_inference_network.py b/bayesflow/utils/dispatch/find_inference_network.py new file mode 100644 index 000000000..617018de3 --- /dev/null +++ b/bayesflow/utils/dispatch/find_inference_network.py @@ -0,0 +1,39 @@ +from functools import singledispatch +import keras + + +@singledispatch +def find_inference_network(arg, *args, **kwargs): + raise TypeError(f"Cannot infer inference network from {arg!r}.") + + +@find_inference_network.register +def _(name: str, *args, **kwargs): + match name.lower(): + case "coupling_flow": + from bayesflow.networks import CouplingFlow + + return CouplingFlow(*args, **kwargs) + + case "flow_matching": + from bayesflow.networks import FlowMatching + + return FlowMatching(*args, **kwargs) + + case "consistency_model": + from bayesflow.networks import ConsistencyModel + + return ConsistencyModel(*args, **kwargs) + + case unknown_network: + raise ValueError(f"Unknown inference network: '{unknown_network}'") + + +@find_inference_network.register +def _(layer: keras.Layer, *args, **kwargs): + return layer + + +@find_inference_network.register +def _(model: keras.Model, *args, **kwargs): + return model diff --git a/bayesflow/utils/dispatch/find_summary_network.py b/bayesflow/utils/dispatch/find_summary_network.py new file mode 100644 index 000000000..bc14b7e21 --- /dev/null +++ b/bayesflow/utils/dispatch/find_summary_network.py @@ -0,0 +1,49 @@ +from functools import singledispatch +import keras + + +@singledispatch +def find_summary_network(arg, *args, **kwargs): + raise TypeError(f"Cannot infer inference network from {arg!r}.") + + +@find_summary_network.register +def _(name: str, *args, **kwargs): + match name.lower(): + case "deep_set": + from bayesflow.networks import DeepSet + + return DeepSet(*args, **kwargs) + + case "set_transformer": + from bayesflow.networks import SetTransformer + + return SetTransformer(*args, **kwargs) + + case "fusion_transformer": + from bayesflow.networks import FusionTransformer + + return FusionTransformer(*args, **kwargs) + + case "time_series_transformer": + from bayesflow.networks import TimeSeriesTransformer + + return TimeSeriesTransformer(*args, **kwargs) + + case "time_series_network": + from bayesflow.networks import TimeSeriesNetwork + + return TimeSeriesNetwork(*args, **kwargs) + + case unknown_network: + raise ValueError(f"Unknown summary network: '{unknown_network}'") + + +@find_summary_network.register +def _(layer: keras.Layer, *args, **kwargs): + return layer + + +@find_summary_network.register +def _(model: keras.Model, *args, **kwargs): + return model diff --git a/bayesflow/utils/workflow_utils.py b/bayesflow/utils/workflow_utils.py deleted file mode 100644 index 0f23a8cb8..000000000 --- a/bayesflow/utils/workflow_utils.py +++ /dev/null @@ -1,44 +0,0 @@ -import bayesflow.networks -from bayesflow.networks import InferenceNetwork, PointInferenceNetwork, SummaryNetwork - - -def find_inference_network(inference_network: InferenceNetwork | str, **kwargs) -> InferenceNetwork: - if isinstance(inference_network, InferenceNetwork) or isinstance(inference_network, PointInferenceNetwork): - return inference_network - if isinstance(inference_network, type): - return inference_network(**kwargs) - - match inference_network.lower(): - case "coupling_flow": - return bayesflow.networks.CouplingFlow(**kwargs) - case "flow_matching": - return bayesflow.networks.FlowMatching(**kwargs) - case "consistency_model": - return bayesflow.networks.ConsistencyModel(**kwargs) - case str() as unknown_network: - raise ValueError(f"Unknown inference network: '{unknown_network}'") - case other: - raise TypeError(f"Unknown transform type: {other}") - - -def find_summary_network(summary_network: SummaryNetwork | str, **kwargs) -> SummaryNetwork: - if isinstance(summary_network, SummaryNetwork): - return summary_network - if isinstance(summary_network, type): - return summary_network(**kwargs) - - match summary_network.lower(): - case "deep_set": - return bayesflow.networks.DeepSet(**kwargs) - case "set_transformer": - return bayesflow.networks.SetTransformer(**kwargs) - case "fusion_transformer": - return bayesflow.networks.FusionTransformer(**kwargs) - case "time_series_transformer": - return bayesflow.networks.TimeSeriesTransformer(**kwargs) - case "time_series_network": - return bayesflow.networks.LSTNet(**kwargs) - case str() as unknown_network: - raise ValueError(f"Unknown summary network: '{unknown_network}'") - case other: - raise TypeError(f"Unknown transform type: {other}") From 25f5c64fe5c2976015b5cf02bc46aca357c4dbad Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 14:58:56 -0400 Subject: [PATCH 15/46] Update dispatching distributions --- bayesflow/networks/inference_network.py | 3 +-- bayesflow/networks/summary_network.py | 3 +-- bayesflow/utils/__init__.py | 27 +++++++++++++++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/bayesflow/networks/inference_network.py b/bayesflow/networks/inference_network.py index ae4856b02..b092ce2cb 100644 --- a/bayesflow/networks/inference_network.py +++ b/bayesflow/networks/inference_network.py @@ -1,8 +1,7 @@ import keras -from bayesflow.distributions import find_distribution from bayesflow.types import Shape, Tensor -from bayesflow.utils import layer_kwargs +from bayesflow.utils import layer_kwargs, find_distribution from bayesflow.utils.decorators import allow_batch_size diff --git a/bayesflow/networks/summary_network.py b/bayesflow/networks/summary_network.py index 6e97c618f..e821be3f3 100644 --- a/bayesflow/networks/summary_network.py +++ b/bayesflow/networks/summary_network.py @@ -1,9 +1,8 @@ import keras -from bayesflow.distributions import find_distribution from bayesflow.metrics.functional import maximum_mean_discrepancy from bayesflow.types import Tensor -from bayesflow.utils import layer_kwargs +from bayesflow.utils import layer_kwargs, find_distribution from bayesflow.utils.decorators import sanitize_input_shape from bayesflow.utils.serialization import deserialize diff --git a/bayesflow/utils/__init__.py b/bayesflow/utils/__init__.py index 73ba7fd8b..737c533ce 100644 --- a/bayesflow/utils/__init__.py +++ b/bayesflow/utils/__init__.py @@ -7,8 +7,11 @@ logging, numpy_utils, ) + from .callbacks import detailed_loss_callback + from .devices import devices + from .dict_utils import ( convert_args, convert_kwargs, @@ -20,30 +23,48 @@ split_arrays, squeeze_inner_estimates_dict, ) -from .dispatch import find_network, find_permutation, find_pooling, find_recurrent_net + +from .dispatch import ( + find_network, + find_permutation, + find_pooling, + find_recurrent_net, + find_summary_network, + find_inference_network, + find_distribution, +) + from .ecdf import simultaneous_ecdf_bands, ranks + from .functional import batched_call + from .git import ( issue_url, pull_url, repo_url, ) + from .hparam_utils import find_batch_size, find_memory_budget + from .integrate import ( integrate, ) + from .io import ( pickle_load, format_bytes, parse_bytes, ) + from .jacobian import ( jacobian, jacobian_trace, jvp, vjp, ) + from .optimal_transport import optimal_transport + from .plot_utils import ( check_estimates_prior_shapes, prepare_plot_data, @@ -53,6 +74,7 @@ add_metric, ) from .serialization import serialize_value_or_type, deserialize_value_or_type + from .tensor_utils import ( concatenate_valid, expand, @@ -75,9 +97,10 @@ fill_triangular_matrix, weighted_mean, ) + from .classification import calibration_curve, confusion_matrix + from .validators import check_lengths_same -from .workflow_utils import find_inference_network, find_summary_network from ._docs import _add_imports_to_all From f6a70b5a318e97bb08078dcc3646f77de3cdf84e Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 14:59:22 -0400 Subject: [PATCH 16/46] Improve workflow tests with multiple summary nets / approximators --- tests/test_workflows/conftest.py | 53 +++++++++++++++++---- tests/test_workflows/test_basic_workflow.py | 23 +++++++-- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/tests/test_workflows/conftest.py b/tests/test_workflows/conftest.py index e9455800b..c98e543e9 100644 --- a/tests/test_workflows/conftest.py +++ b/tests/test_workflows/conftest.py @@ -1,15 +1,52 @@ import pytest +import keras -@pytest.fixture() -def inference_network(): - from bayesflow.networks import CouplingFlow +from bayesflow.utils.serialization import serializable - return CouplingFlow(depth=2) +@pytest.fixture(params=["coupling_flow", "flow_matching"]) +def inference_network(request): + if request.param == "coupling_flow": + from bayesflow.networks import CouplingFlow -@pytest.fixture() -def summary_network(): - from bayesflow.networks import TimeSeriesTransformer + return CouplingFlow(depth=2) - return TimeSeriesTransformer(embed_dims=(8, 8), mlp_widths=(32, 32), mlp_depths=(1, 1)) + elif request.param == "flow_matching": + from bayesflow.networks import FlowMatching + + return FlowMatching(subnet_kwargs=dict(widths=(32, 32)), use_optimal_transport=False) + + +@pytest.fixture(params=["time_series_transformer", "fusion_transformer", "time_series_network", "custom"]) +def summary_network(request): + if request.param == "time_series_transformer": + from bayesflow.networks import TimeSeriesTransformer + + return TimeSeriesTransformer(embed_dims=(8, 8), mlp_widths=(16, 8), mlp_depths=(1, 1)) + + elif request.param == "fusion_transformer": + from bayesflow.networks import FusionTransformer + + return FusionTransformer( + embed_dims=(8, 8), mlp_widths=(8, 16), mlp_depths=(2, 1), template_dim=8, bidirectional=False + ) + + elif request.param == "time_series_network": + from bayesflow.networks import TimeSeriesNetwork + + return TimeSeriesNetwork(filters=4, skip_steps=2) + + elif request.param == "custom": + from bayesflow.networks import SummaryNetwork + + @serializable + class Custom(SummaryNetwork): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.inner = keras.Sequential([keras.layers.LSTM(8), keras.layers.Dense(4)]) + + def call(self, x, **kwargs): + return self.inner(x, training=kwargs.get("stage") == "training") + + return Custom() diff --git a/tests/test_workflows/test_basic_workflow.py b/tests/test_workflows/test_basic_workflow.py index 9a1c7815f..a0a3dc83c 100644 --- a/tests/test_workflows/test_basic_workflow.py +++ b/tests/test_workflows/test_basic_workflow.py @@ -1,21 +1,34 @@ +import os + +import keras + import bayesflow as bf -def test_basic_workflow(inference_network, summary_network): +def test_basic_workflow(tmp_path, inference_network, summary_network): workflow = bf.BasicWorkflow( inference_network=inference_network, summary_network=summary_network, inference_variables=["parameters"], summary_variables=["observables"], simulator=bf.simulators.SIR(), + checkpoint_filepath=str(tmp_path), ) - history = workflow.fit_online(epochs=2, batch_size=32, num_batches_per_epoch=2) - plots = workflow.plot_default_diagnostics(test_data=50, num_samples=50) - metrics = workflow.compute_default_diagnostics(test_data=50, num_samples=50, variable_names=["p1", "p2"]) + # Ensure metrics work fine + history = workflow.fit_online(epochs=4, batch_size=8, num_batches_per_epoch=2, verbose=0) + plots = workflow.plot_default_diagnostics(test_data=50, num_samples=25) + metrics = workflow.compute_default_diagnostics(test_data=50, num_samples=25, variable_names=["p1", "p2"]) assert "loss" in list(history.history.keys()) - assert len(history.history["loss"]) == 2 + assert len(history.history["loss"]) == 4 assert list(plots.keys()) == ["losses", "recovery", "calibration_ecdf", "z_score_contraction"] assert list(metrics.columns) == ["p1", "p2"] assert metrics.values.shape == (3, 2) + + # Ensure saving and loading from workflow works fine + loaded_approximator = keras.saving.load_model(os.path.join(str(tmp_path), "model.keras")) + + # Get samples + samples = loaded_approximator.sample(conditions=workflow.simulate(5), num_samples=3) + assert samples["parameters"].shape == (5, 3, 2) From 7ce37cfa19f8e6dba553c71e37dba5e7924fb056 Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 15:22:34 -0400 Subject: [PATCH 17/46] Fix zombie find_distribution import --- bayesflow/distributions/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bayesflow/distributions/__init__.py b/bayesflow/distributions/__init__.py index ed8d05af7..e9e30b7e4 100644 --- a/bayesflow/distributions/__init__.py +++ b/bayesflow/distributions/__init__.py @@ -9,8 +9,6 @@ from .diagonal_student_t import DiagonalStudentT from .mixture import Mixture -from .find_distribution import find_distribution - from ..utils._docs import _add_imports_to_all _add_imports_to_all(include_modules=[]) From ea5a78db5cb1bc04039fbf076c94d866822843c0 Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 15:47:20 -0400 Subject: [PATCH 18/46] Add readme entry [no ci] --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6f1df3a4b..a42b1df97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bayesflow" -version = "2.0.1" +version = "2.0.2" authors = [{ name = "The BayesFlow Team" }] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -19,6 +19,7 @@ classifiers = [ "Topic :: Scientific/Engineering :: Artificial Intelligence", ] description = "Amortizing Bayesian Inference With Neural Networks" +readme = { file = "README.md", content-type = "text/markdown" } license = { file = "LICENSE" } requires-python = ">= 3.10, < 3.12" From dc3cf816dfa0ac66dfb23df1afabd617df130cb9 Mon Sep 17 00:00:00 2001 From: Marvin Schmitt <35921281+marvinschmitt@users.noreply.github.com> Date: Fri, 25 Apr 2025 22:50:58 +0300 Subject: [PATCH 19/46] Update README: NumFOCUS affiliation, awesome-abi list (#445) --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f3269754..c4049c470 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ![Codecov](https://img.shields.io/codecov/c/github/bayesflow-org/bayesflow?style=for-the-badge&link=https%3A%2F%2Fapp.codecov.io%2Fgh%2Fbayesflow-org%2Fbayesflow%2Ftree%2Fmain) [![DOI](https://img.shields.io/badge/DOI-10.21105%2Fjoss.05702-blue?style=for-the-badge)](https://doi.org/10.21105/joss.05702) ![PyPI - License](https://img.shields.io/pypi/l/bayesflow?style=for-the-badge) +![NumFOCUS Affiliated Project](https://img.shields.io/badge/NumFOCUS-Affiliated%20Project-orange?style=for-the-badge) BayesFlow is a Python library for simulation-based **Amortized Bayesian Inference** with neural networks. It provides users and researchers with: @@ -225,8 +226,10 @@ You can find and install the old Bayesflow version via the `stable-legacy` branc ## Awesome Amortized Inference -If you are interested in a curated list of resources, including reviews, software, papers, and other resources related to amortized inference, feel free to explore our [community-driven list](https://github.com/bayesflow-org/awesome-amortized-inference). +If you are interested in a curated list of resources, including reviews, software, papers, and other resources related to amortized inference, feel free to explore our [community-driven list](https://github.com/bayesflow-org/awesome-amortized-inference). If you'd like a paper (by yourself or someone else) featured, please add it to the list with a pull request, an issue, or a message to the maintainers. ## Acknowledgments This project is currently managed by researchers from Rensselaer Polytechnic Institute, TU Dortmund University, and Heidelberg University. It is partially funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) Projects 528702768 and 508399956. The project is further supported by Germany's Excellence Strategy -- EXC-2075 - 390740016 (Stuttgart Cluster of Excellence SimTech) and EXC-2181 - 390900948 (Heidelberg Cluster of Excellence STRUCTURES), the collaborative research cluster TRR 391 – 520388526, as well as the Informatics for Life initiative funded by the Klaus Tschira Foundation. + +BayesFlow is a [NumFOCUS Affiliated Project](https://numfocus.org/sponsored-projects/affiliated-projects). From 3b1c0530b59e55666c5e8bf9d6f36104766fca5c Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 17:22:57 -0400 Subject: [PATCH 20/46] fix is_symbolic_tensor --- bayesflow/utils/tensor_utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bayesflow/utils/tensor_utils.py b/bayesflow/utils/tensor_utils.py index 4d89249b7..72d83076c 100644 --- a/bayesflow/utils/tensor_utils.py +++ b/bayesflow/utils/tensor_utils.py @@ -97,9 +97,6 @@ def is_symbolic_tensor(x: Tensor) -> bool: if keras.utils.is_keras_tensor(x): return True - if not keras.ops.is_tensor(x): - return False - match keras.backend.backend(): case "jax": import jax From c638124244e9d8b7e49353b53fbe22dae98de11f Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 25 Apr 2025 17:30:29 -0400 Subject: [PATCH 21/46] remove multiple batch sizes, remove multiple python version tests, remove update-workflows branch from workflow style tests, add __init__ and conftest to test_point_approximators (#443) --- .github/workflows/style.yaml | 2 -- .github/workflows/tests.yaml | 2 +- tests/conftest.py | 32 +------------------ .../test_point_approximators/__init__.py | 0 .../test_point_approximators/conftest.py | 0 tests/test_distributions/conftest.py | 2 +- tests/test_links/conftest.py | 5 --- tests/test_networks/test_summary_networks.py | 2 +- tests/utils/check_combinations.py | 6 ++-- 9 files changed, 7 insertions(+), 44 deletions(-) create mode 100644 tests/test_approximators/test_point_approximators/__init__.py create mode 100644 tests/test_approximators/test_point_approximators/conftest.py diff --git a/.github/workflows/style.yaml b/.github/workflows/style.yaml index a451ac89d..3c2da4421 100644 --- a/.github/workflows/style.yaml +++ b/.github/workflows/style.yaml @@ -6,12 +6,10 @@ on: branches: - main - dev - - update-workflows push: branches: - main - dev - - update-workflows jobs: check-code-style: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f90389f69..ab3d03078 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -24,7 +24,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.10", "3.11"] + python-version: ["3.10"] # we usually only need to test the oldest python version backend: ["jax", "tensorflow", "torch"] runs-on: ${{ matrix.os }} diff --git a/tests/conftest.py b/tests/conftest.py index 6e1e69db1..560b7c59b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -41,7 +41,7 @@ def pytest_make_parametrize_id(config, val, argname): return f"{argname}={repr(val)}" -@pytest.fixture(params=[2, 3], scope="session") +@pytest.fixture(params=[2], scope="session") def batch_size(request): return request.param @@ -94,33 +94,3 @@ def random_set(batch_size, set_size, feature_size): @pytest.fixture(params=[2, 3], scope="session") def set_size(request): return request.param - - -@pytest.fixture(params=["two_moons"], scope="session") -def simulator(request): - return request.getfixturevalue(request.param) - - -@pytest.fixture(scope="session") -def training_dataset(simulator, batch_size): - from bayesflow.datasets import OfflineDataset - - num_batches = 128 - samples = simulator.sample((num_batches * batch_size,)) - return OfflineDataset(samples, batch_size=batch_size) - - -@pytest.fixture(scope="session") -def two_moons(batch_size): - from bayesflow.simulators import TwoMoonsSimulator - - return TwoMoonsSimulator() - - -@pytest.fixture(scope="session") -def validation_dataset(simulator, batch_size): - from bayesflow.datasets import OfflineDataset - - num_batches = 16 - samples = simulator.sample((num_batches * batch_size,)) - return OfflineDataset(samples, batch_size=batch_size) diff --git a/tests/test_approximators/test_point_approximators/__init__.py b/tests/test_approximators/test_point_approximators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_approximators/test_point_approximators/conftest.py b/tests/test_approximators/test_point_approximators/conftest.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_distributions/conftest.py b/tests/test_distributions/conftest.py index 29c5b4139..e06ed18af 100644 --- a/tests/test_distributions/conftest.py +++ b/tests/test_distributions/conftest.py @@ -3,7 +3,7 @@ import keras -@pytest.fixture(params=[2, 3]) +@pytest.fixture(params=[2]) def batch_size(request): return request.param diff --git a/tests/test_links/conftest.py b/tests/test_links/conftest.py index 53e9eeac8..be7730ef2 100644 --- a/tests/test_links/conftest.py +++ b/tests/test_links/conftest.py @@ -82,8 +82,3 @@ def quantiles(request): @pytest.fixture() def unordered(batch_size, num_quantiles, num_variables): return keras.random.normal((batch_size, num_quantiles, num_variables)) - - -# @pytest.fixture() -# def random_matrix_batch(batch_size, num_variables): -# return keras.random.normal((batch_size, num_variables, num_variables)) diff --git a/tests/test_networks/test_summary_networks.py b/tests/test_networks/test_summary_networks.py index 50e1726c1..74ce1f5fd 100644 --- a/tests/test_networks/test_summary_networks.py +++ b/tests/test_networks/test_summary_networks.py @@ -103,7 +103,7 @@ def test_save_and_load(tmp_path, summary_network, random_set): @pytest.mark.parametrize("stage", ["training", "validation"]) def test_compute_metrics(stage, summary_network, random_set): if summary_network is None: - pytest.skip() + pytest.skip("Nothing to do, because there is no summary network.") summary_network.build(keras.ops.shape(random_set)) diff --git a/tests/utils/check_combinations.py b/tests/utils/check_combinations.py index 8d3fa5d46..8565703c8 100644 --- a/tests/utils/check_combinations.py +++ b/tests/utils/check_combinations.py @@ -13,12 +13,12 @@ def check_combination_simulator_adapter(simulator, adapter): with pytest.raises(KeyError): adapter(simulator.sample(1)) # Don't use this fixture combination for further tests. - pytest.skip() + pytest.skip(reason="Do not use this fixture combination for further tests") # TODO: better reason elif simulator_with_sample_weight and not adapter_with_sample_weight: # When a weight key is present, but the adapter does not configure it # to be used as sample weight, no error is raised currently. # Don't use this fixture combination for further tests. - pytest.skip() + pytest.skip(reason="Do not use this fixture combination for further tests") # TODO: better reason def check_approximator_multivariate_normal_score(approximator): @@ -28,4 +28,4 @@ def check_approximator_multivariate_normal_score(approximator): if isinstance(approximator, PointApproximator): for score in approximator.inference_network.scores.values(): if isinstance(score, MultivariateNormalScore): - pytest.skip() + pytest.skip(reason="MultivariateNormalScore is unstable") From de8e1cb9c9f1dfd1dcc7992b76d40aef7219e41c Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 25 Apr 2025 17:33:58 -0400 Subject: [PATCH 22/46] implement compile_from_config and get_compile_config (#442) * implement compile_from_config and get_compile_config * add optimizer build to compile_from_config --- .../approximators/continuous_approximator.py | 16 ++++++++++++++++ .../model_comparison_approximator.py | 16 ++++++++++++++++ bayesflow/metrics/maximum_mean_discrepancy.py | 2 ++ bayesflow/metrics/root_mean_squard_error.py | 3 ++- 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/bayesflow/approximators/continuous_approximator.py b/bayesflow/approximators/continuous_approximator.py index dbd9eba0c..f0c1d68fa 100644 --- a/bayesflow/approximators/continuous_approximator.py +++ b/bayesflow/approximators/continuous_approximator.py @@ -104,6 +104,12 @@ def compile( return super().compile(*args, **kwargs) + def compile_from_config(self, config): + self.compile(**deserialize(config)) + if hasattr(self, "optimizer") and self.built: + # Create optimizer variables. + self.optimizer.build(self.trainable_variables) + def compute_metrics( self, inference_variables: Tensor, @@ -213,6 +219,16 @@ def get_config(self): return base_config | serialize(config) + def get_compile_config(self): + base_config = super().get_compile_config() or {} + + config = { + "inference_metrics": self.inference_network._metrics, + "summary_metrics": self.summary_network._metrics if self.summary_network is not None else None, + } + + return base_config | serialize(config) + def estimate( self, conditions: Mapping[str, np.ndarray], diff --git a/bayesflow/approximators/model_comparison_approximator.py b/bayesflow/approximators/model_comparison_approximator.py index 1e26f00b0..03b377537 100644 --- a/bayesflow/approximators/model_comparison_approximator.py +++ b/bayesflow/approximators/model_comparison_approximator.py @@ -118,6 +118,12 @@ def compile( return super().compile(*args, **kwargs) + def compile_from_config(self, config): + self.compile(**deserialize(config)) + if hasattr(self, "optimizer") and self.built: + # Create optimizer variables. + self.optimizer.build(self.trainable_variables) + def compute_metrics( self, *, @@ -262,6 +268,16 @@ def get_config(self): return base_config | serialize(config) + def get_compile_config(self): + base_config = super().get_compile_config() or {} + + config = { + "classifier_metrics": self.classifier_network._metrics, + "summary_metrics": self.summary_network._metrics if self.summary_network is not None else None, + } + + return base_config | serialize(config) + def predict( self, *, diff --git a/bayesflow/metrics/maximum_mean_discrepancy.py b/bayesflow/metrics/maximum_mean_discrepancy.py index 64b8c35a0..37af44fd4 100644 --- a/bayesflow/metrics/maximum_mean_discrepancy.py +++ b/bayesflow/metrics/maximum_mean_discrepancy.py @@ -2,9 +2,11 @@ import keras +from bayesflow.utils.serialization import serializable from .functional import maximum_mean_discrepancy +@serializable class MaximumMeanDiscrepancy(keras.Metric): def __init__( self, diff --git a/bayesflow/metrics/root_mean_squard_error.py b/bayesflow/metrics/root_mean_squard_error.py index 13e724c14..97de62e6a 100644 --- a/bayesflow/metrics/root_mean_squard_error.py +++ b/bayesflow/metrics/root_mean_squard_error.py @@ -1,10 +1,11 @@ from functools import partial import keras - +from bayesflow.utils.serialization import serializable from .functional import root_mean_squared_error +@serializable class RootMeanSquaredError(keras.metrics.MeanMetricWrapper): def __init__(self, name="root_mean_squared_error", dtype=None, **kwargs): fn = partial(root_mean_squared_error, **kwargs) From 16491beae5606953762fbccf99fe368fecd2e580 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 25 Apr 2025 18:58:54 -0400 Subject: [PATCH 23/46] Fix Optimal Transport for Compiled Contexts (#446) * remove the is_symbolic_tensor check because this would otherwise skip the whole function for compiled contexts * skip pyabc test * fix sinkhorn and log_sinkhorn message formatting for jax by making the warning message worse --- .../utils/optimal_transport/log_sinkhorn.py | 18 +++++------------- bayesflow/utils/optimal_transport/sinkhorn.py | 18 +++++------------- tests/test_examples/test_examples.py | 1 + 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/bayesflow/utils/optimal_transport/log_sinkhorn.py b/bayesflow/utils/optimal_transport/log_sinkhorn.py index 3538eaeff..9fa6dba26 100644 --- a/bayesflow/utils/optimal_transport/log_sinkhorn.py +++ b/bayesflow/utils/optimal_transport/log_sinkhorn.py @@ -1,7 +1,6 @@ import keras from .. import logging -from ..tensor_utils import is_symbolic_tensor from .euclidean import euclidean @@ -27,9 +26,6 @@ def log_sinkhorn_plan(x1, x2, regularization: float = 1.0, rtol=1e-5, atol=1e-8, log_plan = cost / -(regularization * keras.ops.mean(cost) + 1e-16) - if is_symbolic_tensor(log_plan): - return log_plan - def contains_nans(plan): return keras.ops.any(keras.ops.isnan(plan)) @@ -57,22 +53,18 @@ def do_nothing(): pass def log_steps(): - msg = "Log-Sinkhorn-Knopp converged after {:d} steps." + msg = "Log-Sinkhorn-Knopp converged after {} steps." logging.debug(msg, steps) def warn_convergence(): - marginals = keras.ops.logsumexp(log_plan, axis=0) - deviations = keras.ops.abs(marginals) - badness = 100.0 * keras.ops.exp(keras.ops.max(deviations)) - - msg = "Log-Sinkhorn-Knopp did not converge after {:d} steps (badness: {:.1f}%)." + msg = "Log-Sinkhorn-Knopp did not converge after {} steps." - logging.warning(msg, max_steps, badness) + logging.warning(msg, max_steps) def warn_nans(): - msg = "Log-Sinkhorn-Knopp produced NaNs." - logging.warning(msg) + msg = "Log-Sinkhorn-Knopp produced NaNs after {} steps." + logging.warning(msg, steps) keras.ops.cond(contains_nans(log_plan), warn_nans, do_nothing) keras.ops.cond(is_converged(log_plan), log_steps, warn_convergence) diff --git a/bayesflow/utils/optimal_transport/sinkhorn.py b/bayesflow/utils/optimal_transport/sinkhorn.py index 1efa5ae0b..04c268eb0 100644 --- a/bayesflow/utils/optimal_transport/sinkhorn.py +++ b/bayesflow/utils/optimal_transport/sinkhorn.py @@ -3,7 +3,6 @@ from bayesflow.types import Tensor from .. import logging -from ..tensor_utils import is_symbolic_tensor from .euclidean import euclidean @@ -76,9 +75,6 @@ def sinkhorn_plan( # initialize the transport plan from a gaussian kernel plan = keras.ops.exp(cost / -(regularization * keras.ops.mean(cost) + 1e-16)) - if is_symbolic_tensor(plan): - return plan - def contains_nans(plan): return keras.ops.any(keras.ops.isnan(plan)) @@ -106,22 +102,18 @@ def do_nothing(): pass def log_steps(): - msg = "Sinkhorn-Knopp converged after {:d} steps." + msg = "Sinkhorn-Knopp converged after {} steps." logging.info(msg, max_steps) def warn_convergence(): - marginals = keras.ops.sum(plan, axis=0) - deviations = keras.ops.abs(marginals - 1.0) - badness = 100.0 * keras.ops.max(deviations) - - msg = "Sinkhorn-Knopp did not converge after {:d} steps (badness: {:.1f}%)." + msg = "Sinkhorn-Knopp did not converge after {}." - logging.warning(msg, max_steps, badness) + logging.warning(msg, max_steps) def warn_nans(): - msg = "Sinkhorn-Knopp produced NaNs." - logging.warning(msg) + msg = "Sinkhorn-Knopp produced NaNs after {} steps." + logging.warning(msg, steps) keras.ops.cond(contains_nans(plan), warn_nans, do_nothing) keras.ops.cond(is_converged(plan), log_steps, warn_convergence) diff --git a/tests/test_examples/test_examples.py b/tests/test_examples/test_examples.py index 245052636..40135627a 100644 --- a/tests/test_examples/test_examples.py +++ b/tests/test_examples/test_examples.py @@ -9,6 +9,7 @@ def test_bayesian_experimental_design(examples_path): run_notebook(examples_path / "Bayesian_Experimental_Design.ipynb") +@pytest.mark.skip(reason="requires setting up pyabc") @pytest.mark.slow def test_from_abc_to_bayesflow(examples_path): run_notebook(examples_path / "From_ABC_to_BayesFlow.ipynb") From ec0ee2f187efabf6ecf1cfac97a61f5987b2e2b9 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 19:58:40 -0400 Subject: [PATCH 24/46] update dispatch tests for more coverage --- tests/test_utils/test_dispatch.py | 255 +++++++++++++----------------- 1 file changed, 112 insertions(+), 143 deletions(-) diff --git a/tests/test_utils/test_dispatch.py b/tests/test_utils/test_dispatch.py index 85e326445..df25ea78e 100644 --- a/tests/test_utils/test_dispatch.py +++ b/tests/test_utils/test_dispatch.py @@ -1,201 +1,170 @@ import keras import pytest -# Import the dispatch functions -from bayesflow.utils import find_network, find_permutation, find_pooling, find_recurrent_net -from tests.utils import assert_allclose +from bayesflow.utils import find_inference_network, find_distribution, find_summary_network -# --- Tests for find_network.py --- +# --- Tests for find_inference_network.py --- -class DummyMLP: - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs +class DummyInferenceNetwork: + def __init__(self, *a, **kw): + self.args = a + self.kwargs = kw -def test_find_network_with_string(monkeypatch): - # Monkeypatch the MLP entry in bayesflow.networks - monkeypatch.setattr("bayesflow.networks.MLP", DummyMLP) - - net = find_network("mlp", 1, key="value") - assert isinstance(net, DummyMLP) - assert net.args == (1,) - assert net.kwargs == {"key": "value"} +@pytest.mark.parametrize( + "name,expected_class_path", + [ + ("coupling_flow", "bayesflow.networks.CouplingFlow"), + ("flow_matching", "bayesflow.networks.FlowMatching"), + ("consistency_model", "bayesflow.networks.ConsistencyModel"), + ], +) +def test_find_inference_network_by_name(monkeypatch, name, expected_class_path): + # patch the expected class in bayesflow.networks + components = expected_class_path.split(".") + module_path = ".".join(components[:-1]) + class_name = components[-1] -def test_find_network_with_type(): - class CustomNet: - def __init__(self, x): - self.x = x + dummy_cls = DummyInferenceNetwork + monkeypatch.setattr(f"{module_path}.{class_name}", dummy_cls) - net = find_network(CustomNet, 42) - assert isinstance(net, CustomNet) - assert net.x == 42 + net = find_inference_network(name, 1, key="val") + assert isinstance(net, DummyInferenceNetwork) + assert net.args == (1,) + assert net.kwargs == {"key": "val"} -def test_find_network_with_keras_layer(): +def test_find_inference_network_by_keras_layer(): layer = keras.layers.Dense(10) - returned = find_network(layer) - assert returned is layer - - -def test_find_network_invalid_type(): - with pytest.raises(TypeError): - find_network(123) + result = find_inference_network(layer) + assert result is layer -# --- Tests for find_permutation.py --- +def test_find_inference_network_by_keras_model(): + model = keras.models.Sequential() + result = find_inference_network(model) + assert result is model -class DummyRandomPermutation: - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs +def test_find_inference_network_unknown_name(): + with pytest.raises(ValueError): + find_inference_network("unknown_network_name") -class DummySwap: - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs +def test_find_inference_network_invalid_type(): + with pytest.raises(TypeError): + find_inference_network(12345) -class DummyOrthogonalPermutation: - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs +# --- Tests for find_distribution.py --- -def test_find_permutation_random(monkeypatch): - type("dummy_mod", (), {"RandomPermutation": DummyRandomPermutation}) - monkeypatch.setattr("bayesflow.networks.coupling_flow.permutations.RandomPermutation", DummyRandomPermutation) - perm = find_permutation("random", 99, flag=True) - assert isinstance(perm, DummyRandomPermutation) - assert perm.args == (99,) - assert perm.kwargs == {"flag": True} +class DummyDistribution: + def __init__(self, *a, **kw): + self.args = a + self.kwargs = kw @pytest.mark.parametrize( - "name,dummy_cls", - [("swap", DummySwap), ("learnable", DummyOrthogonalPermutation), ("orthogonal", DummyOrthogonalPermutation)], + "name, expected_class_path", + [ + ("normal", "bayesflow.distributions.DiagonalNormal"), + ("student", "bayesflow.distributions.DiagonalStudentT"), + ("student-t", "bayesflow.distributions.DiagonalStudentT"), + ("student_t", "bayesflow.distributions.DiagonalStudentT"), + ], ) -def test_find_permutation_by_name(monkeypatch, name, dummy_cls): - # Inject dummy classes for each permutation type - if name == "swap": - monkeypatch.setattr("bayesflow.networks.coupling_flow.permutations.Swap", dummy_cls) - else: - monkeypatch.setattr("bayesflow.networks.coupling_flow.permutations.OrthogonalPermutation", dummy_cls) - perm = find_permutation(name, "a", b="c") - assert isinstance(perm, dummy_cls) - assert perm.args == ("a",) - assert perm.kwargs == {"b": "c"} - +def test_find_distribution_by_name(monkeypatch, name, expected_class_path): + components = expected_class_path.split(".") + module_path = ".".join(components[:-1]) + class_name = components[-1] -def test_find_permutation_with_keras_layer(): - layer = keras.layers.Activation("relu") - perm = find_permutation(layer) - assert perm is layer + dummy_cls = DummyDistribution + monkeypatch.setattr(f"{module_path}.{class_name}", dummy_cls) + dist = find_distribution(name, 10, a=5) + assert isinstance(dist, DummyDistribution) + assert dist.args == (10,) + assert dist.kwargs == {"a": 5} -def test_find_permutation_with_none(): - res = find_permutation(None) - assert res is None - - -def test_find_permutation_invalid_type(): - with pytest.raises(TypeError): - find_permutation(3.14) +def test_find_distribution_none_returns_none(): + assert find_distribution(None) is None -# --- Tests for find_pooling.py --- +def test_find_distribution_with_keras_layer(): + layer = keras.layers.Dense(3) + result = find_distribution(layer) + assert result is layer -def dummy_pooling_constructor(*args, **kwargs): - return {"args": args, "kwargs": kwargs} +def test_find_distribution_mixture_raises(): + with pytest.raises(ValueError): + find_distribution("mixture") -def test_find_pooling_mean(): - pooling = find_pooling("mean") - # Check that a keras Lambda layer is returned - assert isinstance(pooling, keras.layers.Lambda) - # Test that the lambda function produces a mean when applied to a sample tensor. - - sample = keras.ops.convert_to_tensor([[1, 2], [3, 4]]) - # Keras Lambda layers expect tensors via call(), here we simply call the layer's function. - result = pooling.call(sample) - assert_allclose(result, keras.ops.mean(sample, axis=-2)) - - -@pytest.mark.parametrize("name,func", [("max", keras.ops.max), ("min", keras.ops.min)]) -def test_find_pooling_max_min(name, func): - pooling = find_pooling(name) - assert isinstance(pooling, keras.layers.Lambda) - - sample = keras.ops.convert_to_tensor([[1, 2], [3, 4]]) - result = pooling.call(sample) - assert_allclose(result, func(sample, axis=-2)) - - -def test_find_pooling_learnable(monkeypatch): - # Monkey patch the PoolingByMultiHeadAttention in its module - class DummyPoolingAttention: - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs - - monkeypatch.setattr("bayesflow.networks.transformers.pma.PoolingByMultiHeadAttention", DummyPoolingAttention) - pooling = find_pooling("learnable", 7, option="test") - assert isinstance(pooling, DummyPoolingAttention) - assert pooling.args == (7,) - assert pooling.kwargs == {"option": "test"} +def test_find_distribution_invalid_name(): + with pytest.raises(ValueError): + find_distribution("invalid_name") -def test_find_pooling_with_constructor(): - # Passing a type should result in an instance. - class DummyPooling: - def __init__(self, data): - self.data = data - pooling = find_pooling(DummyPooling, "dummy") - assert isinstance(pooling, DummyPooling) - assert pooling.data == "dummy" +def test_find_distribution_invalid_type(): + with pytest.raises(TypeError): + find_distribution(3.14) -def test_find_pooling_with_keras_layer(): - layer = keras.layers.ReLU() - pooling = find_pooling(layer) - assert pooling is layer +# --- Tests for find_summary_network.py --- -def test_find_pooling_invalid_type(): - with pytest.raises(TypeError): - find_pooling(123) +class DummySummaryNetwork: + def __init__(self, *a, **kw): + self.args = a + self.kwargs = kw -# --- Tests for find_recurrent_net.py --- +@pytest.mark.parametrize( + "name,expected_class_path", + [ + ("deep_set", "bayesflow.networks.DeepSet"), + ("set_transformer", "bayesflow.networks.SetTransformer"), + ("fusion_transformer", "bayesflow.networks.FusionTransformer"), + ("time_series_transformer", "bayesflow.networks.TimeSeriesTransformer"), + ("time_series_network", "bayesflow.networks.TimeSeriesNetwork"), + ], +) +def test_find_summary_network_by_name(monkeypatch, name, expected_class_path): + components = expected_class_path.split(".") + module_path = ".".join(components[:-1]) + class_name = components[-1] + dummy_cls = DummySummaryNetwork + monkeypatch.setattr(f"{module_path}.{class_name}", dummy_cls) -def test_find_recurrent_net_lstm(): - constructor = find_recurrent_net("lstm") - assert constructor is keras.layers.LSTM + net = find_summary_network(name, 22, flag=True) + assert isinstance(net, DummySummaryNetwork) + assert net.args == (22,) + assert net.kwargs == {"flag": True} -def test_find_recurrent_net_gru(): - constructor = find_recurrent_net("gru") - assert constructor is keras.layers.GRU +def test_find_summary_network_by_keras_layer(): + layer = keras.layers.Dense(1) + out = find_summary_network(layer) + assert out is layer -def test_find_recurrent_net_with_keras_layer(): - layer = keras.layers.SimpleRNN(5) - net = find_recurrent_net(layer) - assert net is layer +def test_find_summary_network_by_keras_model(): + model = keras.models.Sequential() + out = find_summary_network(model) + assert out is model -def test_find_recurrent_net_invalid_name(): +def test_find_summary_network_unknown_name(): with pytest.raises(ValueError): - find_recurrent_net("invalid_net") + find_summary_network("unknown_summary_net") -def test_find_recurrent_net_invalid_type(): +def test_find_summary_network_invalid_type(): with pytest.raises(TypeError): - find_recurrent_net(3.1415) + find_summary_network(0.1234) From acf1c722e536c6b99c893b5e65d026076b8eb531 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 25 Apr 2025 20:00:39 -0400 Subject: [PATCH 25/46] Update issue templates (#448) * Hotfix Version 2.0.1 (#431) * fix optimal transport config (#429) * run linter * [skip-ci] bump version to 2.0.1 * Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 36 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..a901605ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a bug report to help us improve BayesFlow +title: "[BUG]" +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Minimal steps to reproduce the behavior: +1. Import '...' +2. Create network '....' +3. Call '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Traceback** +If you encounter an error, please provide a complete traceback to help explain your problem. + +**Environment** +- OS: [e.g. Ubuntu] +- Python Version: [e.g. 3.11] +- Backend: [e.g. jax, tensorflow, pytorch] +- BayesFlow Version: [e.g. 2.0.2] + +**Additional context** +Add any other context about the problem here. + +**Minimality** +- [ ] I verify that my example is minimal, does not rely on third-party packages, and is most likely an issue in BayesFlow. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..da5db4b74 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest a new feature to be implemented in BayesFlow +title: "[FEATURE]" +labels: feature +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From d24f5a3dc0899366fd73419cc8fd6c89f7f36acb Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 20:03:22 -0400 Subject: [PATCH 26/46] Robustify kwargs passing inference networks, add class variables --- bayesflow/approximators/approximator.py | 2 +- .../approximators/continuous_approximator.py | 20 +++++++-- .../model_comparison_approximator.py | 12 +++-- bayesflow/approximators/point_approximator.py | 4 +- .../consistency_models/consistency_model.py | 44 ++++++++----------- .../networks/coupling_flow/coupling_flow.py | 4 +- 6 files changed, 51 insertions(+), 35 deletions(-) diff --git a/bayesflow/approximators/approximator.py b/bayesflow/approximators/approximator.py index e09751b3d..825e93d32 100644 --- a/bayesflow/approximators/approximator.py +++ b/bayesflow/approximators/approximator.py @@ -23,7 +23,7 @@ def build_adapter(cls, **kwargs) -> Adapter: raise NotImplementedError def build_from_data(self, data: Mapping[str, any]) -> None: - self.compute_metrics(**data, stage="training") + self.compute_metrics(**filter_kwargs(data, self.compute_metrics), stage="training") self.built = True @classmethod diff --git a/bayesflow/approximators/continuous_approximator.py b/bayesflow/approximators/continuous_approximator.py index f0c1d68fa..dcb661ca0 100644 --- a/bayesflow/approximators/continuous_approximator.py +++ b/bayesflow/approximators/continuous_approximator.py @@ -32,6 +32,8 @@ class ContinuousApproximator(Approximator): Additional arguments passed to the :py:class:`bayesflow.approximators.Approximator` class. """ + SAMPLE_KEYS = ["summary_variables", "inference_conditions"] + def __init__( self, *, @@ -51,6 +53,7 @@ def build_adapter( inference_variables: Sequence[str], inference_conditions: Sequence[str] = None, summary_variables: Sequence[str] = None, + standardize: bool = True, sample_weight: str = None, ) -> Adapter: """Create an :py:class:`~bayesflow.adapters.Adapter` suited for the approximator. @@ -63,9 +66,12 @@ def build_adapter( Names of the inference conditions in the data summary_variables : Sequence of str, optional Names of the summary variables in the data + standardize : bool, optional + Decide whether to standardize all variables, default is True sample_weight : str, optional Name of the sample weights """ + adapter = Adapter() adapter.to_array() adapter.convert_dtype("float64", "float32") @@ -82,7 +88,9 @@ def build_adapter( adapter = adapter.rename(sample_weight, "sample_weight") adapter.keep(["inference_variables", "inference_conditions", "summary_variables", "sample_weight"]) - adapter.standardize(exclude="sample_weight") + + if standardize: + adapter.standardize(exclude="sample_weight") return adapter @@ -334,12 +342,18 @@ def sample( dict[str, np.ndarray] Dictionary containing generated samples with the same keys as `conditions`. """ + + # Apply adapter transforms to raw simulated / real quantities conditions = self.adapter(conditions, strict=False, stage="inference", **kwargs) - # at inference time, inference_variables are estimated by the networks and thus ignored in conditions - conditions.pop("inference_variables", None) + + # Ensure only keys relevant for sampling are present in the conditions dictionary + conditions = {k: v for k, v in conditions.items() if k in ContinuousApproximator.SAMPLE_KEYS} + conditions = keras.tree.map_structure(keras.ops.convert_to_tensor, conditions) conditions = {"inference_variables": self._sample(num_samples=num_samples, **conditions, **kwargs)} conditions = keras.tree.map_structure(keras.ops.convert_to_numpy, conditions) + + # Back-transform quantities and samples conditions = self.adapter(conditions, inverse=True, strict=False, **kwargs) if split: diff --git a/bayesflow/approximators/model_comparison_approximator.py b/bayesflow/approximators/model_comparison_approximator.py index 03b377537..94e8ebc63 100644 --- a/bayesflow/approximators/model_comparison_approximator.py +++ b/bayesflow/approximators/model_comparison_approximator.py @@ -30,11 +30,13 @@ class ModelComparisonApproximator(Approximator): The network backbone (e.g, an MLP) that is used for model classification. The input of the classifier network is created by concatenating `classifier_variables` and (optional) output of the summary_network. - summary_network: bg.networks.SummaryNetwork, optional + summary_network: bf.networks.SummaryNetwork, optional The summary network used for data summarization (default is None). The input of the summary network is `summary_variables`. """ + SAMPLE_KEYS = ["summary_variables", "inference_conditions"] + def __init__( self, *, @@ -304,9 +306,13 @@ def predict( np.ndarray Predicted posterior model probabilities given `conditions`. """ + + # Apply adapter transforms to raw simulated / real quantities conditions = self.adapter(conditions, strict=False, stage="inference", **kwargs) - # at inference time, model_indices are predicted by the networks and thus ignored in conditions - conditions.pop("model_indices", None) + + # Ensure only keys relevant for sampling are present in the conditions dictionary + conditions = {k: v for k, v in conditions.items() if k in ModelComparisonApproximator.SAMPLE_KEYS} + conditions = keras.tree.map_structure(keras.ops.convert_to_tensor, conditions) output = self._predict(**conditions, **kwargs) diff --git a/bayesflow/approximators/point_approximator.py b/bayesflow/approximators/point_approximator.py index 457b23138..1e407e2a6 100644 --- a/bayesflow/approximators/point_approximator.py +++ b/bayesflow/approximators/point_approximator.py @@ -156,8 +156,10 @@ def log_prob( def _prepare_conditions(self, conditions: Mapping[str, np.ndarray], **kwargs) -> dict[str, Tensor]: """Adapts and converts the conditions to tensors.""" + conditions = self.adapter(conditions, strict=False, stage="inference", **kwargs) - conditions.pop("inference_variables", None) + conditions = {k: v for k, v in conditions.items() if k in ContinuousApproximator.SAMPLE_KEYS} + return keras.tree.map_structure(keras.ops.convert_to_tensor, conditions) def _apply_inverse_adapter_to_estimates( diff --git a/bayesflow/networks/consistency_models/consistency_model.py b/bayesflow/networks/consistency_models/consistency_model.py index 3bcd79a0d..b8d4c56ed 100644 --- a/bayesflow/networks/consistency_models/consistency_model.py +++ b/bayesflow/networks/consistency_models/consistency_model.py @@ -187,7 +187,7 @@ def build(self, xz_shape, conditions_shape=None): self.c_huber = 0.00054 * ops.sqrt(xz_shape[-1]) self.c_huber2 = self.c_huber**2 - ## Calculate discretization schedule in advance + # Calculate discretization schedule in advance # The Jax compiler requires fixed-size arrays, so we have # to store all the discretized_times in one matrix in advance # and later only access the relevant entries. @@ -213,34 +213,24 @@ def build(self, xz_shape, conditions_shape=None): disc = ops.convert_to_numpy(self._discretize_time(n)) discretized_times[i, : len(disc)] = disc discretization_map[n] = i + # Finally, we convert the vectors to tensors self.discretized_times = ops.convert_to_tensor(discretized_times, dtype="float32") self.discretization_map = ops.convert_to_tensor(discretization_map) - def call( - self, - xz: Tensor, - conditions: Tensor = None, - inverse: bool = False, - **kwargs, - ): - if inverse: - return self._inverse(xz, conditions=conditions, **kwargs) - return self._forward(xz, conditions=conditions, **kwargs) - - def _forward_train(self, x: Tensor, noise: Tensor, t: Tensor, conditions: Tensor = None, **kwargs) -> Tensor: - """Forward function for training. Calls consistency function with - noisy input - """ + def _forward_train( + self, x: Tensor, noise: Tensor, t: Tensor, conditions: Tensor = None, training: bool = False, **kwargs + ) -> Tensor: + """Forward function for training. Calls consistency function with noisy input""" inp = x + t * noise - return self.consistency_function(inp, t, conditions=conditions, **kwargs) + return self.consistency_function(inp, t, conditions=conditions, training=training) def _forward(self, x: Tensor, conditions: Tensor = None, **kwargs) -> Tensor: # Consistency Models only learn the direction from noise distribution # to target distribution, so we cannot implement this function. raise NotImplementedError("Consistency Models are not invertible") - def _inverse(self, z: Tensor, conditions: Tensor = None, **kwargs) -> Tensor: + def _inverse(self, z: Tensor, conditions: Tensor = None, training: bool = False, **kwargs) -> Tensor: """Generate random draws from the approximate target distribution using the multistep sampling algorithm from [1], Algorithm 1. @@ -249,7 +239,9 @@ def _inverse(self, z: Tensor, conditions: Tensor = None, **kwargs) -> Tensor: z : Tensor Samples from a standard normal distribution conditions : Tensor, optional, default: None - Conditions for a approximate conditional distribution + Conditions for the approximate conditional distribution + training : bool, optional, default: True + Whether internal layers (e.g., dropout) should behave in train or inference mode. **kwargs : dict, optional, default: {} Additional keyword arguments. Include `steps` (default: 10) to adjust the number of sampling steps. @@ -263,15 +255,17 @@ def _inverse(self, z: Tensor, conditions: Tensor = None, **kwargs) -> Tensor: x = keras.ops.copy(z) * self.max_time discretized_time = keras.ops.flip(self._discretize_time(steps), axis=-1) t = keras.ops.full((*keras.ops.shape(x)[:-1], 1), discretized_time[0], dtype=x.dtype) - x = self.consistency_function(x, t, conditions=conditions) + + x = self.consistency_function(x, t, conditions=conditions, training=training) + for n in range(1, steps): noise = keras.random.normal(keras.ops.shape(x), dtype=keras.ops.dtype(x), seed=self.seed_generator) x_n = x + keras.ops.sqrt(keras.ops.square(discretized_time[n]) - self.eps**2) * noise t = keras.ops.full_like(t, discretized_time[n]) - x = self.consistency_function(x_n, t, conditions=conditions) + x = self.consistency_function(x_n, t, conditions=conditions, training=training) return x - def consistency_function(self, x: Tensor, t: Tensor, conditions: Tensor = None, **kwargs) -> Tensor: + def consistency_function(self, x: Tensor, t: Tensor, conditions: Tensor = None, training: bool = False) -> Tensor: """Compute consistency function. Parameters @@ -282,8 +276,8 @@ def consistency_function(self, x: Tensor, t: Tensor, conditions: Tensor = None, Vector of time samples in [eps, T] conditions : Tensor The conditioning vector - **kwargs : dict, optional, default: {} - Additional keyword arguments passed to the network. + training : bool, optional, default: True + Whether internal layers (e.g., dropout) should behave in train or inference mode. """ if conditions is not None: @@ -291,7 +285,7 @@ def consistency_function(self, x: Tensor, t: Tensor, conditions: Tensor = None, else: xtc = ops.concatenate([x, t], axis=-1) - f = self.output_projector(self.subnet(xtc, **kwargs)) + f = self.output_projector(self.subnet(xtc, training=training)) # Compute skip and out parts (vectorized, since self.sigma2 is of shape (1, input_dim) # Thus, we can do a cross product with the time vector which is (batch_size, 1) for diff --git a/bayesflow/networks/coupling_flow/coupling_flow.py b/bayesflow/networks/coupling_flow/coupling_flow.py index ee78f180e..203962b0f 100644 --- a/bayesflow/networks/coupling_flow/coupling_flow.py +++ b/bayesflow/networks/coupling_flow/coupling_flow.py @@ -152,7 +152,7 @@ def _forward( z = x log_det = keras.ops.zeros(keras.ops.shape(x)[:-1]) for layer in self.invertible_layers: - z, det = layer(z, conditions=conditions, inverse=False, training=training, **kwargs) + z, det = layer(z, conditions=conditions, inverse=False, training=training) log_det += det if density: @@ -168,7 +168,7 @@ def _inverse( x = z log_det = keras.ops.zeros(keras.ops.shape(z)[:-1]) for layer in reversed(self.invertible_layers): - x, det = layer(x, conditions=conditions, inverse=True, training=training, **kwargs) + x, det = layer(x, conditions=conditions, inverse=True, training=training) log_det += det if density: From 463c0c7e7f8c5f3f4a990e9b6bc002dd9b6c1130 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 20:13:41 -0400 Subject: [PATCH 27/46] fix convergence method to debug for non-log sinkhorn --- bayesflow/utils/optimal_transport/sinkhorn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bayesflow/utils/optimal_transport/sinkhorn.py b/bayesflow/utils/optimal_transport/sinkhorn.py index 04c268eb0..f7e0ba835 100644 --- a/bayesflow/utils/optimal_transport/sinkhorn.py +++ b/bayesflow/utils/optimal_transport/sinkhorn.py @@ -104,7 +104,7 @@ def do_nothing(): def log_steps(): msg = "Sinkhorn-Knopp converged after {} steps." - logging.info(msg, max_steps) + logging.debug(msg, max_steps) def warn_convergence(): msg = "Sinkhorn-Knopp did not converge after {}." From 8f3739c6c0d5731030228ddd5b4d0021295f7257 Mon Sep 17 00:00:00 2001 From: stefanradev93 Date: Fri, 25 Apr 2025 20:18:30 -0400 Subject: [PATCH 28/46] Bump optimal transport default to False --- bayesflow/networks/flow_matching/flow_matching.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bayesflow/networks/flow_matching/flow_matching.py b/bayesflow/networks/flow_matching/flow_matching.py index 7a097d340..3c0190467 100644 --- a/bayesflow/networks/flow_matching/flow_matching.py +++ b/bayesflow/networks/flow_matching/flow_matching.py @@ -55,7 +55,7 @@ def __init__( self, subnet: str | keras.Layer = "mlp", base_distribution: str | Distribution = "normal", - use_optimal_transport: bool = True, + use_optimal_transport: bool = False, loss_fn: str | keras.Loss = "mse", integrate_kwargs: dict[str, any] = None, optimal_transport_kwargs: dict[str, any] = None, @@ -82,7 +82,8 @@ def __init__( The base probability distribution from which samples are drawn, such as "normal". Default is "normal". use_optimal_transport : bool, optional - Whether to apply optimal transport for improved training stability. Default is True. + Whether to apply optimal transport for improved training stability. Default is False. + Note: this will increase training time by approximately ~2.5 times, but may lead to faster inference. loss_fn : str, optional The loss function used for training, such as "mse". Default is "mse". integrate_kwargs : dict[str, any], optional From 40eccd4ed678ce49710ccff3f9239a23e2dedae4 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:42:01 -0400 Subject: [PATCH 29/46] use logging.info for backend selection instead of logging.debug --- bayesflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bayesflow/__init__.py b/bayesflow/__init__.py index 7a358341e..5a28ffe2e 100644 --- a/bayesflow/__init__.py +++ b/bayesflow/__init__.py @@ -33,7 +33,7 @@ def setup(): from bayesflow.utils import logging - logging.debug(f"Using backend {keras.backend.backend()!r}") + logging.info(f"Using backend {keras.backend.backend()!r}") if keras.backend.backend() == "torch": import torch From 8903089082979501eb781831267e181b1c36679a Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:42:09 -0400 Subject: [PATCH 30/46] fix model comparison approximator --- bayesflow/approximators/model_comparison_approximator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bayesflow/approximators/model_comparison_approximator.py b/bayesflow/approximators/model_comparison_approximator.py index 94e8ebc63..1b9d198ff 100644 --- a/bayesflow/approximators/model_comparison_approximator.py +++ b/bayesflow/approximators/model_comparison_approximator.py @@ -35,7 +35,7 @@ class ModelComparisonApproximator(Approximator): The input of the summary network is `summary_variables`. """ - SAMPLE_KEYS = ["summary_variables", "inference_conditions"] + SAMPLE_KEYS = ["summary_variables", "classifier_conditions"] def __init__( self, From cbc86b8e8f2a5ffc11aa274965feaa6f8aee5555 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:42:16 -0400 Subject: [PATCH 31/46] improve docs and type hints --- bayesflow/simulators/lambda_simulator.py | 6 +++--- bayesflow/simulators/model_comparison_simulator.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bayesflow/simulators/lambda_simulator.py b/bayesflow/simulators/lambda_simulator.py index c6baa7edb..aadefea6e 100644 --- a/bayesflow/simulators/lambda_simulator.py +++ b/bayesflow/simulators/lambda_simulator.py @@ -1,4 +1,4 @@ -from collections.abc import Callable, Sequence, Mapping +from collections.abc import Callable, Sequence import numpy as np @@ -12,13 +12,13 @@ class LambdaSimulator(Simulator): """Implements a simulator based on a sampling function.""" - def __init__(self, sample_fn: Callable[[Sequence[int]], Mapping[str, any]], *, is_batched: bool = False): + def __init__(self, sample_fn: Callable[[Sequence[int]], dict[str, any]], *, is_batched: bool = False): """ Initialize a simulator based on a simple callable function Parameters ---------- - sample_fn : Callable[[Sequence[int]], Mapping[str, any]] + sample_fn : Callable[[Sequence[int]], dict[str, any]] A function that generates samples. It should accept `batch_shape` as its first argument (if `is_batched=True`), followed by keyword arguments. is_batched : bool, optional diff --git a/bayesflow/simulators/model_comparison_simulator.py b/bayesflow/simulators/model_comparison_simulator.py index e05f4bbc4..60174ef92 100644 --- a/bayesflow/simulators/model_comparison_simulator.py +++ b/bayesflow/simulators/model_comparison_simulator.py @@ -1,4 +1,4 @@ -from collections.abc import Sequence +from collections.abc import Callable, Sequence import numpy as np from bayesflow.types import Shape @@ -22,10 +22,10 @@ def __init__( p: Sequence[float] = None, logits: Sequence[float] = None, use_mixed_batches: bool = True, - shared_simulator: Simulator | FunctionType = None, + shared_simulator: Simulator | Callable[[Sequence[int]], dict[str, any]] = None, ): """ - Initialize a multi-model simulator that can generate data for mixture / model comparison problems. + Initialize a multimodel simulator that can generate data for mixture / model comparison problems. Parameters ---------- @@ -40,7 +40,7 @@ def __init__( use_mixed_batches : bool, optional If True, samples in a batch are drawn from different models. If False, the entire batch is drawn from a single model chosen according to the model probabilities. Default is True. - shared_simulator : Simulator or FunctionType, optional + shared_simulator : Simulator or Callable, optional A shared simulator whose outputs are passed to all model simulators. If a function is provided, it is wrapped in a `LambdaSimulator` with batching enabled. """ From 77ddc5ac9f176386831a279d9f4df96d70ae9fef Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:44:13 -0400 Subject: [PATCH 32/46] improve One-Sample T-Test Notebook: - use torch as default backend - reduce range of N so users of jax won't be stuck with a slow notebook - use BayesFlow built-in MLP instead of keras.Sequential solution - general code cleanup --- examples/One_Sample_TTest.ipynb | 325 +++++++++++++++++++------------- 1 file changed, 193 insertions(+), 132 deletions(-) diff --git a/examples/One_Sample_TTest.ipynb b/examples/One_Sample_TTest.ipynb index 73b0ba6db..d75dcff53 100644 --- a/examples/One_Sample_TTest.ipynb +++ b/examples/One_Sample_TTest.ipynb @@ -22,13 +22,29 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:13.774900Z", + "start_time": "2025-04-26T02:34:11.487313Z" + } + }, + "source": [ + "import numpy as np\n", + "\n", + "import os\n", + "if \"KERAS_BACKEND\" not in os.environ:\n", + " # set this to \"torch\", \"tensorflow\", or \"jax\"\n", + " os.environ[\"KERAS_BACKEND\"] = \"torch\"\n", + "\n", + "import keras\n", + "import bayesflow as bf" + ], "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ + "INFO:bayesflow:Using backend 'torch'\n", "WARNING:bayesflow:\n", "When using torch backend, we need to disable autograd by default to avoid excessive memory usage. Use\n", "\n", @@ -39,17 +55,7 @@ ] } ], - "source": [ - "import numpy as np\n", - "\n", - "import os\n", - "if \"KERAS_BACKEND\" not in os.environ:\n", - " # set this to \"torch\", \"tensorflow\", or \"jax\"\n", - " os.environ[\"KERAS_BACKEND\"] = \"jax\"\n", - "\n", - "import keras\n", - "import bayesflow as bf" - ] + "execution_count": 1 }, { "cell_type": "markdown", @@ -76,21 +82,24 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:13.780691Z", + "start_time": "2025-04-26T02:34:13.777756Z" + } + }, "source": [ "def context(batch_shape, n=None):\n", " if n is None:\n", - " n = np.random.randint(5, 50)\n", - " return dict(n = n)\n", + " n = np.random.randint(20, 30)\n", + " return dict(n=n)\n", "\n", "def prior_null():\n", - " return dict(mu = 0.0)\n", + " return dict(mu=0.0)\n", "\n", "def prior_alternative():\n", " mu = np.random.normal(loc=0, scale=1)\n", - " return dict(mu = mu)\n", + " return dict(mu=mu)\n", "\n", "def likelihood(n, mu):\n", " x = np.random.normal(loc=mu, scale=1, size=n)\n", @@ -101,8 +110,11 @@ "simulator = bf.simulators.ModelComparisonSimulator(\n", " simulators=[simulator_null, simulator_alternative], \n", " use_mixed_batches=True, \n", - " shared_simulator=context)" - ] + " shared_simulator=context,\n", + ")" + ], + "outputs": [], + "execution_count": 2 }, { "cell_type": "markdown", @@ -113,27 +125,32 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:13.840198Z", + "start_time": "2025-04-26T02:34:13.837161Z" + } + }, + "source": [ + "data = simulator.sample(100)\n", + "print(\"n =\", data[\"n\"])\n", + "for key, value in data.items():\n", + " print(key + \" shape:\", np.array(value).shape)" + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "n = 37\n", + "n = 20\n", "n shape: ()\n", "mu shape: (100, 1)\n", - "x shape: (100, 37)\n", + "x shape: (100, 20)\n", "model_indices shape: (100, 2)\n" ] } ], - "source": [ - "data = simulator.sample(100)\n", - "print(\"n =\", data[\"n\"])\n", - "for key, value in data.items():\n", - " print(key + \" shape:\", np.array(value).shape)" - ] + "execution_count": 3 }, { "cell_type": "markdown", @@ -155,9 +172,12 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:13.884625Z", + "start_time": "2025-04-26T02:34:13.882973Z" + } + }, "source": [ "adapter = (\n", " bf.Adapter()\n", @@ -166,10 +186,12 @@ " .as_set(\"x\")\n", " .rename(\"n\", \"classifier_conditions\")\n", " .rename(\"x\", \"summary_variables\")\n", - " .drop('mu')\n", + " .drop(\"mu\")\n", " .convert_dtype(\"float64\", \"float32\")\n", " )" - ] + ], + "outputs": [], + "execution_count": 4 }, { "cell_type": "markdown", @@ -188,8 +210,17 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:13.930048Z", + "start_time": "2025-04-26T02:34:13.928375Z" + } + }, + "source": [ + "processed_data=adapter(data)\n", + "for key, value in processed_data.items():\n", + " print(key + \" shape:\", value.shape)" + ], "outputs": [ { "name": "stdout", @@ -197,15 +228,11 @@ "text": [ "model_indices shape: (100, 2)\n", "classifier_conditions shape: (100, 1)\n", - "summary_variables shape: (100, 37, 1)\n" + "summary_variables shape: (100, 20, 1)\n" ] } ], - "source": [ - "processed_data=adapter(data)\n", - "for key, value in processed_data.items():\n", - " print(key + \" shape:\", value.shape)" - ] + "execution_count": 5 }, { "cell_type": "markdown", @@ -231,15 +258,18 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:13.996060Z", + "start_time": "2025-04-26T02:34:13.974940Z" + } + }, "source": [ - "summary_network = bf.networks.DeepSet(summary_dim=4, dropout=0.0)\n", - "classifier_network = keras.Sequential(\n", - " [keras.layers.Dense(32, activation=\"silu\") for _ in range(4)]\n", - ")" - ] + "summary_network = bf.networks.DeepSet(summary_dim=8, dropout=None)\n", + "classifier_network = bf.networks.MLP(widths=[32] * 4, activation=\"silu\", dropout=None)" + ], + "outputs": [], + "execution_count": 6 }, { "cell_type": "markdown", @@ -250,17 +280,22 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:14.021809Z", + "start_time": "2025-04-26T02:34:14.019827Z" + } + }, "source": [ "approximator = bf.approximators.ModelComparisonApproximator(\n", - " num_models=2, \n", + " num_models=2,\n", " classifier_network=classifier_network,\n", - " summary_network=summary_network, \n", - " adapter=adapter\n", + " summary_network=summary_network,\n", + " adapter=adapter,\n", ")" - ] + ], + "outputs": [], + "execution_count": 7 }, { "cell_type": "markdown", @@ -275,14 +310,19 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:14.065229Z", + "start_time": "2025-04-26T02:34:14.063948Z" + } + }, "source": [ - "num_batches = 64\n", + "num_batches_per_epoch = 64\n", "batch_size = 512\n", - "epochs = 20" - ] + "epochs = 32" + ], + "outputs": [], + "execution_count": 8 }, { "cell_type": "markdown", @@ -293,14 +333,19 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:34:14.118477Z", + "start_time": "2025-04-26T02:34:14.109925Z" + } + }, "source": [ - "learning_rate = keras.optimizers.schedules.CosineDecay(1e-4, decay_steps=epochs*num_batches, alpha=1e-5)\n", - "optimizer = keras.optimizers.Adam(learning_rate=learning_rate, clipnorm=1.0)\n", + "learning_rate = keras.optimizers.schedules.CosineDecay(1e-4, decay_steps=epochs * num_batches_per_epoch)\n", + "optimizer = keras.optimizers.Adam(learning_rate=learning_rate)\n", "approximator.compile(optimizer=optimizer)" - ] + ], + "outputs": [], + "execution_count": 9 }, { "cell_type": "markdown", @@ -311,18 +356,18 @@ }, { "cell_type": "code", - "execution_count": null, "metadata": {}, - "outputs": [], "source": [ "history = approximator.fit(\n", " epochs=epochs,\n", - " num_batches=num_batches,\n", + " num_batches=num_batches_per_epoch,\n", " batch_size=batch_size,\n", " simulator=simulator,\n", - " adapter=adapter\n", + " adapter=adapter,\n", ")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -333,23 +378,26 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:35:17.051898Z", + "start_time": "2025-04-26T02:35:16.960475Z" + } + }, + "source": "f = bf.diagnostics.plots.loss(history=history)", "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABjUAAAGGCAYAAAAzegNcAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd8U/X+P/BXRpvudO8NpVAoUAqUvZdcvXgVF8oScXP1J+pVr4rjevUq18t1c70MEcR1xfEVEGWPsgqFskp36d5JZ5Im5/dH6GljW2hL2iTt6/l48CA9K++k+TTJeZ/P+y0RBEEAERERERERERERERGRlZNaOgAiIiIiIiIiIiIiIqKOYFKDiIiIiIiIiIiIiIhsApMaRERERERERERERERkE5jUICIiIiIiIiIiIiIim8CkBhERERERERERERER2QQmNYiIiIiIiIiIiIiIyCYwqUFERERERERERERERDaBSQ0iIiIiIiIiIiIiIrIJTGoQEREREREREREREZFNYFKDiIiIiMhCwsPDIZFIsHHjRkuHYlYSiaTT/6ZMmdItsbzyyiuQSCR45ZVXuuX4RERERETUs+SWDoCIiIiIiHqXxYsXt1pWVFSEX375pd31AwcO7Pa4LGHjxo1YunQpFi9e3OuSV0RERERElsCkBhERERERmVVbJ+/37dsnJjV68uT+448/jrvvvhve3t49dp9ERERERNR9mNQgIiIiIqJey9vbmwkNIiIiIqJehD01iIiIiIhsSF5eHlasWIGoqCg4ODhAqVRi/PjxWLt2LfR6fZv7fPPNN5gxYwa8vLxgZ2cHLy8vxMTEYPny5Th79qzJtiqVCi+++CJiY2Ph7OwMhUKBwMBAjB8/Hi+//DJ0Op3ZH1PLvhe5ublYtmwZQkJCYGdnhyVLlojbfffdd3jggQcwZMgQeHh4wMHBAREREbj//vuRmpp63WO35fLly3jooYfQr18/8fmcNGkSNm/e3G68giDgu+++w8033wx/f3/Y29vD398fEyZMwD/+8Q/U19cDMPZMWbp0KQDgs88+u2YPkbq6Orz11lsYMWIEXF1d4eTkhMGDB+PFF19EZWVlqxiys7MhkUgQHh4OvV6Pd999F3FxcXBxcYFEIoFarYabmxvkcjmuXLnS7mOZO3cuJBIJPvroo3a3ISIiIiKyJpypQURERERkI06cOIE5c+agoqICoaGhuPXWW6FSqbBv3z4cOXIE27Ztw48//gh7e3txn9deew2rVq2CXC7HuHHjEBQUBJVKhdzcXKxbtw6DBw/G0KFDARhPrE+YMAHnzp2Dj48Ppk+fDmdnZxQVFeHSpUs4cuQInnrqKbi7u3fL40tLS0NcXBzs7e0xfvx4CIJgMsvizjvvhEKhQExMDKZNm4bGxkacO3cOGzZswNdff41du3Zh3LhxHb6/b775BosWLUJDQwMGDhyIuXPnQqVS4dixY1i4cCH27NmD9evXm+yj0+lw991347vvvoNUKsXo0aMxbdo0lJWV4cKFC3juuedw1113ITw8HPPnz8fRo0dx+PBh9OvXDxMmTBCP07KHSEVFBaZPn47k5GS4ublh2rRpsLOzw/79+/HGG2/giy++wJ49exAeHt7qMQiCgNtuuw07d+7ExIkTMWjQIJw/fx5ubm5YsmQJ3n//fXzyySd44403Wu2bkZGBnTt3ws3NDYsWLerw80ZEREREZElMahARERER2QCNRoM77rgDFRUVePjhh/Hee+/Bzs4OAJCZmYnp06fjl19+wauvviqewNZoNHjrrbfg4uKCkydPIjo62uSYOTk54qwCAPj2229x7tw53HTTTfjhhx/E4wOAwWDAwYMH4eTk1G2P8YsvvsB9992H//73v1AoFK3Wb9myBTfffDOcnZ3FZYIg4OOPP8Zjjz2GBx98ECkpKZBIJNe9r5SUFCxcuBASiQT/+9//cNttt4nrcnJycMstt2DDhg2YMmWKyQn/5557Dt999x3Cw8Px/fffY9iwYSax7NmzBx4eHgCA1atXY+PGjTh8+DAmTJjQbi+RRx99FMnJyUhISMDPP/8MLy8vAEBNTQ3uvPNO7NixA/feey8OHz7cat/c3FwYDAakpKRgwIABJutWrFiBDz74AP/973/x8ssvt3pOP/74YwiCgMWLF8PFxeW6zxkRERERkTVg+SkiIiIiIhvwzTffICcnB4GBgVizZo1JwiEyMhKrV68GALz//vtoaGgAAKjVatTX1yMyMrJVQgMAwsLCTGYMFBcXAwBmzpxpcnwAkEqlmDx5ssksEHPz9PTEBx980GZCAwDuuusuk4QGAEgkEjz66KMYO3Yszp8/j4sXL3bovt544w1oNBr87W9/M0loAMbnZd26dQCA9957T1xeUlKCDz74AIAxAdQyodEUy/Tp06FUKjsUA2BMSnzzzTeQSCT4z3/+IyY0AMDFxQWffvopHBwccOTIERw5cqTNY/z9739vldAAgKioKNx0000oKSnBN998Y7Kuvr4e69evh0QiwWOPPdbheImIiIiILI1JDSIiIiIiG7Bv3z4AwN13393mSf/bbrsNHh4eqK6uRlJSEgDAx8cH4eHhOHv2LFauXIkLFy5c8z5GjRoFAHj77bexadMmVFRUmPdBXMeMGTOumxBIT0/HBx98gCeffBLLli3DkiVLsGTJEjEh015vjZYMBgN27NgBwJgoacvIkSPh4uKC06dPi0mivXv3QqvVIj4+HvHx8Z15aO06cOAADAYD4uLixDJgLQUFBWH27Nni/bfl9ttvb/f4TzzxBACIyZgmX3zxBSorKzFjxow2E15ERERERNaK5aeIiIiIiGxAfn4+ACAiIqLN9RKJBBEREaisrBS3BYBNmzZh/vz5ePfdd/Huu+/C09MTCQkJmDlzJhYuXGjSs2LKlCn4y1/+gnfeeQeLFy+GRCJBVFQUxo8fj3nz5uGWW26BVNp910W11TOiiV6vx+OPP461a9dCEIR2t1Or1de9n/LycnG7kJCQDm0fFBSEnJwcAKb9MG7U9X6vANCvXz+TbVvy9fW9ZkmwmTNnYtCgQTh27BiSkpLEZMyHH34IAHj88ce7HDsRERERkSUwqUFERERE1ItNnDgR2dnZ+Pnnn7F//34cOXIEv/zyC3bs2IFVq1Zh27ZtmD59urj9W2+9hYcffhg//fQTDh06hMOHD2PDhg3YsGEDRo0ahb1797YqAWUujo6O7a7797//jU8++QT+/v549913MW7cOPj5+cHBwQEAsGDBAmzduvWaCY8mBoNBvL148eLrbt9eOSxrcK3nDDAmu1asWIFHH30UH3zwATZs2IDExEScPn0a4eHhuPnmm3soUiIiIiIi82BSg4iIiIjIBgQFBQEwNgVvT1ZWlsm2TRwdHTF//nzMnz8fAFBaWooXX3wR//nPf3D//feLMxCahIeHY8WKFVixYgUA4MSJE7jvvvtw4sQJvP3223j11VfN9rg66uuvvwYArF27Fn/84x9brU9LS+vwsby9veHo6Ij6+nqsXr3aZLbKtYSGhgIALl261OH7up6O/F6b1v3+99pRixYtwgsvvIAvv/wSq1evFktRPfLII90684aIiIiIqDvwEywRERERkQ2YMmUKAOCrr74Sezy0tG3bNlRWVsLV1fW6/R58fHzw9ttvAzA2qq6srLzm9qNGjcKjjz4KAEhOTu588GbQ1N8jLCys1brz5893Ki6ZTIaZM2cCaE6WdMS0adNgb2+PpKQknDp1qkP7NDVWb2xsbHP9pEmTIJVKkZycjDNnzrRaX1hYiJ07dwIApk6d2uFYW3J2dsayZcvQ0NCAv//97/j222/h4OCAZcuWdel4RERERESWxKQGEREREZENuOOOOxAaGoqCggI89dRTJifJs7KysHLlSgDAihUrxJJMOTk5+O9//9tmn4mffvoJAODh4QE3NzcAxsRIU+PqlnQ6nXhiva2kQk8YNGgQAGMviJbxFRYWYtGiRe0mDdqzatUq2Nvb45lnnsFnn33W6jEDwLlz5/Ddd9+JP/v6+uKRRx4BYPx9nDt3zmR7QRCwZ88eqFQqcVlwcDAAtNukPTQ0FHfccQcEQcBDDz2E8vJycV1tbS0efPBBNDQ0YNy4cRg3blynHmNLjz/+OKRSKd59911otVrcc8898PLy6vLxiIiIiIgsheWniIiIiIgs7PXXX8cnn3zS7vqPPvoII0aMwLfffos5c+bg448/xvbt2zFmzBhUV1djz549aGhowOzZs7Fq1Spxv8rKSixfvhyPPvoohg8fLjajTktLw+nTpyGRSPDOO+9AJpMBAPbv349///vf8Pb2RlxcHHx9fVFdXY2jR4+ipKQEQUFBePbZZ7v3yWjHCy+8gJ07d+LTTz/F3r17MWLECKjVauzfvx+RkZH405/+hG3btnX4eCNGjMDmzZuxZMkSLFmyBC+++CJiYmLg4+ODiooKpKSkIC8vD3fddRduu+02cb+3334bWVlZ+PHHHzFs2DAkJCQgIiICZWVlOH/+PPLz85GVlQWlUgkAGDNmDAIDA3H69GmMGDECsbGxsLOzQ3R0NJ555hkAxkTNpUuXcOzYMfTr1w9Tp06FXC7H/v37UVpaioiICGzZsuWGnr/w8HD88Y9/xPfffw+ADcKJiIiIyHYxqUFEREREZGGZmZnX7KnQNNNi1KhRSE5Oxj/+8Q/s2LED27Ztg0KhQFxcHBYtWoQHHngAcnnzR/x+/fphzZo12L9/P86dO4ft27dDEAQEBQVh0aJF+POf/2xSqmrJkiVwdHTEoUOHcOHCBezfvx9KpRKhoaF48skn8eCDD1rs6v6EhAScPHkSL774Ik6cOIEff/wRISEhWLFiBV588UWx/0d7JBJJq2V33HEHRo0ahffeew+//vorDh8+DL1eDz8/P/Tv3x+PP/642Iekib29Pb7//nt8+eWX2LhxI5KSknDy5El4eXkhKioKTz75JPz9/U22/+WXX/DXv/4ViYmJOHPmDAwGAyZPniwmNby8vHDkyBG89957+Oqrr7Br1y4YDAZERERg+fLlePrpp+Hh4XHDz+Hs2bPx/fffY+zYsRgxYsQNH4+IiIiIyBIkgiAIlg6CiIiIiIioOzz77LN455138Pbbb4tJhL5qwoQJOHz4ML744gvcc889lg6HiIiIiKhL2FODiIiIiIh6JUEQcPz4cQBAdHS0haOxrB07duDw4cMIDQ1tNfuEiIiIiMiWsPwUERERERH1OgsXLkRKSgrOnDmDoKAgzJw509Ih9bjy8nL85S9/QWVlJbZv3w7A2BPEzs7OwpEREREREXUdy08REREREVGvI5FI4OrqigkTJmD16tWIiYmxdEg9Ljs7GxEREZDL5YiMjMTKlSvx4IMPWjosIiIiIqIbwqQGERERERERERERERHZBPbUICIiIiIiIiIiIiIim8CkBhERERERERERERER2QQmNTpIEASo1WqwWhcRERERERERERERkWUwqdFB1dXVUCqVqK6utnQoZEaCIKCqqorJKiJwPBC1xPFA1IzjgagZxwNRM44HomYcD0TNemIcMKlBREREREREREREREQ2gUkNIiIiIiIiIiIiIiKyCUxqEBERERERERERERGRTWBSg4iIiIiIiIiIiIiIbAKTGkREREREREREREREZBOY1CAiIiIiIiIiIiIiIpvApAYREREREREREREREdkEJjWIiIiIiIiIiIiIiMgmyC0dABERERERERERkbXT6XTQ6/WWDoOskCAI0Gq1aGhogEQisXQ4RGYlk8lgZ2dn6TBMMKlBRERERERERETUDrVajbKyMmg0GkuHQlbMYDCgvLzc0mEQdQuFQgFvb2+4ublZOhQATGoQERERERERERG1Sa1WIz8/Hy4uLvD29oadnR2vxKdWBEGAXq+HTCbj64N6FUEQoNPpoFKpkJ+fDwBWkdhgUoOIiIh6nLq6HqUVNfD1doWrs4OlwyEiIiIialNZWRlc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pafkchl8PFl6irouqEUJqvzCKovFQURERERERNSlpMaxY8cwb948eHt7t7tNSEgIiorabordV0kkEgyNCYaLswMAoLZOgzMXrkAQhOvuOygqAGvfXoi7/th61saX3x/nrA0iIjJRXlkLXaMxce7j5cLSU3RD3Fwd4epi/PxSpa5DTe21Z5sSERERERERdZcuneHQaDRwc7t2GYuqqipIpTyB8ntyuQwjYkNhbycHAJRV1CA1o2PJH3t7OR68bxL+/fpdCAlonrXx6RcH8ecXtyInr7zb4iYiIttS2LL0lI/SgpFQb2E6W4MNw4mIiIiIiMgyupR1iIyMxIkTJ665TWJiIgYOHNiloHo7J0d7DB8SAolEAgDIvlKOvIKOnxyIGRCIte8sxJ23jIQExmNcujpr46sfTsBg4KwNIqK+zGAwoKSsGsDV0lNeLhaOiHqDQD93SKXGzx0FxVUdmmlKREREREREZG5dSmrcfvvtOHz4MDZs2NDm+tWrV+PcuXO46667bii43szT3RmDBwSKP5+/XICKqtoO76+wl+OhhZPx79fvRnCABwBAq2vEf7YcwBMvfYXc/Aqzx0xERLahtLwGjVdLT/l6u3LmJJmFvb0cPld7g2m0jSgtr7FwRERERERERNQXdeksxzPPPINBgwbhgQcewMyZM7F7924AwLPPPouJEyfiL3/5C4YPH47HH3/crMH2NsGBHggP8QIACIKA5HNXUFev7dQxBkcH4j/vLML8P8SLszYupBXgoWc/x9c/ctYGEVFfVFTaXHoqwJelp8h8gvw9xNv5RSxBRURERERERD2vS0kNFxcXHDx4EHfffTf27duHQ4cOQRAErF69GkeOHMGdd96J3377DQqFwtzx9jrR/fzh7WksC6LVNeJUSq54dW1HKezleGTxFKx57S6TWRtrNx/Aky9/1anSVkREZNv0eoN4Bb2dXAYvD2cLR0S9iY+XCxT2xr5gJWXV0GobLRwRERERERER9TVdrkfh4eGBLVu2oKioCNu3b8fmzZvx448/oqCgAFu3boWHh8f1D0KQSCQYFhMCZydjAqimtgFnLuR1qU71kIFBWPv2Qtw+t3nWxvnLBVj+zCZ8839JnLVBRNQHlFZUi8lxPx83lp4is5JIJAj0cwdgnGVaUFxl0XiIiIiIiIio77nhMx1eXl6YM2cOFixYgJtvvhl+fn7miKtPsbOTYURsKOzkMgBAaXk1LmcUd+lYDgo7PLpkCv716l1iiQitrhGfbNqH/7fqa87aICLq5YpK1OJtf5aeom4QFNCyBFWV5QIhIiIiIiKiPomXb1oJZycFhg8JgURinGGRdaUM+YVdT0DEDgrCf95ZiNvmjhBnbZxLzcfyZzbhW87aICLqlYylp6oBAPZ2cpaeom7h4qyAu5sTAKC6pgHq6noLR0RERERERETWQBAEFJWorr/hDZJ3Zadp06Z1aDuJRCI2Eafr8/JwQcyAAJxPLQBgLB3l5GgPD/eunZRyUNjhsSVTMWF0f7zz0S4UllRBq2vEx5v24eDxNDz7yGyTqy2JiMi2lZRXQ683Jq39fNzERDmRuQUFuKNKXQfAOFvDzdXRwhERERERERGRpV0pqMSFywUIuFq2uLt0Kamxb9++a66XSCQQBIEnU7ogJNAT1TUNyM2vgMEg4PS5KxgTHwknR/suH3NYTAg+Xb0Q//3iEL7feRoAcO5SPpY/vQnLFkzEn24azprrRES9QGFx89UQ/r5uFoyEejt/HyUupRdBrzegoKgK0f38+FmCiIiIiIioD6tv0OJyZtdaKnRWl759GgyGNv9VVVVhz549SEhIwPz586HVas0db58wKCoA3p4uAIz9ME6fyxWbvnaVo4M9Vtw/Df9cdSf8fYw11jW6Rnz02V6sfPUbFLAmNhGRTWts1KOswlh6SmEvh2cXZ/kRdYSdnQy+3q4AAF2jHsVl1RaOiIiIiIiIiCzpfGrBDZ/D7iizXlLn5uaGKVOm4JdffsHx48fxxhtvmPPwfYZEIsGwmBA4OSoAGOtVn72YD0EQbvjYwweH4NPVizBv9nBx2dmLeVj+9CZs23GavTaIiGxUaXk1DAbj+wRLT1FPCG5RwpIXRxAREREREfVdBUVVKKuoAWC80LK7dUudAFdXV9x0003YsGFDl/b/8MMPER4eDgcHByQkJOD48eMd2u/LL7+ERCLBrbfearJ8yZIlkEgkJv/mzJnTpdh6ip2dDCNiQ2EnlwEASsrUSMssMcuxnRzt8edl07H65TvEWRsNWh0+2LAHK1/9BoXFVWa5HyIi6jktS08F+CotGAn1FZ7uzmJ5zLKKGjRodBaOiIiIiIiIiHqaRtuIi2mF4s8xAwK7/T67rfixVCpFYWHh9Tf8na+++gpPPfUUVq1ahVOnTmHYsGGYPXs2SkqufUI/OzsbTz/9NCZOnNjm+jlz5qCwsFD8t3Xr1k7H1tNcnBUYNjhEvNo2M7fUrFdCxg0JNc7amDVcXNY0a+P7nZy1QURkK3Q6PcoqjVdEOCjs4K50snBE1F3KKmqwY885HD6RbvEkgkQiEZu/CYKAfM7WICIiIiIi6nMuXC6A7mrZqQA/pViquDt1y1yQzMxMfPPNNwgPD+/0vu+++y6WL1+OpUuXAgA++eQT/Pzzz1i/fj2ee+65NvfR6/W499578eqrr+LgwYOoqqpqtY1CoYC/v3+n47E0b08XDOzvL2a7zqXmw8nR3mwnrJwc7fHnB6ZjQkIUVn/8C4rL1KjX6PD++j04cDQNzzw6m1f8EhFZuZIytVh6yt+Xpad6G73egGOns7B9dwqOncqCQTBedOBgb4fRcRGYNCYKo+Mi4Oyk6PHYgvzdkZFtvPAkv7AK/cJ8ejwGIiIiIiIisoyiUhWKS9UAAHs7OQb1D+iR++1SUuP+++9vc3ljYyPy8/Nx6NAh6HQ6vPbaa506rlarRVJSEp5//nlxmVQqxYwZM5CYmNjufq+99hp8fX2xbNkyHDx4sM1t9u3bB19fX3h4eGDatGn429/+Bi8vr07FZylhwV6oqdXgSkEFDAYBp8/lYkx8JBwd7M12HyNiQ/Hffy7GfzYfwE+/ngEAnLlwBctXfoYH75uEm2cOhVTabRN7iIjoBhRd/QABQCwrSLYvv7ASO/eexy/7zqO8qqbV+gatDgeOXcaBY5dhJ5chfmgYJiZEYfyo/nB1ceiRGJ0c7eHl4YLyyhrU1WtQWVULDzapJyIiG5JfWInq2gZEhvrAvgdqgBMREfUWOp0eFy83V2oaFOUPe3u5WfpCX0+X3rE3btx4zfXR0dFYuXIlHnjggU4dt6ysDHq9Hn5+fibL/fz8cOnSpTb3OXToENatW4fk5OR2jztnzhzcdtttiIiIQEZGBl544QXcdNNNSExMhEwma3MfjUYDjUYj/qxWG08YCYLQI7+Y3xvY3w81tQ2oVNVBo21E0tkcjBkRCZnMfIkGRwc7PPHAdExM6I/VH/+KkjI16ht0+Pd/d+PA0TSsfHgm/HvZrI2m36clfqdE1objwTZpdY0oK68GYCw9pXRz5O/QDCw1HrS6Rhw8loYdu88h+fyVVuu9vVwwa/JgVKrqcOREOlTqegDGD5NHkzJxNCkT/5TuwvDBIZg4xpjg8OzmJEOgnxLlV8uf5RVWsvxZL8T3B6JmHA+9hyAIuJhWiCsFlQCASlUdRg8P58V8ncDxQNSM44H6oovphdBoGwEAPl4u8PdViuOguytIdCmpkZWV1eZyqVQKd3d3uLp2f90sAKiursbChQvx6aefwtvbu93t7r77bvF2bGwshg4din79+mHfvn2YPn16m/u8+eabePXVV1stV6lUFvsDFRGiREWlCnX1WtTW1iLxhA5DBgaY/UXSL9Qd/3x5HjZ+fQy79l8EAJw8k4X7/98GLL4jAbOnDOo1pU0EQUBNjfFETG95TERdxfFgmwqKVKiprQUAeLl7QqVSXWcP6oieHg/ZV8rx64FL2H80HTW1GpN1MpkUo4eHYeakgRg+OBhSqTGepXeMxMW0IhxJysLRU9moqDS+DvR64ERyFk4kZ+Ffa3/FoCg/jBkRgbHxEfDxcjF77A72AjQN9WjUG5CZXY9AXyezXnRBlsf3B6JmHA+9g15vwPnLhSgtb54JWVtbiySJHgMifS0YmW3heCBqxvFAfU15ZS3SMvIBAHKZFMF+/uL5CEEQ4OHh0a33LxGsKIWo1Wrh5OSEb7/9Frfeequ4fPHixaiqqsIPP/xgsn1ycjLi4uJMZls0NbeWSqVITU1Fv3792rwvHx8f/O1vf8NDDz3U5vq2ZmqEhISgqqoKbm5uXX2IN6ymToOjSZnQ642PMzLUG1GRftfZq+uSzuZg9ce7UHr1KmDAWKrqqYdnwd/Hcs+DuQiCAJVKBaVSyTcd6vM4HmzTyTPZKL96MnvMiEgo3RwtHFHv0BPjobZOg72HU7FjzzmkZhS1Wh8c4IGbpg3BrCkx8FBee7aFwWDApfQiHDyWjoPH0lBU0nZyK7qfPyaOicKE0f0RHGC+D5nnLuWLjcKHRAciyIzHJsvj+wNRM44H26fVNeJUSq4427Hp99h0amRYTHCvq1DQXTgeiJpxPFBf0tiox+ETGWjQ6AAAg6MDTb5fCoLQ7TMfrapgpL29PeLj47F7924xqWEwGLB79248/vjjrbYfOHAgUlJSTJa9+OKLqK6uxr///W+EhIS0eT95eXkoLy9HQED7jUsUCgUUitYNNyUSiUX/OLk6O2D44BCcSsmFIAjIzC2Dq4sDAvzcu+X+Rg4Lx7p3F2Pt5wfw8+6zAIBT53Kx/OlNeHjhZPxhRqzN/7Fu+p3a+uMgMgeOB9ui1TaioqoOgLG3Acv+mFd3jAdBEHDhciG2707BviOpaNDqrt6Z8T+FnRyTxg7A3OmxiB0Y1OH7lslkGBwdhMHRQXho4SSkZ5fi4LE0HDqWhpz8cnG71MwipGYW4b9fHEREqA8mju6PiQlRiAj1vqHHGRLoKSY1CopVCA707PKxyDrx/YGoGceD7aqt0yDpbC7q6o0XMMrlMsQNCUFdvRbnUwsAAOcvF8LVxREuzq3PB1BrHA9EzTgeqK9IyyoRExpeHi4IscD3vw4lNTZt2tTlO1i0aFGntn/qqaewePFijBw5EqNHj8aaNWtQW1uLpUuXiscLCgrCm2++CQcHBwwZMsRkf3d3dwAQl9fU1ODVV1/F7bffDn9/f2RkZODZZ59F//79MXv27C4/Lkvy8XJFdD8/XEo3XtWZcikfTo6Kbrs619lJgacemomJCVH45ye7UFpRjfoGLf716a84cPQyVj48C369YNYGEZGtKS5Ti1cVskG4datS1eHXgxexY3eKSZKhSVSEH+ZOG4KpEwbC1fnGmnxLJBJERfgiKsIX9989Hjl55Th4LA0Hj6UhPbtE3C4rtxRZuaXY9G0iggM8MHF0FCYmRGFAP79OfxFzVzrB2UmB2joNKqpqUVungbMTTwYREZH1UKnrcSolR6z97aCwQ/zQMLi6OMDLw9hTo6CoCo2NeiSfv4IxIyIgl7fdg5OIiKivqqyqRW5+BQBjqeTB0YEWiaNDSY0lS5Z0+sttU0OQziY17rrrLpSWluLll19GUVERhg8fjp07d4rNw3Nzczs1fUUmk+Hs2bP47LPPUFVVhcDAQMyaNQuvv/56mzMxbEV4iDdqajXIK6yEwSDg9LlcjImPhIPCrtvuc9TwcPz3n4vxyab92LHXOEMmKSUHy1Z+hkcWTcbc6bY/a4OIyJYUFjeXGPL3ZXLZ2hgMBpxKycXPv6XgyMkMNOr1JuudnRSYMWEQbpo2pFtLSYYFeyEs2Av33T4GhcVVVxMc6biQViBuk1dYia0/HMfWH47D19tNTHAMjg7o8OeuIH93XM4sBgAUFFV162MiIiLqjNLyaiSfvyKWcXZxdkD80FA4OtiL2wweEAh1dQNqao3/zl8uwLCYtqs/EFHPKyyuQkGxCoH+7ghgiTgii9DrDUi51Pw9MirCF06O9tfYo/t0qKfGZ5991uU7WLx4cZf3tSZqtRpKpRIqlcqiPTVaMhgMOJGcjUqVsfSIm6sjEuIieqQ55/HTWXh37a8orWjutTFqWDieemgWfL17plG8ObDmIVEzjgfbotE2Yu/hSwAAJ0cFJo2JsnBEvcuNjIeSsmrs3HsOO/eeQ3GZutX6oYOCMXd6LCYmRHXrxQjXU1pejcMn0nHgaBpSLubDIBhabeOpdMb40f0xKSEKQ2OCr3nFaoNGh/2JlyEIAhwd7DBpzAD+Lekl+P5A1IzjwfbkFVTi/OUCcXarp7sz4oaEws6u9XtabZ0GiUmZaGw0XogQMyAQoUEsqdgejgfqKbn5FbhwuflEakSId5dmF3cnjgfqCy5nFCMztxQA4O7mhIQREW2+3psmO3Qnq2oUbs2sMakBGOupHz2Vibp6LQDA31eJ4YN75mqS6toGfPLZfuzcd05c5uSowCOLJ+OmqUNs4o8433SImnE82JacvHJcTCsEAPQL8+FV8WbW2fGg0+mRmJSJHXtScCI5GwJMP155KJ0wa/JgzJ0Wi+BA62uiXVlVi8SkTBw8loZTKbmtZpUAxr5e40f1x4SE/oiPDYO9fesJv6fO5qCk3HjBw8hh4fD2dOn22Kn78f2BqBnHg21JyypBRovSi/6+SgwdFHTNWYhFpSokn7sCAJBKJUiIi+y2Us+2juOBekJeQSXOpea3Wu7j5Yqhg4LbTFBaAscD9Xbq6nokJmVebQIuwdiR/dotncykhhWx1qQGYEwuHDuVJV5N0j/CF/3DfXvs/o+dysS7a39FWWWNuGz08Ag89dBM+HhZ96wNvukQNeN4sC3HTmWKM/XGj+oPV5cb68NApjo6Hq4UVGDHnnPYtf+8+PtoIpVIMWp4GG6aFoux8ZE2U5e7urYBR5MyceBoGk6eyYZW19hqG0cHe4yNj8TEhAEYNTxMLN/R8kRQgJ+SZTt6Cb4/EDXjeLANBoMBFy4XIq+wUlwWHuKF6H7+Hfq9XUovRPYVYw8sRwc7jI3v12Yyv6/jeKDuVlBUhbMX88Sffb1cUVpRI868cnJUYERsKFycLV9enuOBejODwYDEpExU1zQAAKIi/dAvzKfd7ZnUsCLWnNQAjKUuTqXkiD8PGxzSozUGq2sb8PFn+/DLvvPiMmcnBR5ZNAVzpg622j/ofNMhasbxYDsaNDrsO5IKwPi3dmICS0+Z27XGQ1OZpR17ziHlUl6rff283XDTtFjMnjLYpkoytqWuXosTyVk4eCwdR09lor5B22obhZ0co+IiMDEhCqPjwnEyOQdaXSOkUgmmjhtoNVfPUdfx/YGoGceD9Wts1OPMhTyUljeXSh7Y3x/hId4dPsbvSz37eLliRGwof+e/w/FA3amwRIWzF/LEBEZ4iBcG9g9ARVUtks9dES+8sZPLMDQm2OIX1XI8UG+WkVOKtKv9E11dHDA2PvKasx6tOqlx5coV/O1vf8Nvv/2GgoICaLWtv+RKJBI0Nra+us8WWXtSAwCycsuQmlEEwNh9PiEuAm6uPTtN9miScdZGeVXzrI2EuAisWDbdKhs58U2HqBnHg+3IvlKGS+nGv/c9PTuvr2hrPFzOKMb2PSnYfegS6uo1JtvLZTJMGN0fc6fHIm5ISIeba9sSrbYRJ8/m4OCxNCSezEB1bUOrbeQyGcJDvBAa5ImB/f0xangEa5H3Anx/IGrG8WDdNNpGJJ3Ngbq6HoCxfFTsoOAufRdt0OiQeDIDGq3xnAY/c7XG8UDdpbhUjeTzV8SERmiQJ2IGBIrr6+q1OH0uV7xqXCKRYECkHyJCO568NDeOB+qtqmsbkHgyAwaDMVExNj7yuuebrTapkZmZiYSEBFRWVmLw4MFISUlBWFgYHBwckJmZCZ1Oh2HDhsHd3R179+7tjrh7nC0kNQAg5WI+8ouMU2wdFHYYEx/Z401Iq2sa8OHGvfj1wAVxmVwmwy2zhuG+2xLgrnTq0XiuhW86RM04HmzH0aRMVKmNVw5OGB1lFdOte5um8SCVKbDn8CVs330OGTklrbYLD/bCTdNiMXNSTJ+qt93YqEfy+Ss4eCwNh0+km5TeMugF1NRqIJEC0f38cfsfRmDC6Cj217BhfH8gasbxYL1qajU4lZIj9pu0k8sQFxsKT3fnLh+zvLIGJ8/kiCdW2S/KFMcDdYeSsmokn8+FwWAcd8EBHhgcHdjqNdbYqMfZi/koKVOLywL93TEkOtAiFxhxPFBvJAgCjp3KEs8/RIb6YEC/6/fztNqkxuLFi7F161b8+uuvmDx5MqRSKV555RW8/PLLKCwsxCOPPIILFy4gMTERXl5e3RF3j7OVpMbvp8kq3RwxengEZLKe/4N+5GQG1vznN5NZG44O9rjzlpGYf3M8nBztezym3+ObDlEzjgfbUN+gxf7EywCM0z7Hj+pv4Yh6H0EQcOZCHr7ffhLHTudC+7tZpw72dpgyLhpzp8ciZkBAnx8vBoMBKZfycfBYOg4dS0NpRTVqazXQ640fMV2cFZDJpIgZEICJYwZgwuj+Vjl7k9rH9weiZhwP1qlKVYdTKbliORpHBzuMGBrWbgPTzmhZcsPeTo6xIyPFXlJ9HccDmVtZRQ1OpeSICY1Af3fEDgxq9/UlCALSs0uRkd188ZHSzRFxQ0J7/AJfjgfqjVpWiXByVGD8qH4dOsdstUmNoKAgjBo1Ct9//z0AQCqVYtWqVVi1ahUAQKPRIDY2FlOnTsXatWvNGrCl2EpSAzBOuT2alIH6Bh0AyzbqrKnV4OsfT+B/P59Cg1YnLvdQOuG+28fgD9OHWrTWNt90iJpxPNiGlqUGr9ecizqnoqoWu/adx46955BXUAm9Xg+ZTAZcHQ4D+/lj7vRYTBkXDWcnzo5piyAISM0owvc7k7H38CVUVNbB3l4OBwfT5qpREX6YmBCFSWOiEBLI8lTWju8PRM04HqxPcakaZy5cEU+Curo4IH5omNlOaAqCgNMpuSi52qPD3c0Jo+PCe2Wpyc7ieCBzKq+sQdLZ5oRGgJ8SQwcFd+i1VVSqQsrFfOj1BgDGyiVxQ0J7dCY1xwP1NnX1Whw+kS6Oq4S4CHh0cPZjTyQ15NffpLWysjIMHDiw+SByOerqmssOKBQKzJw5U0x6UM9S2MsxIjYMR09lQq83oLBYBRdnB4uc+HJxVuD+eyZg3pzh+Pzbo/j5txQYBAMqVXV4f/0efPt/p3D/3eMxZdwAfigkIuqAolKVeNvfx7qT7LZArzfOcNy+JwWJJzNhEAwm611dHDBjUgxumjaECaQOkEgkGNg/AE896IO4ISEoLFbhckYxikrVyMkvF7dLyypGWlYx1n95COHBXpiQEIWJCVHoF+bDL4FERNRhufkVuJhWKJaH8vJwQdyQEMjl5rtwTiIx9uVITMpAXb0WVeo6pGYUY1BUgNnug6ivq6yqxamU5pJTfj5uHU5oAIC/jxLOjgqcSslBfYMODRodjp3OxJDoIAT6u3dj5ES91/nUAjGhERrk2eGERk/pUlLD29sbtbW1Jj9nZ2ebHlguR1VV1Y3ERjfA1cUBQwcF4/S5XABAWmYxnJ3s4e9jmXIPXh4ueHL5DMz/Qzw2fHUY+xJTAQCFJVV4472f8dWPJ7D83omIHxrGkxlERO2ordNApTY2vnRzdeRsgRtQWKLCjj3nsGvfeZRWVLdaHzckFJPGRGD21GFQ2Pfs1PXewN5eDl9vNwgC4O+rRNyQUGh1jTh4LA0Hj6Xh8tUyHgCQnVeO7LxybP7fUQT6uWPi1QTHwP7+/ExARERtEgQBaZklyMwtFZcF+CkROzCoWy6Ws7OTYfjgEBw9lQmDQUBOXjnc3RwR4Odu9vsi6muqVHVISskVT576erliWEzHExpNXF0cMCa+H5LP5aJSVQeDQcDZi3mormnAgH5+/FxJ1Al5BZUorzS2E3B0sMOAyOv30ehpXSo/NWXKFDg5OWH79u0AgFtvvRX79u3DqVOnEBkZidLSUgwfPhxeXl44e/as2YO2BFsqP9VSy/qfMpkUY0ZEwtXlxuuK3qjU9CJ8+sVBMenSJG5IKJYvmIjo/v49EgenBxI143iwfi3/pg+I9EMkZw50ilbbiMMn0rF99zmcOpfTar2XuwvmTB2MOVOHIMBPyfFwg0rLq5F01vg8+3q7YURsqLiusESFQ8eNPTjOpxZAQOuPoz6erldncPTvtpNU1DF8fyBqxvFgeU29nAqLm2evRob6ICrSt9t/J3kFlTiXmg/g6vfr+Eiz9O2wVRwPdKPU1fU4kZwNXaMeAODj5Yq4ISE39LnPYDDgYloRrhRUiMt8vFwxdFBwt5Y/53ig3qJBo8Ph4+niuBw5LBzeni6dOoZV9dTQaDRQKIxXhP7jH//AqlWrUFRUBHd3d+zbtw/Tp0+Ho6MjBg0ahPT0dKjVanzyySdYvnx5tz6AnmKrSQ0AOHPhiviBz9HBDmPi+0Fh36VJOmYlCAKSzubg0y0Hkd6iqRMATB4zAPffPQHBgR7dHgPfdIiMOB6s3+ET6aiuaQAATBozAE6ObFLZEdlXyrB99zn8euAC1DX1JuukEikSRkRg7vRYJMRFiE3POB5unCAI2J94GQ0aHSQSCaaMi27z80d5ZQ0OHU/HwWNpOHM+r1UJMMDYi2v8qP6YmBCF4YPNW1aEro/jgagZx4Nl6XR6JJ+/Il49CgAxAwIRGtRz/ZlSLuYjv6gSAODspMDY+Mg++77E8UA3orqmAcdPZ4knTr08XDAiNrRDTYg74vfl6ZydFBgRG9pts905Hqi3OJWSi5IyNQAgyN8DsYOCOn0Mq0pqeHp6YsGCBbj//vsRFRWFCxcuICYmBq6urgCAb775Bq+88goyMzMRFhaGFStW4LHHHuvW4HuSLSc1DAYDjp/ORpXa2PfE2hqbGQwG7E+8jHVbD6OwpEpcLpVI8YcZsVg4fwy8PDqXEewovukQNeN4sG61dRocPJYGAFC6OWJsfD8LR2Td6uq12JeYih27z+FCWkGr9YF+7rhp2hDMmjy4zatOOB7M43JGsVgaZGB/f4SHeF9ze5W6HkdOZuDgsctIOpuLRr2+1TYuTg4YOzISk8YMwMihYbC3ggs1ejuOB6JmHA+W06DRIelsjniBh1QqwbCYEPj1cI8xvd6Ao6cyxTgC/JQYFhPSozFYC44H6qrq2gacOJ0Nra4RgPEClpHDws2W0GhSXlmDM+fzxPuxk8swbHBIp6867wiOB+oNCktUOHP+CgBjz+YJo6O6NMPJqpIaTk5OaGhogEQiwbBhw7Bs2TLce++9cHd379YArYUtJzUAQKNtROLJDDRodACAQH93DB0UbOGoTOl0evy8+yw2/+8oKlXNjecd7O1w+x9G4M4/joKLs3kz6nzTIWrG8WDd0rNLkJ5lnNXWkZPDfZEgCLiUXoTtu1Ow90gq6hu0Juvt7eSYlBCFm6bHYuiga5cz4ngwj5bJOBdnB0wY3b/D+9bUanD0VCYOHUvD8dNZ0Fz9MtqSo4M9EuIiMDEhCqPjIjh7qZtwPBA143iwjOraBpw6a2wADBhPTI6IDbVY09LaOg0SkzLRePUK80FRAQgL9rJILJbE8UBdUVOrwYnkLGi0xs927m5OGDksrNtmPNXVa3EqJRc1tcZEpEQiQXQ/P7N/n+J4IFun1Tbi0PF0MQk4fEhIl3szW1VSo7q6Gl988QXWr1+PEydOQCKRQKFQ4NZbb8WyZcswffr0bg3U0mw9qQEYaxUeO50lNl+y1nrsdfVafPt/Sfj6p5MmJ6TcXByx4LYEzJs1zGxXZfJNh6gZx4N1O3Q8XfwgPnnsADg68ORtE3V1PX49cBHb96Qg+0pZq/URoT74w/RYzJg4qMN9pTgezOfYqUzxYoWx8f2gdHPs9DHqG7Q4kZyDg8cuIzEps1XCCjAmrUYOC8fEhP4YG9/PKnqI9RYcD0TNOB56XkVVLU6n5Iolapwc7TEiNszsF7x1VnGpWuwRKZVKMHp4BNyVThaNqadxPFBn1dZpcCI5W7zgVunmiJFDw7u11wUANDbqcfZivlhSBzBe7DskOtBsVUw4HsjWtWxf4OfjhrghodfZo31WldRo6cKFC1i/fj22bNmC4uJiSCQShIaG4v7778eSJUsQEtL7pl72hqQGABSVqpB87or4c9yQ0B6frttRlVW12LLtOH7adcak/ISvtxuW3DkOMycNuuE3H77pEDXjeLBe1bUNOHw8HYBxanbCiEgLR2R5BoMByeeuYPueczh0PE080dHE0cEe0ycMxNxpsRjQz6/Tr2mOB/Np2VQ1NMgTMQMCb+h4Wm0jTqXk4uCxNGOfmavJvpZkUilGxIZiYkIUxo3sZ7EreXsLjgeiZhwPPauwRIWUi3kwGIynLdxcHRE/NMwqekQCQGp6EbKuXlDhoLDDuJH9+lRZRI4H6oy6ei1OJGeJM65cXRwwenhEtyc0mgiCgPTsUmS06Onq7uaE4UNC4KCwM8vxOR7IVpWUVeNUSg4A42zICQlRN/Rea7VJjSZ6vR4//fQT1q9fj507d6KxsRFSqRQzZ87EsmXLMG/ePNjZ3fgfBmvQW5IagGkJE7lchoS4CKu+mrGwRIWNXx3B7oMXIaD55Roe4o0HFkzAmBGRXR4ofNMhasbxYL3SskrED999tbxBk9LyauzafwHbd6egqFTVav3gAYGYOz32hmezcDyYT2OjHnuPpEKvN8BOLsPU8dFmuyKusVGPsxfycPBYGg4dT0eFqrbVNlKJFEMGBmLSmAFIiIuAt6dLnzrhZA4cD0TNOB56TvaVMlxKLxJ/9vFyxbCYYKtqyi0IAo6fzhJnJHp7uiB+aFifeW1wPFBHGWfdZqOu3jjb1sXZAaOHh1vkM1lhiQrnLuWLVUwcFHaIGxLapdnELXE8kK3S6fQ4fCJdnEEVOzAIQQEeN3RMq09qtFRcXIxNmzZhw4YNuHTpEiQSCby8vFBSUnL9nW1Ab0pqAEDy+SsoKjGeDHJ0sMOY+H5Wc7VLe9KzS7Dui0M4npxlsnzIwCA8eO8kDI7u/JWffNMhasbxYL0OHktDbZ0GADBlXLRZriSyJY2Nehw7nYXtu1Nw/HQ2DILBZL3S1RGzJg/GTdOGmC3hw/FgXmcv5qGgqAoAMCwmGAF+7ma/D4PBgPOphTh4LA0Hj6eZlBf4PRcnB3goneDh7gQPpRPclU7wdHe+usxZXOahdOpz460tHA9EzTgeup8gCEjNKEL2lXJxWZC/BwZHB5gtKW5ODRodEk9miP0B+oX7IirC18JR9QyOB+qIBo0Ox09no67e+H3G2UmB0XERFj0Hpa6ux+lzueKsEalUgtiBQTf0GZXjgWzV+dQCXCmoAGBMzo8cFn7Dx7SppEaTlJQUPProozh8+DAkEgn0ev31d7IBvS2podcbcDw5Cyp1PQBjOZNRw8Ot8kPi7yWfv4JPNx/ApYwik+XjRvbHsnvGd6rZE990iJpxPFin6poGHD5hLD3l6e6M0XERFo6o5+QXVmLHnnP4Zd/5VlfgSyDByGFhmDs9FmPj+5l92jrHg3lVVNXi+GnjRQnm+qB8LYIg4HJGsZjgyCus7PKxHB3sjcmOFgmPpn/uSmd4ujevc3Sw65WvF44HomYcD93LYDDg7MV88QI8wDaSBBVVtTiRnI2m0yvxQ8Pg4+Vq4ai6H8cDXY9G24jjp7PEC7ScHBUYHRduFReNaLSNSD6XK860AoDIUB9ERfp26fXM8UC2qLyyBieSswEYq/mMH9XPLP07bSap0dREfN26dUhKSoIgCHB2dsadd96JdevWmSNOi+ttSQ3AmC0/mpQpTi8K8vdA7KAgC0fVMYIg4OCxdKzbetDkRIVUIsWsyTFYfOc4+Hpf/0Mk33SImnE8WKe0zGJk5JQCAGIGBCI0yNPCEXUvjbYRB4+lYfvuFJy5cKXVeh9PV8yZNgRzpg6Gv4+y2+LgeDAv4/t2mlhyoCeb3QuCgOwr5Th4LA0ZOaWorKpFpaoOlaq6NhuO3wiFndxkpoenu/PV/53g7tY0M8S43sVZYTOvLY4HomYcD91Hp9Pj9LlcVFQZL2SQSCQYPCAQwYE3VgKjp2TmlOJyZjEAYz3ycWY6MWTNOB7oWrTaRhxPzkbN1f5nTo72GDU83KrGhcFgwIXLhSbnlXy8XDF0UHCnL5rieCBbo9cbcPhEhjiLypznG6w+qbF3716sX78e27ZtQ319PQRBwJgxY7Bs2TLcddddcHFxMWesFtUbkxoAoFLX49jpTLHxWnQ/f0SEdnymg6Xp9Qbs3HsOn32diPKqGnG5vZ0cf7opDvfcOvqa/UL4pkPUjOPBOh04moa6eg0kEgmmjIu2+lKBXZWeXYLtu1Ow++Al1NSZNn6WSaUYN7If5k6PxchhYT0yq5Djwfxa9vSKivRDvzAfC0dkvMCjUlXXnOioqrua8Kg1uV2lqm/1urxRdnJZi0RHc7Lj98kQD6UzXF0UFp1Ny/FA1IzjoXvUN2iRdDZXPPkpk0kxfHCIzc12OJWSK5Y/VLo5IiEuwiaqIXQVxwO1R6fT43hyFqprjGPa0cEOo4ZHwMnRehIaLeXkleNSepE428rZSYERsaFwdlJ0+BgcD2RrLqUXiqUePZROGB0XYbbXrlUmNfLy8rBhwwZs3LgR2dnG6ZU+Pj5YuHAhli1bhkGDBnVXrBbVW5MagLFJ0pnzzVfD2uJU2QaNDtu2n8bWH46L0xoBY83se24dhVtvimtzeiPfdIiacTxYH3V1PY6czAAAeHm4YNTwcMsGZGa1dRrsPZyK7XtSkPq7koIAEBLgibnTh2DmpBh4uDv3aGwcD+ZX36DF/sTLAIylByaNibJwRJ2j1TaiSl1vMtOjUtUyGVKLKlUdKqrqoK6pN+t9SyXSFgmP5n4fv+8B4unuDHc3R7OfQON4IGrG8WB+1TUNSDqbI1YQsLeTI35o2A037bUEnU6PxKQMcWZiaJAnYgZ0vvejreB4oLbodHqcPJstljt3UNhh1PDwTiUILKG8sgbJ565A12gso28nl2HY4BB4e3bsgm2OB7IlVao6HDudBUEQIJVKMG5kf7g4m2+MWlVS46uvvsL69euxZ88e6PV6SKVSzJo1C8uWLcO8efMgl/fOK0eb9OakBgCkZZUgI9t49aRcLkPCiAi4Orc/w8FaVdc04Ittx/D9zmRodY3icm8PFyy6YyzmTB0Cmaz5iz7fdIiacTxYn8sZxcjMNZaeGhwdiJDA3lF6SqNtxMavDuPHX86gQaszWaewk2PyuGjMnRaLIQMDLfZa5HjoHifPZKOswjizcnRcBDx7OFnVUxob9cYESItZIFViIqTFzBBVLVTqBhgEg9nuWwIJlG6OxiSHyewP04boTYkRufz6pRU4HoiacTyYV3llDU6fu4LGqycRnRwViB/auaujrU11TQMSkzLEagjDYoJvqPmwNeN4oN9rbNQj6WyO2KdCYS/HqOERZj1Z2p3q6rU4ldI8a0wikSC6n1+HerdyPJCtMBgMOHIyU3ydD4j0Q6SZZ9FbVVKj6YqviIgILF26FEuWLEFwcHC3BmdNentSQxAEJJ+/guJS41RZJ0d7jBkRCXsbLXNSUlaNz74+gl37L5icKAgJ8MT990zAxIT+kEgkfNMhaoHjwfocOHoZdfVaSCQSTB0XbbN/k1tKyyrBW+9vR3ZeucnyAZF+mDstFlPHD7SKLz0cD92jsLgKZy7kAbCtXl7dyWAwQHU1AVKlqkPF7xIfxtkftWJSRG8wXwIEAFydHeDp7gylm2OLslemyRAPpTPs5Xq4u7tzPFCfx/cH8yksrkLKpXzx5L+7mxNGxIb2is87eQWVOJeaD8BYSmtMfKRNXjR4PRwP1JJeb0DS2RyxL469nRyj4sJt7rXf2KjH2Yv5Yik5wPi5dXB0wDVnw3I8kK1oeWG7m6sjxsZHmv01a1VJjXvvvRfLli3DtGnTujUga9XbkxqA8Q3o2OksqKuNUwQ93Z17rHZ5d8m+UoZ1Ww/jyMl0k+WD+gdg+X0TMXRQMN90iK7ihzDrUqWqw9FTmQAAb08XjBwWbtmAbpDBYMBXP5zExq+PoFFvvBrT3k6Om6YOwU3TYxEV4WvhCE1xPHQPvd6AfUdSoWvUQyaTYuq46A7NFCAjg8GAmlpNqxkfTWWvxHJYV5e1nLV6QwTA3c0B40cPwNiRkRgRG9Zr+/sQXQ/fH8yjZVNtAPD1dsOwmGCTWfW27tylfLH5sLOTAmPjI3vdex7HAzXR6w04fS5XnJFrJ5dhdFzENXucWjNBEJCeVYKMnFJxmbubE+JiQ9v9DMTxQLaguqYBR05miEmHcSP7dcs4taqkRl/XF5IagLHe9dGkTGi0xi/BwQEeGDLQ9q+iPHcpH59+cRDnLuWbLB81LBx33TIcw2PNn5UksjX8EGZdWjbtGhIdhOBADwtH1HWFJSr844OdSLmUJy7rF+aLF/58U4emclsCx0P3OZ9agCsFFQBs/7VtzQRBQG2d9ursj1pxxkfLJEiVmACpRb1Gd42DAXq9HjKZDJAYy8TFxYZibHw/JIyIsLlebEQ3gu8PN0YQBFxMK0RufoW4LCTQEzEDAnrd86nXG3D0VKbYKNnfV4nhg0MsHJV5cTwQYLzo4vS5KygtrwZgLGk+ali4TfbF+b3CEhXOXcqHXm+cKeugsEPckNA2HxvHA1k7QRBw9FSm2O+mX5gPoiL9uu2+mNSwEn0lqQEYrw4+npwlTgMeFBWAsGAvC0d14wRBQGJSJtZ9cbC57IlgfAOeNWUwltw1HgG+SssGSWRB/BBmPQRBwP7Ey2jQ6CCVSjB13EDY2dnelX2CIGDX/gt4f/0e1DcYG2ZKIMHd80Zh8Z3jrPoxcTx0H5W6HolJGQAAD6UTEkZEWjgiAowXtjTP+DAte5VfXIVTZ7NhMABoYzj0D/fF2JH9MHZEJKIifW16li/R9fD9oev0egPOXMgzKekSFemHfmau421Naus0OJqUKTYeHtjf32ov6OgKjgcyGAxIPt88ruVyGUYODYO70snCkZmPuroep8/lor7BeAGIVCpB7MCgVr1yOB7I2mXlliE1owiAcQbh+FH9uu1zO5MaVqQvJTUA05rXEokE8UPD4O3pYuGozMNgMODXAxex4avDKC2rFq88tLOT4ZZZw3DvnxJ61RswUUfxQ5j1qKyqxbHTWQAAXy9XjBgaZuGIOk9dXY931/6Kg8fTxGV+3m54bsVNGDrI+ntycTx0r0PH08XGdBMTomy6IWxfIAgCikvKkJGrwrFTWTialInyqpo2t/VUOiNhRCTGxEdiRGwonBztezhasnUNGh3KK2rg6eEMRwfre/3w/aFrtNpGnErJRZXa2DxYIjGeFAz0d7dsYD2guFSN0+dyARgf9+jh4fBwd7ZwVObB8dC3/b43q0wmxcihYb3m9d2SRtuI5HO5YgN0AIgM9UFUpK/42ud4IGtWW6fB4RPp4gXsY0ZEduu5TyY1rEhfS2oAQFpmsVg/0E4uQ8KISKto3mouWm0jfvglGZ99fRj1DY3ilYeODva464+jcPsfRvCLOPUp/BBmPS6mFSLn6oyyoYOCbe4L//HTWVj98S6Tk56zJg/G40un2szJa46H7tXyKqHunPZM5vH78WAwGJCeVYrEpAwcPZVpUhe/JXs7OYYPDkHCiAiMiY+Evw9nxNK1NWh0SDyZAY22ERKJBP6+bggP9raqEiZ8f+i8unotTp7JQV29BoDxSu7hg0N6zUVzHXE5oxiZucbv1g4KO4wd2a9X9CbieOi7BEHA2Yt5KCxWATDOXogfGgYvj947rg0GAy5cLhR75QDGC9CGxgRDLpdxPJDVEgQBJ5KzUVFVCwAID/HCwP4B3X6fTGpYib6Y1BAEAafPXRGnETo5GpubWXO5kM4SBAEFhaXYuf8y/vd/SdC0aKjpoXTCfbePwR+mD+1Vj5moPfwQZh0EQcC+I6nQaBttrvRUg0aH/2w+gB9+SRaXuTo74KmHZmHSmCjLBdYFHA/dS6NtxL4jqRAEAQ4KO0weO4DPsxW73ngoq6jBsVOZSEzKxKmzOSafp1qKCPXB2KuzOAZF+bNMFZn4/RfuljzdnREe4g0fLxeL/63g+0PnqKvrkXQ2R+zZqLCXY+SwcJttHtxVv399e3m4YOSwMJt/DXE89E2CIODcpQLkFxlP7kulEoyI7T3VPa4nJ68cl9KL0HQ61dlJIc5O5Xgga3SloALnUwsAAE6O9hg/qj9ksu79HM6khhXpi0kNAGhs1OPY6SyxuZmXhwvih4b2mi+hLT+ElVfW4vNvE7F99zkYBIO4TaCfO+6/ezwmjx3Qax43UVv4pcQ6VFTV4nhT6SlvN4yIDbVwRB1zOaMYb76/HbkFzY0/Rw0Lx9OPzLbJLzgcD93v1NkclFxtKDlyWLhNvk76is6Mh6byDEeTMpF4KlNsGvp7SldHjI6LwLiR/RA/NMxmZnFR92k5S1xhL4cgANrfJcicnRQID/FCoJ97t38Zbw/fHzqutLwayeeviA12nZ0UGDkszCrLivUEjbYRR06kiwme3jBTkeOhbzqfWoArVz/zSyQSxA0Jha+3q4Wj6lnllTVIPndF7JdjJ5dhaEww7GR6jgeyKvUNWhw+kYHGq6/VUcPDe2RGFZMaVqSvJjUA4wA4mpQpfvgKDfJEzIBAC0dlHm19CMsrqMT6Lw9h/9HLJttGRfhh+b0TEW+Dte2JOoJfSqzDhcsFyM03fkkYFhPcqgGdtdHrDfjyhxP47Osj0BuMJy3s7eR4aOEkzJs93GZfSxwP3a9ljXF/XyWGDw6xcETUnq6OB0EQkJFTimOnspB4MsN4VSNaf/WQy2QYOigYY+IjMTY+0uZK7tGNKymrxqmUHADNPQfcXB1RUFyFrNxysWxRE3s7OUKCPBEa5NnjJXz4/tAxeQWVOH+5QLyS2UPphBGxYTYz+7S7VFTV4kRytvi8jIgNs+mTwRwPfU/LMrkSiQTDBgf32fKStXUanD53RewTJwEQ4OuM2JhwjgeyGklnc8SLjIIDPDBkYFCP3C+TGlakLyc1AGPT2hNnssWGMjEDAhEa5GnhqG7ctT6EpaYX4T9bDiD5/BWT5SOGhOGBBRMQ3d+/J0Ml6nb8UmJ5giBg7+FUaHXG0lPTxg+EXG69X/4Liqrw1gc7cP5ygbhsQKQfnl8x1+bfIzgeup/BYMC+I5fF17stlVrra8w1HiqranHstLHR+MmzOahv0La5XViQF8bERyJhRASGRAdZ7Ip86hn1DVocOZEhXu06sL8/wkO8xfWCIPz/9u48PK6zvBv/d/Z902ibGY12W/Im73acBAIkkISlCWUJaUuWQktboOENtIW2EChtAy+8LWUpUH4BUigUQlnKFgghCVnsJN4t25KtfR2ts+8z5/z+OKMjjSXZki1pZqTv57p8yXNm0ZE0z5xznvu57xsTUxH0DU7OK02lVCrgrrKj3lu+Zr3/eHy4sq6+cXT1jsu3qyqs2Lm1hlnvOXP7SmnUKhza11SyvRw5HjaWjq5R9A3OBjTatniKfgHWastksjh9bkjOPo5Go9jc5MG2Fjc/86jgRnwBnD4/BEDq53TD/uY1u95iUKOIbPSgBgAMj/pxpmMYgHQA29tW+jUTr3QSJooijp7qx9f+61l094/n3feqQy24/64bUON2rNXuEq0qXpQU3uR0BEdP9QGQJgB2by/O0lOiKOKXT7Xj37/5tDwpqVQocfed+/HOtx5aFxPTHA9rY+7F8XpZMLEercZ4SKUyOH1+CIeP9eDIsR74JoILPs5i0uPArgYc2te4Ievwr3eCIOClE30IhGIArlx2MRiKo29oEr7xEC69jK10WlDnda56SQUeHxYniiLOdo7kNdGtq3Gitbmav6tLHD8zIPeutFoMuG5PQ0lOgHI8bBxzSwQCQNuWGmZW5oiiiK7ecXT3TyAajcJkMsFhM2LX9to1zyYkmpFMZfDcixflRSO7t9eiqmLt5rMZ1CgiDGpIOrt86B2cBCCtKrlub2NJ10Be6knYzGrSR777XN5Ft0qpxBtu3oF3vu0QyuymtdhlolXDi5LCa+8YlicCdm7zwlVZfKncgWAM//IfT+D5l7vkba5KOz7y/tuxrWV9lCYEOB7WSjiSkN9LVosB1+9rKvAe0UJWezyIooj+oSm5D8e5ztG8/mYzlAoltre6cWhfEw7tbYTXzSBYqZsb2DQatDi0t2lJgfF4IoWBoWkMjvrlGtEzrBYDGrxOVFVYV2WSmMeHhWUyWZw6N5TXR6elqRoNteWXedbGlU5ncfhYj1xazesuK8nzKI6HjeHS7KvtLR4u7lzAiM+PF49fhMFgBCCtjN+zoxZWi6HAe0Yb0cmzg/CNS/OXriobdm5d21K/DGoUEQY1JKIo4sSZATm1zmjQ4dDexpJdlbvck7B0OoufP3ka3/rBEXlFGQDotRq85Y17cdfv7SvpIA9tbLwoKSxBEPDU851IZ7JQqZR4zQ2tRVdy5cixHnz2K7+CPzj7+Xfbq7bjvfe/umTLJiyG42HtvHC0G6FwHABww/5mrsQvQms9HoKhOF46KZWpeulk37x+CjNqXA4c3N2I6/Y2om2Lp6jL9dF8vokgTrZLZV6VSgWu29O47ImfdDqLoVE/BoanEE+k8+7T6zSoq3GixuVY0WsVHh/mS6YyOH6mH8GQ9FmuVCqwo7V0ytKIoghRFNc8UyIcSeDwsW65xHMprnzneFj/evoncKFnTL7NzNrFiaKIoeExdPUH5J60pfZ5SOvD3N6FWo0aNx5ohrYAPcgY1CgSDGrMymSyOHK8V26GVF5mxt62upI8ibnak7BYPIXHfnoUj/3sWF49aKvZgD/8/YP4vdftXPMPDKJrxYuSwpqYCuPYaalJaiFWUlxOPJHCV7/1O/z0iVPyNpvFgAff8zrceKC5gHu2ejge1s7A8DTO5fqy1HudaG12FXiP6FKFHA+ZTBZnOoZx+GgPjhzvwbDPv+DjjAYdDuyql3px7G7gqsgiF40lcfhYj5xlca2TZIIgYGwihN7BKTlIOkOtVqHGZUddjRMG/bUH4Hl8yBeNJXHsdD9icemaSKNWYfeO2qLLYhdFEYFgDMO+AIZG/RjJfR0aDWBkLIBkMgNXlQ1etwO1njKpEb1baka/mp8nc0s8q1RKXLensaSC+xwP61vf4CQ6unzy7Ut7HlG+mfGgN5hw6uxg3kKwproKNDdUcpzQqkuns3jupYtyYG3n1pqCBNUY1CgiDGrki8VTOHKsB6m0NEjqapzYsqn0JiGu9STMH4ji2z98ET974jQy2dnU96pyK+6763rc8ootJVkblTYmXpQU1pnzw/Jk3VrXu7ycjq5RPPyFX+bVxz64uwEf+vNbi27CYiVxPKyddDqLp17ogCCI0GrUeNX1m3nsLDLFNB6GRvx44Vg3jhzrwZnzw4uWqdq62YVDe6UsjroaZ8H3m2ZlswKOHO9BOCItkFrpQP50IIq+gUk5s3yGQqFAdaUV9TXlsFmvfpK6mMZDoQWCMRw/MyBfE+p1GuzdWQeLqTCT8qIoIhiKy4GLYV9ADl4M+wKLZn1dic1igNdThjqPMxfskAIfK1XibG75UaNBh+v3NZZM5hnHw/o1d9EJAGxurEJjXUUB96j4zR0PUo+h0bzFGJVOC9q21pTM+KbSNHdeodJpwZ62uoLsB4MaRYRBjfmmA1EcPdUnp8tua3GXXG3jlToJGx0L4Bv//QKefP583vaG2gq8++4bcXBPA0/yqOjxoqRw5paeUqtVeM0NLQWf1M1mBXz7f47g2//zojxpqNdq8Gf33IQ3vrZt3b9HOB7W1tyar8UU1CNJsY6HcCSBo6f6cORYD1480YtwLov4UtUVNly3txGH9jaibUsNs2kL7GznCAZHpgEAJqNUynY1Jngi0ST6h6Yw7PPL1yszyuwm1NU4UVluWfZ7uljHw1obmwjh9PkhZLPSOYLFrMfetjrodZpV/96hcFwOVMxkXcz8PxpbXuBCqVDCVWWDQa/B8Kgf8WT6yk/K0WrU8LgcqHU78jI7atyOZWUFCYKAI8d75Syjqgordm+vXdbPUSgcD+vT0Igf7Z3D8u3mhko011cWcI9Kw0LjoW9wEp3dY5iZejWb9Ni93cuy5bQqJqcjOHqqD4CUqXrjgeY1OS4vhEGNIsKgxsLmHuwUCgX27ayD02Eu8F4t3UqfhHX1jeOR7zyHl0725m1v21KDP/nDV2Dr5tJr/kYbBy9KCmd8MozjZ6TSU+5qO9q21BR0f4ZG/PjUF3+J812j8raWpmr87ftfv2GaAnI8rK25J+CFXFFECyuF8ZDNCjh7YQRHjvXgyLEe9A9PLfg4g16LfW11OLinEdftaYBjHWecFaMRXwCnzw8ByJXa2du46qv6U6kMBkamMTA0LWcUzDAadGiodcJdZV9yH6tSGA+rbWB4GucvjsqTdE6HGbu2eVe0d0k4ksCwTyoPNTzqx/BoAEM+KYCxWABzMUqFElUVVtS47HBX21HjcsBTbYen2oHqSqscVBNFEZPTEQwOT0vvmeFpDA77MTgyjYnp8BW+S74KpyUvs8PrkQIeZXbTgu+bWDyFw0e7kc6VZCuVMj8cD+vP3JJogFQ2aVNjVQH3qHQsNh4mpyM4dXZQHt8atQq7tntLau6Mil8mk8XzL3fJPcYKvfCcQY0iwqDG4jq6RtE3KF04atQqXLe3sWSizqt1EnaifQBf+69n0dnty9t+w/5mvOvuG1FX41yx70W0UnhRUjinzw9hxBcAAOzZUYfKcktB9kMURfz8N2fw5UefRiIlnQwpFUr84VsO4o9+/+CGSpXmeFhboijimcMXkEimoVAo8KrrW6DjavqiUYrjYcQXwOFcgOP0+aG8MqEzFFCgtbka1+XKVDXVVZTMz1eKwtEEjhzrkVf272j1wONau0B5NitgZCyAvsGpeav5tRq1POl8pc+eUhwPK+lizxi6+yfk264qG3a0eq4qwzQcTWBkdLY81LBPCmAMjfqvKnBRWW6BJxe0mAle1LgcqKqwXnPAJRpLYmjEj4GRaSnoMTyNwZFpDI0GFvx8WYzRoMsLcnhz2R3uKjv8wZi8yEWhUODArvqiD7xu9PGw3oyOBXD6/LAcsGzwlqOlubrAe1U6LjceorEkjp8ZkI8/CoV0DsK5IVop5y+Oon9Impsts5uwf1d9QT+XGdQoIgxqLE4URRw/M4CJXN1ak1GH6/Y0ruhKndWymidhoiji2Rcv4pHvPpdXi16pUOLWV23DvW8/hApnYSYuiRbCi5LCEAQBv32+E5lMFhq1Cq8uUOkpfyCKz37l1zhyvEfe5ql24MPvu21DZplxPKy9uRNlLU3VaKgt/hWqG0Wpj4eZRsaHj/XgxeM9CF7SSHpGhdOCQ3ukAMeu7bUMrK2gTCaLw8d65MmcGpcD21s9BdkXURQxMRVB3+AkpgPRvPuUSgXcVXbUe8thNi28SKvUx8PVEgQB7Z0j8iIMAGisrcCmxss3vo3GkhgeDchZF0OjfoyOSV8XG4uLUUCRC1w44HHZ4cllXNS4HKiusBaktFw2K2B0LDgv2NE/NI1IbOmBGZVSCXeVHRazDhq1CuVOKUDzxte2FfVq7o06HtYj30QQp84OyQGNUu2bWkhXGg+ZTBanzw3l9XyqcTmwdbOr4KWHqbT5A1G8eEKqGKNSKXH9vqaCLzZnUKOIMKhxeel0FkeOz16oVDgt2LOjtuhPbNbiJCyTyeLxp87iPx87jKlARN6u1ajx5tt34+47D8BiLkwzPaK5eFFSGGMTIZxoHwAgBRF2bFn7SZ4Xjnbj/33l1wiEYvK2N97Shve88yYYDUuvCb2ecDysvWgsiWdfvAhAWiDxioObCrxHNGM9jQdBEHD+og8vHu/BC8d60DswseDj9FoNdu+oxaG9jTi4pxHlZcU7qVgKTp0bxOiY1DfHYtbjuj2NSy73tJpC4Th6ByfhGw/h0sviCqcF9V7nvAnl9TQeliqTyeLk2UFMTs9ey2zZ5JJXGMfiKTnLQm7SPerHyFgA/mBssZddVIXTMlsiSi4VZYe7yl4yPXFEUUQgGMsFO/xysGNgeBpjEyGIWGQaRpR+n5mMlNGkVivhqbbPy+zwespQVW4p+EToRhwP69HYRAgnzw7Kn4Nedxm2tWy8RU3XainjQRRFXOwZR8+c8w+HzcjFFHTVBEHA8y93y/OxxVK+cMMGNb70pS/hM5/5DHw+H3bu3IkvfOELOHDgwBWf99///d+4++67cccdd+DHP/6xvF0URTz00EP42te+hkAggBtuuAFf/vKXsWnT0i+WGdS4smgsiSPHeuQ6gfVeJ1qbizuyv5YnYYlkGj/8xXH8909ezkt5Nxv1+IM3H8Cdt+/mQYwKihclhTF3omdvW92aZnDF4il8+dGn8YvfnpG3OWxGPPie1+H6fU1rth/FiOOhMF483iNPgF23pxF2m7HAe0TA+h4Pvomg1Gj8eC9OtA/I57GX2txYhev2NOLQ3iY0N1QUfCKxWImiiGQqg3g8hWgshVg8hd6BSZy7OIJkKoNMRkCNy45sVkQ0npQfE09IX6OxJGKxFJLpDAw6DYwGbe6fDkajFga9tM1s1MGg18Jo1M55jBYmg076f267Tqte0ns2nkhhYGgag6N+ZC55D1gtBjR4naiqsEKpVK7r8bCQRDKNY6f7MTkdwbQ/iulgFEaDFpFIEkM+KXhxVYGLMouUbVE9G7yocdnhqrKv+2uiZCqDoVE/Boen0T80hcERKegxNDKNZDoDUQAisSTEXIN7nU4NnW7+70SnUaNmJsjhdsjBDq/7ymXUVspGGw/r0cRUGCfaByDk3m+eage2t7qL4u85ExycCZaO+AKYmI5Ap1XLn/sGfe7z3zj7f6NBC4NBA5NBB4Nes2YldJczHkbHAjjTMSz/3g16DXZvr4XVYliLXaV1ZG62u81qwHV7Gotm/G64oMb3vvc93HPPPfjKV76CgwcP4nOf+xwee+wxdHZ2orKyctHn9fX14cYbb0RjYyPKysryghqf/vSn8fDDD+PRRx9FQ0MDPvrRj+LMmTM4d+4c9PqlrZBnUGNppvwRHD3VL0f4t7d4irqpbCFOwkLhOL7zo5fwk1+dzGtWWFFmwT1vO4RbX7WtKFau0cbDi5K1l80K+O3zHchmhTUvPXW2cwSf+uIvMTIWkLcd2tuED77ntUVfv3ktcDwUxtzmlFwlWDw2yniIxVM4fmZAajZ+vHvRiVqn3YyDexpwaF8Tdm/3wqAv/Yw2QRAQjacQywUZ5ABDPIVYQtou377kn/S8ZO45KQiiIL9uNitKi3lyV5wGg2ZNS9QqFcrZIIdeA5NRd8kkmC438SUFTnRaNaKxJPyhGCDmJpO10j+zSY+6Gic81XbEYpF1OR4SyXQu40LKuugZmMCZjmGMTYQQiSShUABGgxYq9dLOVcod5rzG3O5qh9ysW6/TrPJPU3oEQcD4VBiDw36cvziCI8d7MTEVxuRUBIIoQr3E37sCClRVWC/J7HCg1l0Gu824ou/bjXJ8WK+m/BEcO90vT6y7qmxo21Kz5n/LcCSBoVw/nUuzvi7tgXQ1tBp13me/9E8jBcwNswFzo1EHo14zJzAyEzCffe7lssWWOx6CoThOtA8gkZR6GapUSmxv9cBVabvmn5k2hlA4jsPHeiCKIpRKBQ7ta4LFVNhKMOFIAl194+juG8db37hvVb9X0QU1Dh48iP379+OLX/wiAOnA7vV68f73vx8f/vCHF3xONpvFK1/5SvzxH/8xnn32WQQCATmoIYoi3G43PvjBD+JDH/oQACAYDKKqqgrf/OY38Y53vGNJ+8WgxtINjkzjbOcIAKku7b6d9Sgr0gmyQp6EjU2E8OhjL+CJZ87nXfzVusvwx3ffiBsPNPPEkNYUL0rWnm8iiJPtgwDWrr54JpPFt/7nCL7zw5fkzx6DToO/uO/VuP012/m3z+F4KIxMJounXuiUA32vur6Fgf4isBHHgyAIuNgzjsPHe3D4aDe6+sYXfJxWo8aubV65TFVVxdpdJ4iiiFQ6i1hsNuMhGr8k8DBn+2JZEbF4ColUehX2D4hGk/JkmVargl6/+ET23ACEVqNCMplBNJ5CPJ5avFTPahGBdCaLVCqDbFb63iqVAjqdBjqtGhazFq5KB6wWvTwplvfPODsJJgdScquGjQZtQT/XkqmMPGE4kps0nFkBPemfLS+VzQiIxVOYmS1QKhUwGrRQqvI/A8psprzeFh6XA+5qGzzV9nUR8CukvsFJdHT5AEjHR0+1A2OToVwZKynTY9gXyLuWvBKLST+vjFWtuwyuKttVvS834vFhvZgORHHsdD+yWen9U11pw86tqxfQWKi/znDu8yccXXr/mUJTq1SLB0H0GigUApwOW94xYV5wJJd5qNdpkExlcLJ9MK8McFNdBZobLt+viEgQBBw+1oNwRBo/zQ2VaK5fPBlgpYmiiPHJMLr7J+Rska7ecYxNhnIPAJ587IOrug9FFdRIpVIwGo34wQ9+gDvvvFPefu+99yIQCOAnP/nJgs976KGHcPr0afzoRz/CfffdlxfU6OnpQVNTE06cOIFdu3bJz7npppuwa9cu/Nu//duCr5lMJpFMzkaEQ6EQvF4vAoEAgxpLcP7iKAaGpwEAGo0KB3c3FLxJzUKK4SSsb3AKj3z3ORw+2p23fctmF9702p1o8Drh9ZRxNROtumIYDxvNyfZB+aC/t61u1Wu2D45M41NfeByd3T5525bNLnz4vbfB4yrerLpC4HgonDMdw3Ij2rYtHriq7AXdH+J4AKTyHC8e78WR4z04fnogL9t2rqb6Chzc04jr9jSgtbl6wew7QRDmZDjkZ0XEE2nE4nODFLNZEPMyJGKzAYNC0mrVMBnyJ++NBh3CkQQy2Sx0Wg3sVgO2t7phMupzWRG5iX+jTp4c0us0C76/BEFAIpm55OdPzgZtEuk5v7/5AR359xhPIZlcfvAmkxWQypXOkojICgJUShU0GiW0WjVUy8yynPs7M+g1uXJZCwRHZibBcqVVjIbZ35fJqINep17wPZZKZzDiC8oThiNjuf/7/JiciiywR/nSmSziibScYWOzGtDaXI1aT5mceTFTKmqj9t5aK3PPFaV+NA15f/N0OovR8aDUpHymWfmIHwPDU4jFUkv+Pmq1Eu5qB2rdDrmEVV1NGWpcjstex/P4UJr8wSiOnprN0Kh0WrBzW801Z4zHEymM+AIY9gUwOOKXA6cjvuX311EopIwj6TPHLpepqyq3IpPJyp/98UQK8Xha/ryXA/e5oPjs9rR8/Jj9PF952WwWKtXSMhKVSgUMeinAkckKEARRWgCg08BhM6K+xgmLxQCDXpMLkmth1OeC5LkyjDPHBINew9KYG8xMEAEAzCYdDu1tXLX3QDYrYHDEj4u9UvCiu3cCXX3jckBlMasd1CiqYpWTk5PIZrOoqqrK215VVYWOjo4Fn/Pcc8/hkUcewcmTJxe83+fzya9x6WvO3LeQhx9+GJ/4xCfmbQ8Gg/OayNF8rgoDxsal6D8APPlsO7a3uOB0FFfGhiiKiESkE/tCnYQ5rGp86D2vwrlXt+I//+dFdFwcAwC0nx9C+/khed8qyy3wuu2ozdVLrXHb4XU5FqyvSnQ1imE8bCSZjID+wTFkBQEajQpqZQbBYHBVvpcoinj86fP4xveOIJWSJuKUSgXu+r29eOsbdkGlUq7a9y5VHA+FYzWpcDEqnT90dA3BqOfvv9A4HgCtGnjFgTq84kAdkskMznSM4KWT/Th2egBT/qj8uAvdPlzo9uFbj70Am9UAr9suTcjnJt/jiRSSyYUDImtpZiLFaNDM+aqRVpPmvppykyRzJ1MufYxBp1mwJM7AsB8XcxfaarUKB3bVwbBolkYWqWQcqWT8svusUQE2swo2swHA1dUcz2YFxBNp+d9Mea1EIo1YblIsnvs7xeLp2cmyRArhSBLTgRiC4TgSiRSyQhbZZBaJZBpqlRTcUC9xpXs8nkU8nsTkdPiqfo65Zv4mM3+rYCiOyenosq9ZbRYDXFVWOUjhsBlQZjehsa4c+3fWLfh3TqfiCKYu/3eja+N1meEbn5ImaaNRvHQ8gy2bqvMeYzOrYGupwPaWCnmbKIrwB+MYHg1gKFdSbGhUmmyeWCCwlc1m0ds/jt7++VlpZQ4Taqpt8LjsqKm2S19ddvnafqMfH0pNKJzAifZBZHIZGk6HCfU1VoTDS/s8SqYy8I2HMDIWlL+OjAUxOh7C9Jzj4VI5HSa4q2xwVdngqrLCXWWDu8qGqgortIuWK9QA0AO4ul6EqXRW+tzPfcbP/dxPJOceC+Zsn3n8zPbcsSGdzu/DlBWWHjDJZoF0OoNQeHa/ksmMnJ34vFIJg0ED5RLHlkGvgV6vkQMfBp1GWmig10Kv18DpMMFTbYO72g53le0yv18qdtFYCmfO9UEQpL4VW5vLlzyGrySRTKNvcBq9g1PoHZhEz8AU+oem573XF2LQa1DnLUNjbTkavM4V2Z/LKenZ0HA4jHe+85342te+hvLyle3s/pGPfAQPPvigfHsmU8NmszFTY4luOGDBiyd6EM2tELnY54cINRrrKormhGfmZL8YVpYc2m/Ddfs24/CxHjzy3efQPziVd//kdBST01GcaB+Wt82sXqj3OlHrKUO9txz1NU54PQ6me9OyFdN42AhGxwLQG6RJmRqXA3a7fVW+z3Qgis/8+6/w8sk+AIBKpUKNy4GP/OXtaGmqvvyTNzCOh8Kx2WwYGAkjFk8hlQa0OgOPaQU0NhnCiC8Io14Bj4fjYcbNlU7c/ModEEUR3X0TOHysB0eO9eRlwkWiKZy/OH+CcKkrOBeyWFaEKa9hti5vlf9ysiJWQiAYw+h4FCaTNOG5a7sXVeXr5/opmUrjXMcAxqfjiMZSSKUySCQzSKbSUCgUsFkNMOi0SKbS+Zk1C2SQxBPXnnGTSgtIpRMIhmZXSy62UtNmNcBdnVvxXO2Y7XXhssNk0OJCzxj65lyDuKvt2LbZxdW/BXbDAQOOHO+BIIgIRjKIxIQlZdja7XY01LnmbY8nUhgeDcxmduT+DY8GFsxCC4ak99fZC2N52/V6DWpcDljNGlQ47bDbjLBbDbDbjLBZDLBbjbDZpK/rvfl7qQiF47jQNwKd3gAdpIDCnh2188b4TAbQTJbF7NcAJqbCiwZNFzu+Oewm1LhmS9R5csExd1Xp99fJ5DLborkskPHxaShVWjk4cum/uRkjMxkm0dzxQJVIQ6NRS2UXc7/iRCIDg0G7pKC5dDxIIhS+ch+SmbmkGrcDXpdD+up2wOsuQ3mZmed7RUwURXR098FgMAIA6r1OeGuqrvCshfmDUXT3TaCrT8r66Oobx/BoYMExfun4dthNaK6vkMteNdVXwF1lkz9P1iIhoKiOLOXl5VCpVBgbyz9Yjo2Nobp6/sRLd3c3+vr68KY3vUneJuSiomq1Gp2dnfLzxsbG4HLNHtDHxsbyylFdSqfTQaebn2apUCg4uJdIq1Xjuj1NOHN+CONTUsSwq28CwXACO1o9l22wtJZm/qbF8HdVKBS4YX8zDu1txIn2QXT1jqN/eAp9g1MYGJpC/JKUeREifBNB+CaCOHK8Z/Z1cs3h6mucqPM6Ue91os7jRG1NGSeG6LKKaTysd76J2ZUU7mr7qvzOn33xIv7lq08gFIkDuZf/vdfuxJ++85X8LFgCjofC8bgcuNgjnQ+OjAXXtD4sSdLpLM53jcqlwKLRKEwmM7zussLuWJFRKBTY1FiFTY1VuOdthzAdiOLF4704fKwbx0/3I55M5zernhtomBOAMBnza3PPBC1m+zNIAQm1urhXVaZSGZw+PyR3wGjwlqO6Yn01PNVpNWisK0fbNgt84yH0Dk7Oa2Sr1aix1eNGrafsipO5l/ZGiSfmBEAuKT82sz2ea8oeiyflCbJILIlEIg2zSTfb26LKlisV5YC72g6LeeHmoYIg4EzHMEbHZrM2m+oqsKnx6iZJaGVZLQZsb/HgdC6L/9zFUdisxkX/nldiNOjkz625BEHA2ERICnKM+OWAx+DIdF69/xmJZBpdveO5cjvD8rnmQgw6DWzWXNBjTrDDbjXkbbdaDbBbuZhhNYQjCRw7PSCXXrJZDaiqsOLY6YG83jqDI36MT4Yv36tlgb+13WqUS0TVyIEL6et6LlOn0aih0ahhtRggiiKcNu1VL4qSylOmMeWP4OWTffAHo0ilskimM6gos8Bi1s0pvTgnUBJPy2UZZwIsV+pHNXcu6eipvrz7DDoNPK5chZCZr+4y1LjYK6kYDAzPfiYbDTpsaqi64vtNEAT4xkO54MVYrpH3RF4vrTyXvFyNyyEHLprrK9HcUFkUvZOLqqcGIDUKP3DgAL7whS8AkH7xtbW1eN/73jevUXgikUBXV1fetr//+79HOBzGv/3bv2Hz5s3QaDRwu9340Ic+hA9+UKrlFQqFUFlZyUbha0QURfQOTOJi77gcqTPoNdi1rRY269Wljq/kvpVKDVBBEDA+FcbA0DT6BifRNzSF/sEp9A9PI55Yer3U6gqbHOyoq3GivobBDpKU0ngodZlMFr99vgOCIEKnVeNV17es6O88Fk/hS994Co8/3S5vK7OZ8Fd/cSsO7G5Yse+znnE8FFY8kcIzhy8AAIwGLV5xcBP/DmtoYiqMs50jSMxZTCEFNUzYubWGfU6WKJsVkEpnVjUropiIoojjZwYwkVvM5LAZsX9X/bpb5X/p8UEURUxMRdA/NIWpSyYHlEoF3FV21HmdsJiubgJ6ufu23PdaOp3FifYBuWywQqHA1s0uBjCL0NnOEQyOSH0rjQapfrpmjcrHhMJxDI745SBH/9AUBkf8GB0LIJ3OSCt4V/BjTq/VwDYn68OWC3o4bEZYLQbYLwmKSA2a1//n7HIJgoCxyTC6esfx7JEL8E2EMOmPIBxJIJFIQ1jmdKDFpIcn11dntr+OA26XfU0+44rdSl4/pNNZnD4/JB9TAWlieesSs+dm+lHJvaZiKfgmghgc8ef+SUHL5cwlAUBFmQVedxm87pnsjjLUuB2oKresu+N9MYrFU3j+5S5kc+XjDuxumBdcSKez6B+ewsWecbnvRnf/BGLxK2fwaNQqNNSWo6muEs0NUgCjsa7iqgKTV3NOslxFF9T43ve+h3vvvRdf/epXceDAAXzuc5/D97//fXR0dKCqqgr33HMPPB4PHn744QWff2mjcAD49Kc/jU996lN49NFH0dDQgI9+9KM4ffo0zp07B71+aR+8DGpcuyl/BKfODskprUqlAls2FfaEeT1MWomiiPHJMPqHptA3OIn+XNBjJYIdXk/Zul5VQfnWw3goFSO+gLzarq7GiS2b5pcGuFpnzg/jU1/8JXwTs6stbzywCQ/+6WsLHkguJRwPhXf0VB8mp6UJwoVO2GnlZTJZdHT5MDTql7dp1Co4bEb0DvhgMpmgUCiwa5sXVRU8H6Z8Pf0TuJDLsNJq1Lh+f1PJlxVZyOWOD6FwHL2Dk/CNh+aVXahwWlDvdcLpMK/l7l5WPJHCsdMDiESl0lUqlRI7t3pRWX51deppdQmCgCPHexEKS31MKsut2LOjtqD7lE5nMDQyDgHqXJkqqe+MPxhDMBRHICjdDoakbeFI4rKrx6+WVqOWAx+XZn/MZIVIJbGkQInRoF0353eCIGDKH81rzD2TdTEyFkAylUE0loKYK3OnUilhNGqx2I9v0GvlYIVnpmRULoBhtfBa4nJW+vpBFEVc7BlHz8CEvM1hM2LX9toVKekmiiKmA9J7ZygXtBwancbgiB++8dDls3UuodWoUeNy5IIdUtCjNhfwMBnnV8GhqzP3+qjWU4ZajxPd/eO5ElLj6OqdQP/QFDLZK/e/MBv1czIvpK+1nrIVywpei6BGcdT/meOuu+7CxMQEPvaxj8Hn82HXrl14/PHH5UbfAwMDy47+/fVf/zWi0Sj+9E//FIFAADfeeCMef/zxJQc0aGU4HWZcv78JJ9sHEQjFIAgiznaOIBCMY+tmF1RLbKxH+WZqIVZVWPNWYOcFO2ayOnL/XyjYIZexOtGTt72qXOrZUe8tR62nDA3ecgY7iK6Rb3w24FC9QhOD6XQW//nYYfz3T16WT0ANei3ee9+rcdurt62bCzfaODzVdvmkfWjUz6DGKpvyR9DeMYx4YjY7o8JpwbYWN3RaNVKpBALhNERRxKlzg9izow7lZcUzOUuFNR2Iyo3BAaBta826DGhcidViwM6tXmxuTGFgaBqDo35kMtLEwsRUGBNTYVjMeqksV6W1oKtapVI0/XJGllajxt62Oi6AKGJKpRK7tnlx+Gg30pksxidD6B2YREPtyvYXXQ51LvC91ElcQRAQDMURDMcRCMURDMXkAEgwFEcgFJO3B0JxhMKJJU2sptIZeYwthUatkspd5QIdDpsRNqsxFxiZHxQxG3UFPZeemXweGvVj2BfA8KhfbgI/MupHcoFeKAAgCOIlAQ0FjEatXF5oftaF1BuF1w3FQaFQYHNTFSxmHc50DEMQRPiDMRw51o09O+quugTd3Nd3OsxwOszYtc2bd18qlcHIWEAOeAyOTsuBj3A0Me+1UukMegYm8gIwM8psJtS4Z8tYeXPBj+pKG+cBl0gURZw5P4znX+7C6FgQE1NhJJKZvIWMl1PhtMhlo2bKSFVXWEt+rBddpkaxYqbGyhEEAZ3dY+gfmm1CZzHrsWubd80juBtxJe5MinxeCavLBDsWMxPsqKvJZXZ4naj1OBnsKGEbcTwUQjqdxVMvSKWn9DoNbjq0+Zp/3/1DU3j4C7/Exd7ZnlTbWzz48PtuY5mYq8TxUHiCIOCp5zuRzmShUinx6utbir6fQCnKZLK40DOGgeFpeZtarUJrUzVq3FIjWlEUEQgE0D8SkYOyKpUS+9rq4GCwacNLpjJ44eUuJFPSpFpTfSU2NazfPjjLOT5kMlkMjfrRPzSVFzAEAL1Og1pPGbzusjUrITRjyh/BifZBOeBiNOiwt62Wq2lLxPhkGMfP9AOQJiX376ovWOB/tc+XBEFAOJLMBTtmsz8CuQBIKJyAPxiVAyHB0NKCIMulVqnyymDZbZf0ArFIgZGZslgmo3bZQUtRFBEMxTGcy7YYzgUwZgIZyy0TpFYpodWq4bAa4Swzw+Ny4FXXbUZ9bTmcDhPPb1fBao6HYCiOE+0DciBapVJie6sHrsq17Vs18z4dvCTQMTTix8hYAFlh6eNPo1ZJgbVcoGOmnFWtp+yaAzalTBAEDI0G0N03jou94+jqlb4Oj/rlBvJGoxZq9fzPGKVCiVpPfv+LpvrKgixY2JDlp4oVgxorb3QsgPbOEbkWnEatwvZWz5qWM+Ck1ay59YD7hqbQNzCJgeFp9A5OLusEqrJcalA+E+RoqGWwo1RwPKyN4VE/znQMAwDqvU60Nl996SlBEPCTX53Cf3z7d3JpP5VSiXvffj3uvnM/65peA46H4nDuwog82b69xSNPstPK8AeiONMxjFh89jjvdJixrcWdd9yeGQ8WiwWnzg1jfDIEQAp+HNhVz3IUG5goijh6ql/uJeF0mLFvZ926/ty8muODKIrwTYTQNziJYCied59arUKNy75m58ujYwF5xS8gNQves6NuRUqZ0Nq52DOG7n5pRbROq8b1+5sL8jcstvMlQRAQjaUQyGWABIJxhMKzGSCBmbJYodmvy5mEXSqlQgmbVQ9brg+IFPjQy+Wx7DYjUukshnOlomaCF9HYlWvez6VWqeCqtMHjssNTLa1+91TbUe60oKd/EomkdHw3m/TYv6ue43yVrfZ4SCTTcuWTGU31lWiuryiK8ZfJZOEbD2FgZBpDI9N5/Tvm7vNS2CwGqV+H3KhcCni4q2zrapFTMpVBX64HcXffOLr6JtDTP4FEKn8hRCyeQiadmzvVqGAwaKDXatBYV5FXQqqhtqJoxjmDGkWEQY3VEY4mcLJ9MO/g3VhbgU2NlWvyoVxsJ2HFSBRFTE5H0DeT0TEns2MpjYZmzAQ75mZ21NUw2FFMOB7Wxtw6mNftaYTdZryq15mcjuAz//4rHD3dJ2+r8zjxkfffjk2NVSuxqxsax0NxCIbiOHysG4BUQ/jgnsYC79H6kM0KuNg7hr7B2axZlUqJlqZqeN2Oee/5ueNhphn0zOeYVqPG/t31bBC6Qc1chANS5sGhfU1FczG9Wq71+DAdiKJvcEoODs6YKSnb4C1ftRWVc/ueAECl04Kd27ws/1GCLg0oltlN2L+rfs3PWUr9fEkURURiSQSD80tfBUKxvO0zwZF05sq16leSUqGEq8oGT7UdnlyJKE+19LWy3Dpv/CZTGbx0oleeYzEadDiwu35DlgRca2sxHgRBQHvnCEZ8AXlbZbkVbVs8RT3ZH44kMJgLdAzlAh2DI9MY9gWWNaaUCiXc1Ta5WflMo3Kvy1H0pdNC4Ti6esfR1T+B7t5xdPWNY2DYf8XssnQ6C4VCAVelDV63A6995Va0bnKhxmUv6kWMDGoUEQY1Vk8mk8WZjmGMTcye2DsdZrRtrVn1i6JSPwkrpJlgx9xAR//QFHoHlxfsqHBa0OAtv6SMVRnT3wuA42H1pVIZPPVCJ0RRhEGvwU2HWq7qdX535AL+5atP5NUzvfO23fjTP3rlup9MWiscD8XjuZe65Ca2Nx7YBLOJx4drEQjGcKZjOG9BicNmxPZWz6LH3kvHQzYr4OipPviD0qo7vU6D/bvqeezeYCanIzh6qg9A4UvgrKWVOj5EY0n0DU5h2OeXsyZmOGxG1HvLUVluWbFmsx1dvrzyv153GbZudvEYV8KSqQwOH+2Wy9E01lZgc9PaLmzZaOdLoij1qJgJfARnskByWSFSU/Q5QZFQXM6mvhwFpKDm/B4XDlRXWpc8WZ1KZfDSyT75vMlo0OLA7gYGNNbIWo6HvsFJdHaPYWZK12zSY8+O2pJbNCoIAsYmQhiUG5VLAY+hET8mc0HbpTIb9blG5Y5c0KNMzl7SruE18kx25mzzbimAsdSeP65KO5rrK9DcUIm6GiempiPQ6dRQKBTYuc275iXHrhaDGkWEQY3Vd+mHsl6nwc6tNataq3mjnYSthcWCHX1DU8tKp50JdtR6ylDvLUddTRnqapycMFlFHA+rb2jEj/ZOqfRUg7ccLc3Vy3p+JJrEF77+W/zm2XPyNqfdjL/6i1uxf1f9Su7qhsfxUDz6BifR0eUDUJgJm/VCEAR0902gZ2BSPtdSKhXY1FCFeq/zsu/zhcZDOp3Fy6f6EApLpXQMeg0O7mnkxMkGkUim8cLL3fJk3ebGKjTWVRR4r9bGSh8fUqkMBkamMTg8LfclmWE06FDvdcJTbb/qbIpsVsCpc0N5mSGbGqvQtEH+XuudPxDFSyf75M/13dtrWc65iIiiiHginRfoCASlwIdSpURNrlm3q9J2zZOu6XQWL53sRTgiBTQMeg0O7G6AQV9ak9ylbK3Hw+R0BKfODsqZDlqNGju31cDpMK/6914L0Vgy16R8NtAhZXpMI7mEYOEMpUKJqgqrnNkxN/Bxrf1lMpksBoanc8ELKYjR3TeBSGx+M/VLqVUq1NU40dxQIffAaKqrzFvAdfr8kJyVU1luxZ4dtVe9r2uNQY0iwqDG2vAHojh1bkhebaJQKNDaXI26GueqfD+ehK2dmWDHwPA0enP9OmaalS8r2FFmmW1Q7nWivkbq2cGVu9eO42H1vXyyTy4TcP2+pmXVoT91bhCf/uLjGJszKfHKg5vxf/70FtazXwUcD8Ujmcrg6VyGk16nwU2HNvNvskyhcBxnOobliQ5AqqO/o7VmScfPxcbDpStCTUYdDuxuYMbYOicIAl4+OZupU+m0YPeO2g0zLlfr+CAIAkZ8QfQOTs47N9aoVaj1lKG2xrms8ZVKZXCifUD+WykUCmxvccPjYn+i9WRu8F+jVuG6vY1rthCM50vF4dKFBsygLIxCjIdoLInjZwbk44ZCocCWTS7UesrW5PsXgiAImJiKyFkdM43KB0f980o7XolBr5WCHa6Zvh0OeD1SL49LF+rE4in0DkziYu+YnIXRNzi1pGwsg16b63tRiaY6qeR+rbvsssHMiakwjp3uByB9tt9woLmkFg8xqFFEGNRYO8lUBqfODmI6EJW3uaps2LbZveI1AnkSVniiKGLKH83v13ENwY5aj1TCqlTqKhYTjofVNXdi1mjQ4pXXbV7S81KpDL75/Rfw/f89ChHSIdug1+Iv3/UavPaVW/m3WiUcD8Xl+JkB+SJlb1sdKpyWAu9RaRAEAT0Dk+jum8jLzmiqr0RjbfmymhwvNh4SyTReOtEnl560mPU4sKsBGk3x1nWma9PZ5UPv4CQAaSXw9fuaN9Tfe7WPD3P72U1dUnpDqVTAXWVHndd5xT42sXgKx073y+fTarUKu7Z5UV62PlbwUr6TZwfhGw8CkD6Hr9vTuCa9Uni+VHiZTBZHT/XLjZh1WjUO7G5gQKMACjUe0uksTp8fyitv5HWXYcum6qLuubAa4okUhn0BDA7P6d8xKv0/nkgt67Uqy63wuhwwGrToHZzE8GhAvh6/nHKHeU7z7ko011eiutK6rL9FJpPFcy91yQu+d7R6Sm5BAoMaRYRBjbUliiIudI/JF0yAtPpv9/baFV2Rz5Ow4jUT7BjI9ekYGJaCHn2DU0tK5ZthMupQ48o1kHI55FRDT7WdqbiX4HhYXQPD0zh3YQQAcqszrlxCp29wEv/8+V+iu39c3rajtQZ/877bSqaWZqnieCguYxMhnGgfAABUVVixe3vppF4XSjiawJnzw/KqTUCa6GrbUgOLeXlNva80HuKJFF460Yt4QrrwsluN2LezrqgbVtLVmTsWlUoFDu5uXLWm1sVqLY8PoXAcfUNTGB0L4tLL9gqntKBnoTIjoXAcx073y+WsdFo19rbVMbNzHctksjh8rEcOYtW4HNje6ln178vzpcK6tMeVVqPG/t31Vwx60uoo5HhYaA7NYTNi9/baNe0nUaxm5pfmlrGaaVw+NhG6YrPuhSigQI3LkQtcVMhZGCtRQv9s5wgGR6YBAOVlZuzbWX/Nr7nWGNQoIgxqFIZvIoj2jhFkcjUC1WoVtrW4V2wyjydhpUcURUwHougflPp0zGR4LDfYAUjZHVKww46aXG1Fr9uBqorlRdHXC46H1fXSiV45A+2G/c2XnVQUBAE//MUJPPLd5+R0VrVKhT9+xw1425v2bsj351rjeCgugiDgmcMXkExloFQq8KpDLbxAW4QoiugdmERX37jcfFihUKCxrgJNdeVX9fmxlPEQjSXx0oleeRK1zG7C3ra6NVkpTGsjFk/h8NFuuXb3lk2uVSsRW8wKcXxIJNPoH5zC0Khf/v3PsJj1aPCWy6tAJ6cjOHl2UL5+Mhl12NtWV3LNY2n5wtEEjhzrQTYrTc5tb/Ggxr26K3t5vlQ42ayA42cG5IwujVqFA7sblr1wgVZOMYyHEV8A7Z3D8jmgQa/Bnh11fF9cRiqVwchYAAPDM4GO2cDHzByTVqNGQ225lH1RXyFlPdeVr8pC2elAFC+d6AUAqFRK3LC/uSSP4QxqFBEGNQonGkviRPugXK8ZAOq9TmxurLrmib1iOOjQypgb7BjIRdyHR/1y5H0paYIzNGoVPNV2eGYyO1y5ZlIuB2xWw7p9r3A8rJ5EMo2nX+gEIE0uvOLgpkUfOzEVxv/90q9wvL1f3lZf48RH/vL1aK6vXPV9JQnHQ/GZW/Jmo06mXkkkmkR7x7BcggIAzCY9drR6rmk1/VLHQziSwEsneuVJ1wqnBbu3exmIXQcEQcCR471y5k91pQ27tnkLvFeFUcjjQyaTxdCoH/1DU3Jm1Ay9ToPKcgsGR/xyVgdX6W48o2MBnDo3BEDKpjq0t2lVJzN5vlQYgiAFNCanZwMa+3fVMxurwIplPARDcZxoH5BLF6lUSuzY4kF1BTP9l0MURQSCMURjKbiqbGuyUCebFfD8y91yWddSvuZhUKOIMKhRWNmsgLMXRjDiC8jbHDYjdm7zXlOjnGI56NDqSqUyUl3FET+GRqcxNOqXI++hSPzKLzCHxaRHjcuRayIlBTs8LqmcVSk1bVoIx8Pq6R+awvmLowCApvpKbGpYODjx1PMd+NzXnszLOnrL6/fi3X9wIyck1hjHQ/EJRxN4/qUuANLK5Bv2Nxd4j4qHKIroH5rChZ6xvOyMBm85mhsq1nQRSDAUx8un+uRV4tWVNuzcWsNxVOLOXRjBwLBUBsFo0OH6fY0btrxYMRwfRFGEbyKEvsFJBEMLn8tWlluxc2sNs6U2oPzxqsWhvU2r1vemGMbDRiMIAk62D2I81z9BrVZhX1sd7DZjgfeMimk8JJJpnGgfyDtGNNVVoLGugseFIjZ3EZfDZsSB3Q0Ffy9drbUIanCGhEqCSqVE25Ya2K1GdHSNQhBE+IMxHD7ajZ3bvChbgZp1tH5ptVKqYENt+bz7QuG4XE9xeDQgpRuO+jHiC8hlf+YKRxM43zWK812j8+6rLLeiptoBr2e2h0eN24GqcgtXqW5wM40bAaC6cn5gPBxN4Av/32/x5PPn5W0VZRb89V/cij1tdWuyj0TFzmLSw2Y1IBiKIxxJIBxJMJUeUkZre8ewXE8bkCad27Z4CjLBYbMasGdHLY6e6oMgiPCNB6FSKrG91V2yF2Ub3ehYQJ4gVSoV2L3du2EDGsVCoVDAVWmDq9IGfyCK3sEpjE+G5Pvrapxoba7mmNugWpurEQzHEQzFEYuncKZjGHt2sBfVeiAIAk6dG5IDGiqVEnt31DKgQfPodRoc3N2A9s7ZxcHd/RMYGQtgU0MVXFWFD7xQvmBI6qMFSOdb21o8/BtdAYMaVFJqPWWwmvU4dW4Q8UQayVQGL5/sw+bGqgUnrImuxGoxYFuLAdta3HnbBUHA2GQYQyN+DI36Z+sqjvrzLhrnGp8MYXwylFc2CJDqL7qr7fDmGpZ7XHY5y4MpwutfPJGSJxvNJv28xn3Hzwzg/37pcUxMh+Vtr76+FQ+8+2ZO2BJdosblkFecDY36sWWTq8B7VDiiKGJwxI/Obp9cPx2QSnRuaqgq6Cq8MrsJu7fX4kT7AARBxLDPD5VKga2b3Vd+MhWVSDSJsxdmF3Js3eTmsanIOOwmOOwmRGNJjIwFYTZq4aqyF3q3qICUSiV2bfPihZelHjjjkyH09E+gsa6i0LtG10AURZzpGMbYhHQtqlIpsbetbkWaEtP6pFRKi4MtJj0u9IxBFEXEE2mcPj+E3sFJtDRVo7zMXOjdJEjzT+2dw3L5yKb6SphNugLvVfFjUINKjt1mxKG9TTh9fgiT0xGIoojObh/8wRh2tHpWLbWWNhalUimvgNu/qz7vvkQyjWFfQO7ZsVAjqblS6Qz6BifRl0sjnMtqNkj9OnI9O2b6d3iq7Sw3tE7MXHgA+VkaqVQGj3z3Ofzg58fkbSajDg+862bc/Iota7qPRKWiusKG8xeljM3RsSBamq69v1YpiidSaO8YkZuDAlKJke2tnqLJXq1wWtC2tQanzg5BFEUMDE9DrVJhc1NVoXeNliibFfIaTrur7avedJiunsmoW7S8JW08Br0WbVtrcOy0tNjqYu847DZj0RwjaHlmAhqjY1L2t5Q1V8u/Jy1JQ205ysvMuNAzholclk84ksDRU30oLzOjpamaCxYKrGdgEuGINJdkMevR4C3NPhprjTNmVJK0WjX2ttWhq28C3X3jAKRV8oePJbF7u5cfyLSq9DoNmuoq0HTJaidRFBEMxXOZHVL/jpmG5cO+gNw4da5QJI6zF+I4e2Ekb7sCClRVWOf075jN8qh0spxVKRmdW3oq15ytu38CD3/hl+gdmJDv27XNi7/+i9tQVcG+TUSL0WhUqKqwYnQsiFQ6g/Gp8IZrejg04kdHt0+eaAYAr7sMLU1VRVcSqLrCBqFVxOnzUtPanoEJqNTKecdPKk7nLowiEpUusM0mPbYx04aopFQ4LWiqr0R33zhEUcSps4M4tK+p5PsAbjSiKOLsnBJCMwENrrCn5bCY9djbVocpfwSd3WMIhaXM58npCCanu+CpdmBTYyU/HwogHE2gp1+aF1AoFNjR6uF8zxIxqEElS6FQYFNDJWwWA86cH0I6k0UsnsSR4z3YttkNd7W90LtIG4xCoYDdZoTdZsT2Vk/efYIgYGwilMvs8GNoZBpDuR4ec8sOzRAhwjcRhG8iiKOn+/Lu02rU8LgcuXJWs1keXncZA3pFJhZPyaVyLGY9jAYNvv+/L+Pr//28HOTSqFX447tvxFvfsIcnL0RL4Kl2yCsVh0cDGyaokUimcbZzRF5hB0hB9u2tnqKe2HBX25HJCjiXC95f7BmDWqVEXQ1XoBUzaUGGH4BU4mTXNi8bixKVoOb6CgRDMUxOR5BMZXDq7CD276rnOWcJOX9xFEOj0uexQqHAzq1eVDgtBd4rKlVOhxmH9prgGw/iQs8Y4ok0AGDY58foeAAN3nLUe8tZAWWNiKKI9o5hCIJUdqqxtpwlypeBQQ0qeZXlFhza14STZwcRCseRzQo4fX4IgVAMrc3VPGGjoqBUKuGqssNVZceB3Q1598UTKQz7AnIJq5keHoMjfsTiyXmvlUpn0DswkbfKf4bNYpCblHvdDin44XbAXcVyVoUwt0G4Rq3Ch/7hBzh1blDe1lBbgY+8/3auWiZaBqfDBINeg3gijcnpCBLJ9LpfVTY6FsC5C6N5GX+eagdam6tL4qKz1lOGbFZAZ7cPgDRBo1IqWcqoSIUjibwM0u0tbtZ1JipRCoUCbVtq8MLRbiSSafiDMVzsGUdLc3Whd42WoKNrFAPD0wByf8utNczqpmumUCjgqrKjqsKK/qFp9PRPIJ3JQhBEdPdPYHDEj6b6CnjdDs6nrbL+oSl5EaTJqENTPecFloMzXLQuGA1aXLenAecuzK5iGBieRjAcx65tXhj02gLvIdHiDHotmusr0VyfXwdZFEUEgjEMjvrlhuUDw9MYHvVjZCyITHZ+OatgOI5g5zDaO4fztisVSlRVWPMzO1wO1LjLUF5m4snKKvFNhCCKIk6fG8KzL15EPCmthFFAgbe9aS/uv+sGBpuIlkmhUMBd7ZDLaYz4Auu2+WkylcHZzhGMT8725tFp1djW4kFleWmt0myoLUcmK8hlQ89eGIFKLfWvouKRyWRxon1QXjHodZex6TRRidNq1di1zYuXTvZCEET0Dk7CbjNycrzIXegeQ9/glHy7bYuHx0xaUUqlEg215fBU29EzMIGB4WkIgohUOoPzF0fRPzSNzU2VGyYreq1FY0lc7B2Xb29vcXNeZpk4k0LrhlKpxPZWD+xWI85dHIEgSP0NDh/tQdvWmqIuzUC0EIVCAYfdBIfdhLYtNXn3ZbMCfOPBvMyOoVzwY3JO49gZgihgdDyA0fEAXjrZm3efXquBx+WAx2WHQadEZbkdFrMBVoseVrMeFrMeVrMBFrMeZpOO5SeWKBpLwjcexE9/fQrnL47ClFvlWuG04MPvux27tnkLvIdEpctTbZcnx4fXaVDDNxHEuc5RpNIZeZuryoYtza6SDYZuaqhENptF3+CUHPBVKZUlF6BZz9o7R+QsUavFgC2buJqbaD2w24xoaarG+YujAIAzHcMwm3QwGZmFVYwu9o6jZ05W/o5WDwPMtGq0WjVam12o9ThxsXdMLvMaiydxsn0QdusUWpqq4GBj+hUz0ysnmxUAAHU1Tv5+r0JpXhERXUaN2wGrRY+TZwcRi6eQSmdw9FQfmhsq0VRXAYVCUehdJLpmKpUyF4iYX7ojFk9heHRuZkcAg7mm5fFEat7jE6k0uvvH0d03jmw2C5VKBVxmmJiNuUCHRQ+LaSbooYc599VqyQ+EWEw6WC2GDRcMefK5DnzpG08hFE5Ar5cOt7fcuAXve9drYDGx9wnRtTAatCizmzAdiCIaSyIQjMFuMxZ6t1ZEKpXB+a5R+YISkHopbW1xrYuVcq3NLmQyAoZG/RBFESfPDmBvWx2cDi4+KbSB4Wm5bKJarcLOrTVcMUi0jtTVOBEIxTA6FkQmk8XJs4O4bk/jhjtHL3bd/RPywg0A2NbiXvCaj2ilGQ1a7NzqRX1NOTq7fZgORAEAgVAML57oRWW5FZsbq1iScgUMjfrl36/RoMWmhsorPIMWwqAGrUtWiwGH9jbhzPkhjOcaanb1jiMYiqNtS01J1J8mulpGgxabGquwqbEqb7soipgORKXMjuH8DI/RsaC8SuBKIrEEIrEERsev/Nj8/dLJmR8Wkz4vE8RiMcjBDylQooPNYoDZpC+58ZpMZfD/fedZ/OdjhyFkpfIddqsRD77ntXj1Da0F3jui9cNTbZcvBoZG/esiqDE+GcbZzmEkU7PZGVUVVmzd7IauRLMzFrKtxY2sIGB0LAhBEHH8zAD276xfF3/DUhUMxdHRNSrf3t7q5gpuonVo22Y3QuEEorEkwpEEzl0YxY4tnkLvFuX0DkziYs+YfHvLJhe87rIC7hFtRDarAQd2N2B8MozObh+iMSmDc3wyhImpMLxuB5rqK9fVuelaSiTT6OyeHedbN7uhVpfWnEexUIiiKBZ6J0pBKBSCzWZDMBiE1crak6VCFEX0DEyiq1equw1IE767tnlhtRggiiKCwSBsNhszOGhDy2SyGJsIYXh0AiLUiESTCEcTCIUTCEUSCEcSCEfiCEeSCEXiCIUTiESTEMSlBUKuhUGvXTAQImWC6GDJZYTkBUlM+oKUZ+nqG8fDn/8FevonEYlKJ39bNrnw/z7+NlQ4WV6llPD4UPyyWQFPvdCJTCYLtVqFV1/fUrKrTdPpLDq6fBj2+eVtGrUKWze7iqLcxGqMB0EQcPLskNwvRKNWYf+uelgthhV5fVq6dDqLw8e6EYtL2Zz1Xidam10F3qvixeMDlbpwNIEjx3rkBU3bWzyocV9dJgDHw8rpH5qSy4MBQEtTNRpqywu4R7Rc63E8iKKI4dEALvaO5S26UatVqPc60eAtL9nz70I5frpfXnxd43Jge+v6DCyLorjq44BhNVrXFAoFmuoqYLMYcPrcEFLpDGLxFI4c78HWTW54XPZC7yJRUVCrVXBX22EyKJZ8EiYIAqLxFCIRKfgxGwSRgh/hyMz/paDI3McsJxgST6QQT6TyGuUuhV6ryQU7csEP0+z/rWYDLJbZQIlcRstiuKoVJ4Ig4Pv/exTf+N4LyGSzSGeyUKuVeO1NW3Hf269nQINoFahUSlRXWDE06pcDs+5qe6F3a9kmpyNo7xhGIpmWt1U6Ldja4oZepyngnq0upVKJXdtqcOz0AKb8EaQzWRw91Y8DuxtY1mCNnekYlgMadqsRmy/J9CSi9cVi0mN7ixunzg0BAM5dHJEWBzGoXDCDI9N5AY1NjVUMaFBRUCgUqHE74KqyoXdwEr0Dk8hmBWQyWXT1jmNoxI/m+kp4XPZ1E8hZTaNjATmgodOq0dLE3mXXgkEN2hDKy8w4tK8Rp84OIRCKQRBEtHcOYzoYhaeSpQ6IroZSqZSCASY9XMuY/xBFEbF4SsoAkQMdueBHNIlgKI5wNCEHQ2aCI6FwAllh6cGQRCqNxHQaE9PhZf1cWo1aKn01px/IvHJZcpaI1Bvji19/Cmc6huTXqHBa8Obbd6Gy3FqSk6xEpcJTbcfQqJTdMOwLlNR4y2Sy6Owew+DItLxNrVZhS3P1hqmdrVQqsWdHLY6e6oM/GJP7oB3Y3QCjQVvo3dsQegcm87Jldm5jHw2ijcBVZYc/GMPA8DQEQcTJs4M4tLep5Mq+rgfDo36c7RyRbzfVS71AiYqJSqVEc30lvO4ydPeNY3BE6o2WSKbR3jmMvqEpbG6sQmU5F/MtJpnK4PxFn3x762Y3P3OvEYMatGEY9Foc2F2Pzu4x9A9NAQBGfAGM+iZx40ET6wYTrRGFQgGTUQeTUQdX5dKb3oqiiHgiPZsJEo4vUCJL+n8oEpczSEKRBDLZ7JK/TyqdwcR0eNnBEABQQIE7btuFBq8TarUKDptxXa+0Jio0h90Eo0GHWDyJKX8EsXiqJCbDpwNRtM9ZHQ9ICzC2tbhh0Bf//q8klUqJPTvq8PKpPoTCcSSSaTmwwc/P1eUPRHFhTu32tq01G+79R7SRtTZXIxROIBCKIRZP4cz5IezeUcvV1mtodCyAMx3D8u3G2go2DKaiptOqsXWzG7UeJy70jMkLIyLRBI6f6YfTYUZLUxUzvxZw/uIoUmmphFd1pQ1VFWxtcK0Y1KANRalUYssmF+xWA9o7R5DNCohEkzh8rBttW7yMKhMVMYVCAaNBC6NBi+plLF6aWUGS3w8kVw4rImWKzM0OCUUSUkAkkpBPOpaiqtyKv3nfbTDqtejunwAgnawQ0eryuOxyU81hX6CoJwOyWQEXemYXVwDSpH5rc/WGbgSq0aiwr60OL57oRTSWRCyewssnpcAGm1CujlQqg1PnhuSec011FSyVSLTBKJVK7NxWg8NHe5BKZzA+FUbPwCSzBNbI6HgQp8/PBjTqvU5sbmL5PyoNZpMOe3bUwh+IorN7DIFQDAAw5Y/ghaMRuKps2NRQVRKLjdbC2EQIvvEgACkzdssm9i5bCbxKoA3JVWWH2azHiTMDiEajyGQEHD/Tj6a6CjQ3VHJ1CtE6olAoYNBrYdBrlx24lIIhs5kgUiAknrctHEmgtsaJt79pH8wmHX535KL8fK6+IFp9nmo7unrHIYoiRnx+NNdXFOVx3B+I4kzHCGLxpLytzG7C9lYPL/gAaLVq7N9Vj5dO9CIWTyEaS0oZG7samJq/wkRRxOnzQ3IflzK7Cc1FHAwkotVj0GvRtrUGR0/1AQC6esdhtxrgdJgLu2PrmCiK8E2EcHpOYLnWU4bWZk5yUulx2E24bm8jRseDuNgzLp/njo4FMTYRQp3Hica6ig19LpdOZ/N65mzZ5OKinRXC3yJtWBaTHof2NuLI0QuIJqQ6/d39EwiE4ti5tQZafsgQbXh6nQZ6nWbJq1fDkYR8IldmN7F0CtEa0Os0KC8zY2IqjHgijelAtKgmYwRBwMWecfQOTsrblEoFNjdWoa7GWZQBmELR6zTYt1MKbMwElY+d7se+nXVQqzfuxfBK6+6fwOR0BIBURqJtaw3fh0QbWHmZGc0NlfICgdPnhnBoXxPPY1eAIAiIxlIIheNSidzc12x2tk9gjcvBVdtU8lyVNlSVWzA44kd33wRS6QwEQUTv4CSGRv1orKtAXU3Zhuzb1dntkxeSVDgtJdUDsNhx1pY2NLVahR1b3JgOpnExdxInpct1Y9c2L+w2NhEnoqUbHQvK/3dVsfQU0VpxV9sxMSX1wRka9RdNUCMYiuNMxzAi0YS8zW41YscWD3t5LcJo0GLfznq8fLIXyVQGgVAMJ9oHsbetdkNeCK+0KX8EXb3jAKRMxratNZy4JCI01VUgEIxhcjqCZCqDU2cHsX9XPT93l0EQpNLWoXACwXA81wcwAUEQF32Ou9qObS1uBpZpXVAqlairccJdZUfvwCT6hiYhCCLSmSw6u30YGJaaiVdX2jbMe37KH8HQqB+ANP+4dTMDmCuJQQ0iAA215bDbjDh1dhDJVAaJZBovnexFa7MLtZ6NW+OaiJbHNyEFNRQKBSrLWXqKaK1UlVugUauQzmQxNhFCOp0taJq7IAjo7ptAz8CkXFpCqVSgub4SDbXlG+ZC7mqZTTo5YyOdyWLKH8HJ9kHs2u7lBNs1SCTTOH1uSL7d3FBZNAFAIioshUKBti01OHysG/FEGv5gDBd6xlgSaREzvTmD4bicfRGJXj6AMcNo0MJi1qOizAKPy85zAlp3NBoVNjdVwetxoKt3AsM+aVI/nkjj1Lkh9A5OoaWpat2fg2SzAs52jsi3W5qqYNCz5OxKYlCDKKfMbsKhfU04dXYQ/mAMgiDi3IUR+INRbG/xQKXiRTQRLS4YiiMWTwEAnA4T62QSrSGlUgl3tR39Q1MQBBG+iWDBGm+HIwmcPj+EcGQ2O8NqMWDHFg8sJn1B9qkUWcx67G2rw9HT/chkshifCuP0+WHsZKmkqzJTUiaZygCQys001pYXeK+IqJhotWrs2laLF0/0QBBE9A1OwW4zorpiY2cfZ7PCnPJRCYQicUSiSXnRwuUYDTrYrHpYzQZYLdLXjdxbgDYWg16LHVs8qPc60dntk0tfhsJxvHyyDxVOCzY3Va3b8+OLvWPy/ECZ3YQal6PAe7T+cMaFaA69ToP9u+rzal+PjgURjiSxa5sXZhNLRRDRwnzjs6WnNvrFH1EheHJBDQAYHg2seVBDFEX0DEyiu29cXqmpUCjQVF+BxtpyZhhcBbvNiD07anH0VJ8UrBoPQq1SslTHVbjYM47pQBSAdL7btoXBISKaz2Y1oLXZhXMXpNXF7R0jsJj0G6ZkYiaTzet9MbNo6UoBDIVCAZNRlwtc6GG1GGA169kPigjSQpV9O+sxOR1BZ7dPXvgzMRXG5HQEnmo7mhsq11U5zEAwhr5B6bpEqVTw3HWVMKhBdAmlUomW5mrYbAa0d4wgk8kiEk3gyPEebG91c7KyCGWzAqYDUUxORzA5HUE6nYXZpIPFrIfFpIfFrIfZpGO2Da2q/NJTS2ssTkQrx2oxwGLWIxxJIBCKIRJNrtlihHA0gfaOYQRDcXmbxazHjlYPrBbDmuzDelVmN2H39lqcaB+AIIgYGvVDpVKyqeoyjE+G0TMwAUA6Ru3cWgMtswmJaBG1njL4g1GMjgWRyWRx8uwgrtvTuO6updLpLEKRuJx9EQwlEIsnr/g8hUIBs0k3J/tCCmKst98P0UorLzPD6WjC6FgQF3vHEE+kIYrSud3oeBD1NU401JaXfDBQEASc6RiWb29qqNowgeG1xrNZokVUV9hgNupx8uwgItGEdELXPogGbxybGiu54rLAItEkJqelyP50IDqvful0ICOvSASkk8+Z+qWzwQ4daxrSiggEY4gn0gCkkzVOFhEVhqfajo4uHwBgeNSPlubqVf1+oiiV57jYO5aXndFYW46m+gqeK6yQCqcFbVtrcOrsEERRRP/QFNQqJTY1VhV614pePJHCmfOzfTRamqrgsJsKuEdEVAq2bXYjFE4gGksiHEng7IURtG2pKfRuXbVUKiNlXuR6YIQjCbkszOUolQqYTVLgwmY1wGo2cLEc0TVQKBRwV9tRVWFF/9AUegYmkclkkc0K6O6fwOCIH80Nlahx2Uv2PLq7bwLRmBQgtVkNqPc6C7xH6xdnXYguw2zS4bo9DTh7YQSjY9Iq7N7BSQRCMezc5l1X6XHFLpPJYso/k40RlieQL6VQKKDVqOSa0TNEUUQ0lkQ0lswrE6RRq2azOuZkdvBElZZjJksDAKor2CCcqFDcVXZc6JECDCNjAWxuqlq1VO9oLIn2jmH4gzF5m8moQ9uWGtiszM5YadUVNmRbZle+dfdPQKVSorGuosB7VrwEQcCps0NIZ7IAgMpyK+q97KNBRFemVquwe3stjhzvQSaTxYgvAIfNWLB+VcuRTGUQDMXlLIxwJL7oteNcSqUCFrMeNotBzsIwm3QlO7FKVMxmzuFqXA70DExgYHgagiAilc7g3IUR9OWaiVeV2LV1OJJAz4BUyl6pVGB7i4dlp1YRgxpEV6BWq7BzqxcOmwkdXaMQBBH+YAyHj3Zj5zYvyrjabdWEIwm5zqI/GFu0lqlBr0F5mQXlZWaU2U3QaFRIpTIIRxMIRxIIR5IIRxOIRBPzMjrSmSz8wVjepBQgNXWzmPNLWBn0Gh6QaB5RFOEbDwGQTlwqy0vrxItoPdFq1ahwWjA2EUIylcHEVGTFy8GJooiB4Wlc6BlDNivI2xu85WhuqGRQfBV5XA5kBVGu9X6hZwwqlRJ1NVwBt5ALPWMIhKTzG6NBix2tngLvERGVErNJh20tbpw6OwgAOH9xFFazoagC94lkOhfASCCc64ORSF45gKFSKfN6X1gtBpiMWgYwiNaYVqtGa7MLXncZLvaOywtQY/EkTrQPwGEzoqWpGnabscB7emWiKOJMx7A8b9VQWwGLeX02QS8WDGoQLVGtpwxWs1SOKpFMI5nK4OWTfdjcWIWGWq56WwnpdBaT/ggmp6RsjEuzLWYolQqU2U1STcYyMyym+QcKrVYNp9YMp8Msb5OyNVJSgCOSC3hEEwuu3InFk4jFkxibCMnb1GoVLCadlIJs0cNslIIepV7zka5NIBiTL57KHWZoNHw/EBWSp9ohf3YP+/wrGtSIxVNo7xjOK29oNOiwo9XNkj5rpNZThkwmiws9YwCkSTa1SgmPy1HgPSsuvolgXoPKXdu8PD4R0bK5Km0IBGPoH5qCIIg4eXYA1+9rLsjnSSyeQigSRzickJt4p9ILXy/OpVarcoELPaxmAyy56zguViMqHiajDru2eRGocaKz2ycvOvUHYzhyvAdVFVZsbizu3hS9A5MIhaX+emaTHk11nCdcbQxqEC2D3WbE9fuacOrcEKb8EYiiiM5uHwKhGHa0eji5vUyiKCIUTmBiOozJqQiC4fii2RhGgw4VTrOcjXE1K2FnmrqZTTqgcrbhezqdnZPVkchldSTzVuACUgmshbM6tDDnsjksZh0sJj2MBi1PlDcI35zAV/Wc9xURFUaF0wy9ToNEMo3xyTBSqcyK9LkZHJlGZ/cYMrlSPgBQV+PE5sYqZmessca6Crn2MgC0d45AqVLCxc9gADOl0Ubk21s2udiwnoiuWktTFYKhOAIhqYfc6fND2LOjdtWudURRzAUwErkARhzBUFwupXc5GrVqTvaFlIHB6zKi0mG3GXFwTyPGJkK40DMm96YYmwhhfDKMWk8Zmuoqiq6HZSSaRFffOABp3mlHq4eZX2uguN4FRCVAq1Vj3846dPWOyxfTYxMhhCNJ7N7hXTBrgGYlUxlMTUcwMR3G1HR00dU1KpUSTrsJ5U4LnA7TqkbkNRoVyuymvFJic0+mI5EEwtHkovVYY/EUYvEUxidnJ7dVKqVctkoqYSVleHCV5PoilZ6SUmSl0lMrW+aGiJZPoVDAXWVHz8AERFHqrXEtfQQSyTTaO4YxOR2Rtxn0Gmxv9eRlA9La2tRYhUxWQP/QFERRxOlzQ1CrlKhwbuzP4WxWwMmzg3LwzVVlK4ka+ERUvJRKJXZt9+KFl7uRSmcwMRVGz8Akmlagp5GcSR+J55p4S1kYmSUEMLQaNawWqQeGJZeFYTRor3mfiKjwqiqsqCy3YGjUj67ecSRTGYiiiP6hKQz7AmioLUd9jbMoFhaJooizncNyqfP6GmdRlelbzxjUILoKCoUCmxqrYLMacea81IAxFk/iyLEebG9xw1VlL/QuFg1BEBAMxTE5HcHEdEROx1uI2aRHRZkZ5U4zHDZjQSPbCoUCJqNOCqZcktURic4EOWYzOy7N6shmBQRCMbmW9QyDXiMHO8y5fh0mI1cPFZtMJotkKoNUKoNkOoNkMvf/3LZE7msqnZFPXsrLLMzWIioS7mopqAEAw76rD2oMj/rR0eXLWx1a43Kgtbma470ItDZXI5MRMOzzQxRFnGgfwL6d9Ru639n5i6MIRxIApFIO2za7C7xHRLQe6HUatG2twdFTfQCArt5x2K2GZX3eiqKISCyJUK4HRijXA+PS66iF6LRqKQNjThaGQc8ABtF6plAo4HWXwVVpQ+/gFPoGJ5HNCshksrjYM4bB4WlsaqiEu9pe0PmUwRG/XM3DaNChuaGyYPuy0TCoQXQNKsstOLSvCSfaBxDOnZCdOjeEQCiOlqaqDZtuFk+kMDUdlbIx/NFFV9qo1So4HSZUlFngLDOVxImpRqOCw27Kq50+k9UhlbCSgh2RaAKxeGre8+OJNOKJNManwvI2lUoJs0l3SWYHszpWmiAIuaBEdk5wIp0XrEjm/i3l4upSriqWPSEqFmaTDnarEYFQDOHcxMlyyu8kkmmc6xzJ+6zW6zTY1uLe8JkAxUShUGB7qxtZQYBvPAhBEHH8zAD2tdWVREPJlTbiC2Bo1A9AOrfYtd3L4BsRrZjyMjOaGyrR1TsOURRx6uwQDu1rXPCxgiAgEk3mMi/i8teZxUCXo9dpcgEMKfvCatFDr9Os9I9DRCVCrVZhU0MlvG4HuvsmMDQqLWZJJNM40zGMvqEpbG6sKsg5ejyRkvu8AcD2VndRZI9sFAxqEF0jo0GL6/Y04tyFUQz7pAvJ/qEpBENx7NxWUxIT9ddKEARMB2K5slIRRKKJRR9rtRikbIwyM2xWw7oI/MzN6qiek4WdyWSlAEdsblZHcl6QJ5uVslmCofwsFr1OA2suyGE26WA2s6ndpURRRCqdzc+iSKaRSmfkAEYiKQUullKHd6kUCgW0GhW0WjX0Og3sNiNruRMVGY/LLmfLDfsCSw5qjI4FcO7CaN5nhrvaji3NLgabi5BCoUDbFg+ErIDxqTAymSyOne7Hgd0NsJg3TknQcDSBsxdm+2hs3eRiSVQiWnFNdRUIhuKYmAojlc7g1NkhNNfZEQzFEY4mEAonEAzHEYkmlhTAMBq0sJj1czIwDNAVWa18IioOMwuMamvKcLF7TF58FI4kcOx0P8rLzNjcWLWmfcTOdo7I8zted9mGzhYuBIW4WFfeAvrSl76Ez3zmM/D5fNi5cye+8IUv4MCBAws+9oc//CH++Z//GV1dXUin09i0aRM++MEP4p3vfKf8mPvuuw+PPvpo3vNuvfVWPP7440vep1AoBJvNhmAwCKvVenU/GBUdURQRDAZhs9lWZKJ4aMSPcxdH5BM4rUaNtq01KC9bfzW3o7EkJqcjmJqOYCoQXXRlu1ajhrNsJhvDvOFPUkVRRDyRlstWScGOJGLx5JKer1QqpKbkplxT8lxWx0o0ylrp8XAt0ulcNkUuOJFMpmczLNIZOVCRSmcXbS5/NTTq2UCFVquCTquGVqOGTquGTqeRbmvV0GpUBf8d0eoqpvFAVyedzuLpw53IZgVo1Cq8+oaWywbSU6kMzl4YwdjEbH8knVaNrZvdqKrY2Od+pTAeBEHAsdMDmPJLvU+0GjUO7G6A2bR6PbmKRSaTxeFjPXIzzRqXA9tbPQXeq/WrFMYD0WpKpTI4fKxb7jUYjUZhMl15Is9o0OWyL6Tghc1i4GIBWld4fFhb04EoOrt98xaHuqvt2NRQueoLjIdH/TjTMQxACrjcsL+Zn2lziKK46uOg6GYXv/e97+HBBx/EV77yFRw8eBCf+9zncOutt6KzsxOVlfPrkpWVleHv/u7v0NraCq1Wi5/97Ge4//77UVlZiVtvvVV+3G233YZvfOMb8m2dbv1f4NDaq3E7YLXocfLsIGLxFFLpDI6e6sOmxio01paX9IEtmxUw5Y9iyh/BxFRk0Ul4hUIBm8WAcqcZFWUWWC36kv65V5pCoYDRoIXRoM2bJMtksohEk3MCHQlEosl52QWCIEr1Z8PzszpmAhwWs5TVYTJoiyoTJpsV8so8ScGJzCXln9JIpjJLWtm1VCqVUgpK5AISeV81+QGMYvp9EdG10WhUqKqwYsQXQDqTxdhkeNGMqrGJEM52jiCVzsjbqitt2LrJtSJBY1p9SqUSu7d7cfRUPwKhmHwOdmB3w7pvHHv2wogc0LCY9diyyVXgPSKi9UyrVWPXtlq8eKJn0XN2k1EnN/G2WgwsrUtEK67MbsJ1exrhGw/iYu+4XP57xBeAbzyI+ppyNNSWr8pnTzKVQUeXT769dbObn3EFUHSZGgcPHsT+/fvxxS9+EYC06srr9eL9738/PvzhDy/pNfbs2YM3vOEN+OQnPwlAytQIBAL48Y9/fNX7xUyN9Wm1IunpdBanzw9hYk4t7kqnBTu21JTUB104msDUdAST0xFMB6KLnrTqdRo4HVKD73KHuaR+xmIXT6SkPh1zgh2xeGpJ2QlKpVQWazbYIf1bLFvmasaDKIr5gYo5TbRny0FJAYzFeqtcDYVCIQcn9JcEKy7dxnridDW40mp9mPJH8PLJPgBAhdOCvW11efen01mcuziC0bGgvE2rUWPLZhdLys1RSuMhnc7ipZO9crNso0GLA7sb1m099qERP9o7pVWCarUKh/Y2wmTk4q3VVErjgWg1jY4FcP6iD6lUAm5XuRTAyF1v8PybNiIeHwpHEAQMDE+ju28ib2GoRq1CU30Faj1lK7qA8UT7gJzd7aqyYedW74q99nqx4TI1UqkUjh07ho985CPyNqVSiVtuuQWHDx++4vNFUcRvf/tbdHZ24tOf/nTefU8//TQqKyvhcDjwmte8Bv/4j/8Ip9O56Gslk0kkk7Mr0UOhkPw9iiwORNdg5u+50n9TtVpaLdjTP4GuvgkAwPhUGC8c7cKubd41rfG3HOl0FlOBKCanpAbfiWR6wccpANhtRjkb49Ka1RwjK0ev00Cv06DCOVvCLJsV5mV1hKMJZDL5JcAEQZTvn0urUeeVrrKY9TAZtVAoFPJ4SKUvzaDIylkUcxtqp9MrF6gApJXVOo0aOl1+NoUcwMht16iXXv6J70e6Gqt1fKC15bAZYdBrEE+kMTkVRjyRkie3J6bCONs5gmRqNjuj0mnB1hY3dFo1//ZzlNJ4UKuV2LezDi+d6EU0lkIsnsLLJ3txYHcDtJqiuvS5ZqFwPK+PxrbNLhgN2pL4O5WyUhoPRKuputKGqgrrgpO4HB+0EfH4UDgKhQJ1NU64q+zoGZhA/9A0RFFEOpNFR5cP/blm4tUrsGjJNxGSAxoajQqtzdX8my9gwwU1Jicnkc1mUVVVlbe9qqoKHR0diz4vGAzC4/EgmUxCpVLh3//93/Ha175Wvv+2227D7//+76OhoQHd3d3427/9W9x+++04fPgwVKqFVxA8/PDD+MQnPrHg9+Kbdf0QRRGRiFR7eTUGW7lDBwUcOHthFOl0FtEo8NvngmhprIK7uvArQEVRRCSazJWViiIYTiz6/payMUxwOkxw2IxQq6Uot5BNIhhcWj8IWlkWoxIWoxGoNAIAkskMwlGpbFUklkI4kkA8kZ73N40C8AfyX0uhUMCgVyMSiUGl1mAlP+bUKqXch2JuTwqtRp3Xt0KjUUGpvNw4zCKbySKeSSJ+mUcRrYTVPj7Q2rEYVZicCgAAOi8OwuOyo6t3AiNzsjPUahU2N1bCVWlFIh5Fgh8yeUpxPGyuL8OxMwOIJ9KIRqN45oU4dm+vgWadrB5OZ7I4empALrXgdTtg0EnXKrS6SnE8EK0WjgeiWRwPxaG63AC7pRLd/ZPwjUvBh2gUmJwKwGrWo7mhAg6b8apeO53J4uiJPrlk7fYWN+KxKOcnFiCKIhwOx6p+j6IKalwti8WCkydPIhKJ4Mknn8SDDz6IxsZGvOpVrwIAvOMd75Afu2PHDrS1taGpqQlPP/00br755gVf8yMf+QgefPBB+XYoFILX64XNZmP5qXVkZrJ3NdMDbTYbXNXlOHl2SO6DMDAaRlZUYetm15rX8E+lM5iajmBiOoKp6eic+uFKGI2zH+wKhQJldiPKyywod5phZimDknBp5yFByGV1RBIIy18TC2ZYiKIIhVINg8F4xfFwufJPOt1MAENqrK1SsU8FlZ61OD7Q2mjZZMD4tJSx5g+lEQhPIpHMyE1Ny8vM2NbiXrfliVZCqY6Hm6634KUTvVKvJhHo7g9i3866dXFcOtk+CIVSA5NJA6vFgL0769kXao2U6nggWg0cD0SzOB6Khw1AVWU5QuE4OrvHMB2IAgCyItDZM40KZwqbm6qXPc91pmMYGq0OGq0OlU4LNjfXrMLerw9rkRBQVEGN8vJyqFQqjI2N5W0fGxtDdXX1os9TKpVobm4GAOzatQvnz5/Hww8/LAc1LtXY2Ijy8nJ0dXUtGtTQ6XQLNhNXKBT8cFpnZv6mq/l3NRp0uG5PAzq6fBgYngYADPsCCEeT2LXNu6oNLEVRRDAUzwUxIgiEYpfdzwqnGeVlZpTZTevion+jU6lUsFmNsFnzVyIkkum80lXhSBLRWBI6rdRwXK/TyEGJmWwKnU4jl4Ji3xTaCNbi+ECrz2TUwekwY8ofySs1pVar0NpUjRr36q4gWi9KcTyYjDrs39WAl070IpXOIBCK4UT7IPa21ZZ0AKBvcBJjk7myB2oVdm3zLpp9TqujFMcD0WrheCCaxfFQXGxWIw7sbsDEVBgXesbk0twTUxFMTnejxuVAc0Plon1H55qYCmPEFwAgnX9tbXHz71xgRRXU0Gq12Lt3L5588knceeedAKRVxk8++STe9773Lfl1BEHI64dxqaGhIUxNTcHlcl3rLhMtmVKpxNbNbtitRpy9MIJsVkAoHMfho91o21qDCqdlxb5XIpmek40RyWuUNJdKpZQafJdJ/1YzuELFZbZXx+z7jo3NiGi98lTbMeWPyLedDik7g8e99c9s0mHfzjq8fLIP6UwWU/4ITrYPYtd2b0kGNgLBGDq7ZxeA7dhSw/cxERER0WVUOC0oLzNjxBfAxd5xJJJSme7BkWmMjAVQ7y1Hg9cJ9SJlSjOZLM7N6WPW0lTNLO8iUFRBDQB48MEHce+992Lfvn04cOAAPve5zyEajeL+++8HANxzzz3weDx4+OGHAUi9L/bt24empiYkk0n84he/wLe+9S18+ctfBgBEIhF84hOfwFve8hZUV1eju7sbf/3Xf43m5mbceuutBfs5aeNyV9thMetxon0QsXgS6UwWx073o6m+Es31FVc1mSwIAgKhOCanIpiYDs9rDD2XxaxHucOMcqcZDpuxJC/oiYiIlqO60ophnxnhSAJN9RWo9ZQxeLuBWC0G7G2rw8un+pDNChifCuNMxzDattSU1Psglcrg1LlBOZ2/sbYCleUrtyiGiIiIaL1SKBTwuByorrShb2gKvQOTyGSyyGYFdPeNY2hkGs0NlahxOeadH17oGUM8kQYgLY5ipndxKLqgxl133YWJiQl87GMfg8/nw65du/D444/LzcMHBgbyJmGj0Sj+4i/+AkNDQzAYDGhtbcW3v/1t3HXXXQCk0iunT5/Go48+ikAgALfbjde97nX45Cc/uWB5KaK1YDHrcWhvI850DGM8Vz6gu28cwVAMbVtqoF1C6ls8kcLkdAQTUxFMB6LILJKNoVGr4Cwzy4EMRpOJiGijUSqV2L+rvtC7QQVktxmxZ0ctjp3uhyCIGB0LQqVUYnurp9C7tiSiKOJMx7B8Qe2wGdHcUFHgvSIiIiIqLSqVEk11FfC6HOjun8DA8DREUUQylcHZzhH0DU5hc2MVqiqkfsr+QFQuI69SKbGtxV3I3ac5FOJadO5YB0KhEGw2G4LBIBuFryPFUG6nd2ASF3rG5FV3Br0GO7d6Ybfl90AQBAHTgRgmp8OYmIogGlu8xJrNapCCGGVm2G1XbvpMBBTHeCAqFhwPRLPW03gYnwzjRPuAfN5V73Witbn4S9L29E/gQo9UdkqrUeP6/U1cqFIg62k8EF0rjgeiWRwPpSkaS+JCzxjGJkJ528vsJjQ3VKK9YwSxuDT/1tpcjXpveSF2s+SIorjq46DoMjWINpqG2nLYrAacOjuIZCqDeCKNl072orXZBafDhMnpCCanpWyMbFZY8DW0GjWcZSZUlFngLDMvqckRERER0UZTWW5B29YanD43BFEU0Tc4BZVKhU0NlYXetUVNB6K42Dsu327bWsOABhEREdEKMBl12L29Nte3zAd/MAZAOv966USv/Di71Yi6GmehdpMWwJlPoiJQZjfh0L4mnDo7CH8wBkEQ85oQXUqhUMBuNaDcaUG5wwyrRc+VAERERERL4Kq0QcgKONMxDEAqAapWKdFQW3wr75KpDE6dne2j0VRfifIyc4H3ioiIiGh9sduMOLinEWMTIXR2j8nZGQCgVCqwvdXDebciw6AGUZHQ6zTYv6seF3rG0Dc4teD95WVmuT+GRqMqwF4SERERlT6Py4FMVsD5i6MAgM5uH1QqJWo9ZQXes1miKOL0uSEkUxkAQHmZGc317KNBREREtFqqKqyocJoxNBpAV+84UukMWpqqYTaxL3OxYVCDqIgolUq0NrtgsxrR3TcBnVaN8jKpN4bFrC/07hERERGtG3U1TmSyAi7melWcuzACtUoJd7W9sDuW09U3gSl/BIC0uGXHlhquECQiIiJaZUqltNClxmVHOiOwxHuR4l+FqAi5Km1wVdoKvRtERERE61pTXQWyGQE9AxMAgDMdw1CqFKiuKOx52OR0BN19Uh8NhUKBnVtreEFNREREtIaUSiV0WmWhd4MWwb8MERERERFtWJubquTGjzMlnyamwgXbn0QyjdPnhuTbmxoq4bCbCrY/RERERETFhkENIiIiIiLa0Fqbq+GpdgAABEHEifYBTAeia74fgiDg1NlBpNJSH41Kp6UoG5gTERERERUSgxpERERERLShKRQKbG91o6rCCkAKbBw/M4BgKL6m+3GxZxz+YAwAYNCzjwYRERER0UIY1CAiIiIiog1vpndFhdMCAMhksjh6qg/hSGJNvv/YRAi9g5MAAKVSgV3baqHRqNbkexMRERERlRIGNYiIiIiIiCA1hNy1zYuyXA+LdC6wEY0lV/X7xuIptHcMy7dbmqphsxpW9XsSEREREZUqBjWIiIiIiIhyVCol9uyohd1qBAAkUxkcPdWHeCK1Kt9PEAScPDuIdCYLAKiutMmNy4mIiIiIaD4GNYiIiIiIiOZQq1XY21YHi1kPAIgn0nj5ZD8SyfSKf6+OLh9CYal3h9Ggw/YW94p/DyIiIiKi9YRBDSIiIiIioktoNCrs21kPo0EHAIjFkzh6qh+pVGbFvsfoWAADw9MApD4au7d7oVazjwYRERER0eUwqEFERERERLQAnVaNA7vrYTRoAQCRaAJHT/cjnc5e82tHokmcvTAq3966yS1nhhARERER0eIY1CAiIiIiIlqEXqfB3rY66HUaAEAoHMfxM/3IZoWrfs1sVuqjkcn10XBX21HjdqzI/hIRERERrXcMahAREREREV2GyajD3p110GrUAAB/MIYT7QMQhKsLbJy7MIpINAEAMJv02LaZfTSIiIiIiJaKQQ0iIiIiIqIrsJj02LezTu55MTkdwcmzQ8sObAyP+jHs8wMAVColdm3zQqXiZRkRERER0VLx7JmIiIiIiGgJrBYD9u6olYMQ45MhnOkYhiiKS3p+OJLA2Qsj8u3tLW6YTbpV2VciIiIiovWKQQ0iIiIiIqIlcthN2L29FkqlAgAwOhbE2c6RKzwLyGSyONE+CEGQAiBedxlcVfbV3FUiIiIionWJQQ0iIiIiIqJlKC8zY+dWLxQKKbAxNOpHR9foZZ/T3jmCWDwJQMr42LKpetX3k4iIiIhoPWJQg4iIiIiIaJmqKqxo2+KRb/cNTuFi7/iCjx0YnoZvPAgA0KhV2Lm1BkolL8WIiIiIiK4Gz6SJiIiIiIiugqvKju0ts4GN7r5x9A5M5j0mGIrnZXFsb/XAZGQfDSIiIiKiq8WgBhERERER0VWqcTvQ2jxbSqqz24fBkWkAQDqdxalzs3006r1OVFVYC7KfRERERETrhbrQO0BERERERFTK6r3lyGQFdOXKT53tHIFKqYRvIoRYPAUAsFuN2NxYVcjdJCIiIiJaFxjUICIiIiIiukbN9ZXIZgT0Dkrlp06fH5Lv02rU2LmNfTSIiIiIiFYCz6qJiIiIiIhWQEtzNWo9ZfO279jigUGvLcAeERERERGtPwxqEBERERERrZAtm1xwV9vl2011FahwWgq3Q0RERERE6wzLTxEREREREa0QhUKBHa0e2CwGKBQKeN2OQu8SEREREdG6wqAGERERERHRClIoFKircRZ6N4iIiIiI1iWWnyIiIiIiIiIiIiIiopLAoAYREREREREREREREZUEBjWIiIiIiIiIiIiIiKgkMKhBREREREREREREREQlgUENIiIiIiIiIiIiIiIqCQxqEBERERERERERERFRSWBQg4iIiIiIiIiIiIiISgKDGkREREREREREREREVBIY1CAiIiIiIiIiIiIiopLAoAYREREREREREREREZUEdaF3oFSIoggACIVCBd4TWkmiKCIUCkGhUEChUBR6d4gKiuOBaBbHA9EsjgeiWRwPRLM4HohmcTwQzRJFEQqFAhaLZdXGA4MaSxQOhwEAXq+3wHtCRERERERERERERFS8xsfHUVFRsSqvrRBnUhDosgRBwMjIyKpGmGjthUIheL1eDA4Owmq1Fnp3iAqK44FoFscD0SyOB6JZHA9EszgeiGZxPBDNmhkPgUAANpttVb4HMzWWSKlUoqamptC7QavEarXyoEOUw/FANIvjgWgWxwPRLI4HolkcD0SzOB6IZq1mYgAbhRMRERERERERERERUUlgUIOIiIiIiIiIiIiIiEoCgxq0oel0Ojz00EPQ6XSF3hWiguN4IJrF8UA0i+OBaBbHA9EsjgeiWRwPRLPWYjywUTgREREREREREREREZUEZmoQEREREREREREREVFJYFCDiIiIiIiIiIiIiIhKAoMaRERERERERERERERUEhjUoHXr4Ycfxv79+2GxWFBZWYk777wTnZ2dl33ON7/5TSgUirx/er1+jfaYaPV8/OMfn/febm1tvexzHnvsMbS2tkKv12PHjh34xS9+sUZ7S7S66uvr540HhUKB9773vQs+nscGWk9+97vf4U1vehPcbjcUCgV+/OMf590viiI+9rGPweVywWAw4JZbbsHFixev+Lpf+tKXUF9fD71ej4MHD+Kll15apZ+AaOVcbjyk02n8zd/8DXbs2AGTyQS324177rkHIyMjl33N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R/G+972vUD8C0YoSBAHf+MY3cO+990Ktzq/GyWMDrWeRSAQnT57EyZMnAUiN+k6ePImBgQEoFAp84AMfwD/+4z/if//3f3HmzBncc889cLvduPPOO+XXuPnmm/HFL35Rvv3ggw/ia1/7Gh599FGcP38ef/7nf45oNIr7779/jX86ouW53HhIp9N461vfiqNHj+K//uu/kM1m5euJVColv8al4+FK51xExepy4yESieCv/uqvcOTIEfT19eHJJ5/EHXfcgebmZtx6663ya/D4QOvF5cbDjFAohMcee2zRawIeH2g9uNLcqs1mw7ve9S48+OCDeOqpp3Ds2DHcf//9OHToEK677jr5dVpbW/GjH/0IAJZ8zXFFItE6BWDBf9/4xjfkx9x0003ivffeK9/+wAc+INbW1oparVasqqoSX//614vHjx9f+50nWmF33XWX6HK5RK1WK3o8HvGuu+4Su7q65PsvHQuiKIrf//73xc2bN4tarVbctm2b+POf/3yN95po9fzqV78SAYidnZ3z7uOxgdazp556asHzo5n3vCAI4kc/+lGxqqpK1Ol04s033zxvnNTV1YkPPfRQ3rYvfOEL8jg5cOCAeOTIkTX6iYiu3uXGQ29v76LXE0899ZT8GpeOhyudcxEVq8uNh1gsJr7uda8TKyoqRI1GI9bV1Yl/8id/Ivp8vrzX4PGB1osrnS+Joih+9atfFQ0GgxgIBBZ8DR4faD1YytxqPB4X/+Iv/kJ0OByi0WgU3/zmN4ujo6PzXmfuc5ZyzXElitwLExERERERERERERERFTWWnyIiIiIiIiIiIiIiopLAoAYREREREREREREREZUEBjWIiIiIiIiIiIiIiKgkMKhBREREREREREREREQlgUENIiIiIiIiIiIiIiIqCQxqEBERERERERERERFRSWBQg4iIiIiIiIiIiIiISgKDGkREREREREREREREVBIY1CAiIiIi2kAUCgVe9apXXdNrPP3001AoFPj4xz++Ivu00dXX16O+vr7Qu0FEREREVBIY1CAiIiIiWmMKhWJZ/4iu1nPPPQeFQoGf/OQn8rbq6mr8yZ/8SQH3ioiIiIjo6qkLvQNERERERBvNQw89NG/b5z73OQSDwQXvW0nnz5+H0Wi8ptc4cOAAzp8/j/Ly8hXaK1otTz31FJRKJW666SYAQHt7O8bGxvCa17ymwHtGRERERHR1FKIoioXeCSIiIiKija6+vh79/f3g6fnGM1N6qq+vb8Vf++abb0YwGMTRo0cBAJ///OfxwAMPYGRkBC6Xa8W/HxERERHRamP5KSIiIiKiItXX1weFQoH77rsP58+fx5vf/GY4nU4oFAp5AvxHP/oR7r77bjQ3N8NoNMJms+EVr3gF/ud//mfB11yop8Z9990HhUKB3t5efP7zn0drayt0Oh3q6urwiU98AoIg5D1+sZ4aM70hIpEIHnjgAbjdbuh0OrS1teEHP/jBoj/jXXfdhbKyMpjNZtx000343e9+h49//ONQKBR4+umnl/z76u3txbvf/W7U1tZCp9PB5XLhvvvuQ39//6K/h6GhIdx9990oLy+H0WjEDTfcgN/85jcLvv7k5CQ+8IEPoKGhATqdDpWVlXj729+O9vb2BR+fSqXwr//6r9i/fz8sFgvMZjO2bt2KBx98EH6/f97jl/N7W8z09DS6urrQ1dWFzs5OHD58GDt27JC3/fKXv0RtbS2i0Si6urowPDy8rNcnIiIiIio0lp8iIiIiIipyXV1duO6667Bjxw7cd999mJqaglarBQB85CMfgVarxY033giXy4WJiQn87//+L9761rfi85//PN7//vcv+fv81V/9FZ555hm88Y1vxK233oof//jH+PjHP45UKoV/+qd/WtJrpNNpvO51r4Pf78db3vIWxGIx/Pd//zfe/va34/HHH8frXvc6+bHDw8O4/vrrMTo6ittuuw27d+9GZ2cnXvva1y67PNKLL76IW2+9FdFoFG984xuxadMm9PX14b/+67/wy1/+EocPH0ZjY2Pec/x+P2644QZUVFTg3e9+NyYmJvC9730Pt912G37wgx/gzjvvlB87MTGBQ4cOobu7G6961avwjne8A729vfjBD36An//85/jVr36FG2+8UX58PB7Ha1/7Wjz//PPYtGkT7r//fuh0Oly8eBFf/epXcc8998DhcFzV7+1yPv/5z+MTn/hE3rZvfvOb+OY3v5m3bdOmTQCAm266aVmBIyIiIiKighOJiIiIiKjg6urqxEtPz3t7e0UAIgDxYx/72ILP6+7unrctHA6LO3bsEG02mxiNRvPuAyDedNNNedvuvfdeEYDY0NAgjoyMyNsnJiZEu90uWiwWMZlMytufeuopEYD40EMPLfgz3HHHHXmP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+eefR3NzM772ta/hO9/5Dtra2vD444/P6zVRUVGBF198EX/5l3+J7u5ufPazn8UTTzyBO++8Ey+++CJuvPHGvMfr9Xo88cQT+OxnPwuTyYSvfe1r+PKXv4zz58/jz/7sz1BfX39Vv7OleOaZZ6BQKPCKV7wCAHDx4kWMjo7ipptuWrXvSURERES0VhTi3FxrIiIiIiKiInDjjTfi8OHDCAaDMJvNK/76CoUCN910k9wPg4iIiIiISgMzNYiIiIiIqGBGR0fnbfv2t7+N559/HrfccsuqBDSIiIiIiKh0sacGEREREREVzPbt27F7925s3boVKpUKJ0+exNNPPw2LxYLPfvazhd49IiIiIiIqMgxqEBERERFRwfzZn/0ZfvrTn+Lo0aOIRqOoqKjAH/zBH+CjH/0oWltbC717RERERERUZNhTg4iIiIiIiIiIiIiISgJ7ahARERERERERERERUUlgUIOIiIiIiIiIiIiIiEoCgxpERERERERERERERFQSGNQgIiIiIiIiIiIiIqKSwKAGERERERERERERERGVBAY1iIiIiIiIiIiIiIioJDCoQUREREREREREREREJYFBDSIiIiIiIiIiIiIiKgkMahARERERERERERERUUn4/wEWCupiAJ2oDAAAAABJRU5ErkJggg==", "text/plain": [ "
" - ] + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABjUAAAGGCAYAAAAzegNcAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd0W9eVNvwHlZ0gCYC9F5HqvVruclwSl7g7iVscZxKPnTev02bmc+JxJhlPnIxfJ3GqU5yZFLe491i2ZVu9USIlUey9AGBBIYh67/fHJS8BkZJICiAKn99aWkLHIYkDXJx99t4KURRFEBERERERERERERERRTllpAdAREREREREREREREQ0EwxqEBERERERERERERFRTGBQg4iIiIiIiIiIiIiIYgKDGkREREREREREREREFBMY1CAiIiIiIiIiIiIiopjAoAYREREREREREREREcUEBjWIiIiIiIiIiIiIiCgmMKhBREREREREREREREQxgUENIiIiIiIiIiIiIiKKCQxqEBERERFFodLSUigUCjz99NORHkpIKRSKWf+76KKLwjKWf//3f4dCocC///u/h+XxiYiIiIgo9NSRHgARERERES0cd95555TL+vv78c4775z2+pqamrCPKxKefvpp3H333bjzzjvjLnhFRERERBQuDGoQEREREdG8mW7x/sMPP5SDGvO5uH///ffj1ltvhcFgmLfnJCIiIiKic8OgBhERERERLUgGg4EBDSIiIiKiGMOeGkREREREcaK7uxsPPPAAqqqqkJiYCJ1Oh/POOw+/+c1v4Pf7p73P888/j23btkGv10Oj0UCv12PJkiW49957cfTo0aDbWq1WPPTQQ1i+fDlSUlKQkJCA/Px8nHfeefje974Hr9cb8p8psO9FZ2cn7rnnHhQVFUGj0eCuu+6Sb/fiiy/iS1/6EpYtW4bMzEwkJiairKwMX/ziF3Hy5MmzPvZ0Ghsb8U//9E+oqKiQf58XXHAB/vznP592vKIo4sUXX8RnPvMZ5ObmQqvVIjc3F1u3bsWPfvQjjI2NAZB6ptx9990AgD/96U9n7CHidDrxX//1X1izZg3S0tKQnJyMpUuX4qGHHsLw8PCUMbS3t0OhUKC0tBR+vx+PP/44Vq9ejdTUVCgUCthsNqSnp0OtVqOrq+u0P8tVV10FhUKBX/7yl6e9DRERERHRfGOmBhERERFRHNi/fz+uuOIKDA0Nobi4GNdddx2sVis+/PBD7Nq1Cy+99BJeffVVaLVa+T7f//738fDDD0OtVmPLli0oKCiA1WpFZ2cnfv/732Pp0qVYsWIFAGlhfevWraivr4fRaMSll16KlJQU9Pf3o6GhAbt27cKDDz6IjIyMsPx8TU1NWL16NbRaLc477zyIohiUZXHzzTcjISEBS5YswSWXXAKfz4f6+nr88Y9/xHPPPYd3330XW7ZsmfHzPf/887jjjjvgcrlQU1ODq666ClarFXv37sXtt9+O999/H3/4wx+C7uP1enHrrbfixRdfhFKpxIYNG3DJJZfAYrHg+PHj+Jd/+RfccsstKC0txY033og9e/Zg586dqKiowNatW+XHCewhMjQ0hEsvvRS1tbVIT0/HJZdcAo1Ggx07duCHP/wh/vrXv+L9999HaWnplJ9BFEVcf/31ePvtt3H++edj8eLFOHbsGNLT03HXXXfh5z//OX7961/jhz/84ZT7trS04O2330Z6ejruuOOOGf/eiIiIiIjCjUENIiIiIqIY53a7cdNNN2FoaAhf+cpX8LOf/QwajQYA0NraiksvvRTvvPMOHnnkEXkB2+1247/+67+QmpqKAwcOoLq6OugxOzo65KwCAHjhhRdQX1+PK6+8Eq+88or8+AAgCAI+/vhjJCcnh+1n/Otf/4ovfOEL+N3vfoeEhIQp1//lL3/BZz7zGaSkpMiXiaKIX/3qV/jnf/5nfPnLX0ZdXR0UCsVZn6uurg633347FAoF/v73v+P666+Xr+vo6MDVV1+NP/7xj7jooouCFvz/5V/+BS+++CJKS0vx8ssvY+XKlUFjef/995GZmQkA+MlPfoKnn34aO3fuxNatW0/bS+S+++5DbW0tNm7ciDfeeAN6vR4A4HA4cPPNN+Ott97C5z//eezcuXPKfTs7OyEIAurq6rBo0aKg6x544AE8+eST+N3vfofvfe97U36nv/rVryCKIu68806kpqae9XdGRERERDRfWH6KiIiIiCjGPf/88+jo6EB+fj6eeOKJoIBDeXk5fvKTnwAAfv7zn8PlcgEAbDYbxsbGUF5ePiWgAQAlJSVBGQMDAwMAgMsuuyzo8QFAqVTiwgsvDMoCCbWsrCw8+eST0wY0AOCWW24JCmgAgEKhwH333YfNmzfj2LFjOHHixIye64c//CHcbjd+8IMfBAU0AOn38vvf/x4A8LOf/Uy+3GQy4cknnwQgBYACAxoTY7n00kuh0+lmNAZACko8//zzUCgU+O1vfysHNAAgNTUVTz31FBITE7Fr1y7s2rVr2sf4z//8zykBDQCoqqrClVdeCZPJhOeffz7ourGxMfzhD3+AQqHAP//zP894vERERERE84FBDSIiIiKiGPfhhx8CAG699dZpF/2vv/56ZGZmwm634+DBgwAAo9GI0tJSHD16FN/4xjdw/PjxMz7H+vXrAQCPPfYY/ud//gdDQ0Oh/SHOYtu2bWcNCDQ3N+PJJ5/E17/+ddxzzz246667cNddd8kBmdP11ggkCALeeustAFKgZDrr1q1DamoqDh8+LAeJPvjgA3g8HqxduxZr166dzY92Wh999BEEQcDq1avlMmCBCgoKcPnll8vPP50bbrjhtI//f/7P/wEAORgz4a9//SuGh4exbdu2aQNeRERERESRxPJTREREREQxrqenBwBQVlY27fUKhQJlZWUYHh6WbwsA//M//4Mbb7wRjz/+OB5//HFkZWVh48aNuOyyy3D77bcH9ay46KKL8J3vfAc//vGPceedd0KhUKCqqgrnnXcerr32Wlx99dVQKsO3Z2q6nhET/H4/7r//fvzmN7+BKIqnvZ3NZjvr8wwODsq3KyoqmtHtCwoK0NHRASC4H8a5OtvfFQAqKiqCbhsoOzv7jCXBLrvsMixevBh79+7FwYMH5WDML37xCwDA/fffP+exExERERGFC4MaREREREQL1Pnnn4/29na88cYb2LFjB3bt2oV33nkHb731Fh5++GG89NJLuPTSS+Xb/9d//Re+8pWv4LXXXsMnn3yCnTt34o9//CP++Mc/Yv369fjggw+mlIAKlaSkpNNe99Of/hS//vWvkZubi8cffxxbtmxBTk4OEhMTAQCf+9zn8Le//e2MAY8JgiDIp++8886z3v505bCiwZl+Z4AU7HrggQdw33334cknn8Qf//hH7N69G4cPH0ZpaSk+85nPzNNIiYiIiIhmjkENIiIiIqIYV1BQAEBqCn46bW1tQbedkJSUhBtvvBE33ngjAMBsNuOhhx7Cb3/7W3zxi1+UMxAmlJaW4oEHHsADDzwAANi/fz++8IUvYP/+/XjsscfwyCOPhOznmqnnnnsOAPCb3/wG11xzzZTrm5qaZvxYBoMBSUlJGBsbw09+8pOgbJUzKS4uBgA0NDTM+LnOZiZ/14nrTv27ztQdd9yBf/u3f8MzzzyDn/zkJ3Ipqq9+9athzbwhIiIiIporHqUSEREREcW4iy66CADw7LPPyj0eAr300ksYHh5GWlraWfs9GI1GPPbYYwCkRtXDw8NnvP369etx3333AQBqa2tnP/gQmOjvUVJSMuW6Y8eOzWpcKpUKl112GYDJYMlMXHLJJdBqtTh48CAOHTo0o/tMNFb3+XzTXn/BBRdAqVSitrYWR44cmXJ9X18f3n77bQDAxRdfPOOxBkpJScE999wDl8uF//zP/8QLL7yAxMRE3HPPPXN6PCIiIiKicGNQg4iIiIgoxt10000oLi5Gb28vHnzwwaBF8ra2NnzjG98AADzwwANySaaOjg787ne/m7bPxGuvvQYAyMzMRHp6OgApMDLRuDqQ1+uVF9anCyrMh8WLFwOQekEEjq+vrw933HHHaYMGp/Pwww9Dq9XiW9/6Fv70pz9N+ZkBoL6+Hi+++KJ8Pjs7G1/96lcBSH+P+vr6oNuLooj3338fVqtVvqywsBAATtukvbi4GDfddBNEUcQ//dM/YXBwUL5udHQUX/7yl+FyubBlyxZs2bJlVj9joPvvvx9KpRKPP/44PB4PbrvtNuj1+jk/HhERERFROLH8FBERERFRFPuP//gP/PrXvz7t9b/85S+xZs0avPDCC7jiiivwq1/9Cm+++SY2bdoEu92O999/Hy6XC5dffjkefvhh+X7Dw8O49957cd9992HVqlVyM+qmpiYcPnwYCoUCP/7xj6FSqQAAO3bswE9/+lMYDAasXr0a2dnZsNvt2LNnD0wmEwoKCvDtb387vL+M0/i3f/s3vP3223jqqafwwQcfYM2aNbDZbNixYwfKy8vx2c9+Fi+99NKMH2/NmjX485//jLvuugt33XUXHnroISxZsgRGoxFDQ0Ooq6tDd3c3brnlFlx//fXy/R577DG0tbXh1VdfxcqVK7Fx40aUlZXBYrHg2LFj6OnpQVtbG3Q6HQBg06ZNyM/Px+HDh7FmzRosX74cGo0G1dXV+Na3vgVACtQ0NDRg7969qKiowMUXXwy1Wo0dO3bAbDajrKwMf/nLX87p91daWoprrrkGL7/8MgA2CCciIiKi6MagBhERERFRFGttbT1jT4WJTIv169ejtrYWP/rRj/DWW2/hpZdeQkJCAlavXo077rgDX/rSl6BWTx7+V1RU4IknnsCOHTtQX1+PN998E6IooqCgAHfccQe+9rWvBZWquuuuu5CUlIRPPvkEx48fx44dO6DT6VBcXIyvf/3r+PKXvxyx3f0bN27EgQMH8NBDD2H//v149dVXUVRUhAceeAAPPfSQ3P/jdBQKxZTLbrrpJqxfvx4/+9nP8I9//AM7d+6E3+9HTk4OKisrcf/998t9SCZotVq8/PLLeOaZZ/D000/j4MGDOHDgAPR6PaqqqvD1r38dubm5Qbd/55138P/9f/8fdu/ejSNHjkAQBFx44YVyUEOv12PXrl342c9+hmeffRbvvvsuBEFAWVkZ7r33Xnzzm99EZmbmOf8OL7/8crz88svYvHkz1qxZc86PR0REREQULgpRFMVID4KIiIiIiGi+ffvb38aPf/xjPPbYY3IQYaHaunUrdu7cib/+9a+47bbbIj0cIiIiIqLTYk8NIiIiIiJacERRxL59+wAA1dXVER5NZL311lvYuXMniouLp2SfEBERERFFG5afIiIiIiKiBeX2229HXV0djhw5goKCAlx22WWRHtK8GxwcxHe+8x0MDw/jzTffBCD1BNFoNBEeGRERERHRmbH8FBERERERLSgKhQJpaWnYunUrfvKTn2DJkiWRHtK8a29vR1lZGdRqNcrLy/GNb3wDX/7ylyM9LCIiIiKis2JQg4iIiIiIiIiIiIiIYgJ7ahARERERERERERERUUxgUIOIiIiIiIiIiIiIiGICgxozJIoibDYbWK2LiIiIiIiIiIiIiCgyGNSYIbvdDp1OB7vdHumhEEWMKIoYGRlhcI8oxnEuE8UHzmWi2Md5TBQfOJeJ4gPncuxgUIOIiIiIiIiIiIiIiGICgxpERERERERERERERBQTojao8Ytf/AKlpaVITEzExo0bsW/fvhnd75lnnoFCocB111132tt85StfgUKhwBNPPBGawRIRERERERERERERUdhFZVDj2WefxYMPPoiHH34Yhw4dwsqVK3H55ZfDZDKd8X7t7e345je/ifPPP/+0t3nppZewZ88e5Ofnh3rYREREREREREREREQURlEZ1Hj88cdx77334u6778aSJUvw61//GsnJyfjDH/5w2vv4/X58/vOfxyOPPILy8vJpb9PT04MHHngAf/nLX6DRaMI1fCIiIiIiIiIiIiIiCoOoC2p4PB4cPHgQ27Ztky9TKpXYtm0bdu/efdr7ff/730d2djbuueeeaa8XBAG33347vvWtb2Hp0qUhHzcREREREREREREREYWXOtIDOJXFYoHf70dOTk7Q5Tk5OWhoaJj2Pp988gl+//vfo7a29rSP+6Mf/QhqtRpf+9rXZjQOt9sNt9stn7fZbAAAURQhiuKMHoMo3ky8/jkHiGIb5zJRfOBcJop9nMdE8YFzmSg+cC5HB4VCcdbbRF1QY7bsdjtuv/12PPXUUzAYDNPe5uDBg/jpT3+KQ4cOzeiXAgCPPvooHnnkkSmXW61WvrBpwRJFEQ6HA8DM3mCIKDpxLhPFB85lotjHeUwUHziXieID53J0yMjIOOttFGKUrdB7PB4kJyfjhRdewHXXXSdffuedd2JkZASvvPJK0O1ra2uxevVqqFQq+TJBEABIZatOnjyJ1157DQ8++CCUyslqW36/H0qlEkVFRWhvb58yjukyNYqKijAyMoL09PQQ/bREsUUURVitVuh0Or65E8UwzmWi+MC5TBT7OI+J4gPnMlF84FyODjGZqaHVarF27Vps375dDmoIgoDt27fj/vvvn3L7mpoa1NXVBV320EMPwW6346c//SmKiopw++23B/XoAIDLL78ct99+O+6+++5px5GQkICEhIQplysUCr6oaUGbmAOcB0SxjXOZKD5wLhPFPs5jovjAuUwUHziXY0PUBTUA4MEHH8Sdd96JdevWYcOGDXjiiScwOjoqByDuuOMOFBQU4NFHH0ViYiKWLVsWdP+JFJWJy/V6PfR6fdBtNBoNcnNzUV1dHf4fiIiIiIiIiIiIiIiIzllUBjVuueUWmM1mfO9730N/fz9WrVqFt99+W24e3tnZGVRKioiIiIiIiIiIKBIGhx0wmaxIT0/n7m4ionkQdT01opXNZoNOp4PVamVPDVqwWFuQKD5wLhPFB85lotjHeUwU+xyjbuzc3wyHw4GaqkIsrS6I9JCIaI74uRw7mO5AREREREREREQ0BwMWGyb2C3f1DmN4ZDTCIyIiin8MahAREREREREREc2ByWILOl9/sheCIERoNERECwODGkRERERERERERLPkcnthtY0FXTbqdKO10xKhERERLQwMahAREREREREREc2SedAunzbqUzFRgb+1wwzHqDsygyIiWgAY1CAiIiIiIiIiIpols2UyqFFWpEdpkQEAIAgijp3skXttEBFRaDGoQURERERERERENAt+vwDLsAMAkKBVIy01ERWlRiQnaQEAw1YnuvuGIzlEIqK4xaAGERERERERERHRLFiGHBAEKRPDqE8DAKhUSixZlC/fprFlAC63NyLjIyKKZwxqEBERERERERERzYIpoPRUtiFNPm3ISkV+bgYAwOvz40RT33wPjYgo7jGoQURERERERDTPfD4/WtpNqD3WjaGR0UgPh4hmQRRFuUm4SqWEPjMl6PqailxoNWoAwIDZhgGzbd7HSEQUzxjUICIiIiIiIponfr+Atk4LPtrThOZ2MwaHR3GorpMlaohiyIjVCY/XB0DKzFAqg5fXtFo1aipz5fMnmvrg8/nndYxERPGMQQ0iIiIiIiKiMBMEAV29Q/h4bxNOtvTLC6KAFOiob+iJ4OiIaDbMgw75dI4hfdrb5OdmwJCVCgBwub1oajPNy9iIiBYCBjWIiIiIiIiIwkQURfQNjOCTfS04drI3KCMjN1uHBK1UosYy5EB373CkhklEszBgkcpJKRQKOXAxnSWL8qFSSUtvHd2DGLE652V8RETxTh3pARARERERERHFI5PFjqa2AdgdrqDLsw3pqCrLRmpKAtKSlWhql4IZDS390GelIClRG4nhEtEMjDrdGHW6AQAZ6UnQatUQRXHa2yYnaVFZmo2TLf0AgPqTvdiyrnxKuSoiIpodBjWIiIiIiIiIQmhoZBRNrQMYPmVXtj4zFVVl2cjQJQOQsjj0mSlwukT09I/A5/OjvqEX61aWQKFQRGLoRHQWJotdPp19mtJTgUqL9OgzWWGzj8Ex6kJb1yAqSozhHCIRUdxjUIOIiIiIiIgoBGz2MTS2DsAy5Ai6XJeehKqynNOWqamuyMXQyCjGXF4MDjvQ1TuM4oKs+RgyEc2SeTAwqJF21tsrFAosq87H7oOtEEURLe0m5BrTkZKcEM5hEhHFNea7EREREREREZ0Dx6gbh+s7setAS1BAIzUlEauXFWPz2ooz1t3XaFRYWl0gnz/Z0g/nmCesYyai2fN4fHIGVkpywowDE+lpSSgt1AMABEHE8ca+sI2RiGghYFCDiIiIiIiIaA7GXB7UnejBzv3NGDDb5MuTk7RYsbgQ562vQI7x7OVpAMCQlYqifCk7w+8XUN/Qc9o6/UQUGZYhhzwvs/Vnz9IIVFmWjeQkqV/O4LADPX3DIR8fEdFCwfJTRERERERERLPg9vjQ2mFGV+8QBGEy8JCgVaOiNBuFeRlzagRcXZGDwWEHnGMeDI2MoqN7EKVFhlAOnYjOwYBlMng5k9JTgVQqJRZX5eHg0Q4AQENzPwz6NCRouTRHRDRbzNQgIiIiIiIimgGv14+m1gF8tKcRHd2DckBDo1ZhUXkOLti0CMUFWXMKaACAWq3C0up8+XxTmwmjTndIxk5E50YQBLm8nFajRoYuedaPYdSnIS9HBwDw+vxoaGYZKiKiuWBQg4iIiIiIiOgM/H4BrR1mfLSnES0dZvj9AgBp53VFiREXbFqE8hIjVKpz/4qtz0xFyXjtfZahIooeg8Oj8tw36tOgUCjm9Dg1lXnQqFUAgL4Ba1DjcSIimhkGNYiIiIiIiIimIQgCOnuG8NGeRjS2DsDr8wMAlEoFSgr1uGDTIlSV50CjUYX0eReV5yA5SWpAPGx1or1rMKSPT0SzFxh8MBpS5/w4CVo1aipz5fPHG3vhG39vISKimWFQg4iIiIiIiCiAKIro7R/BJ/uacbyxF26PDwCgUChQkJuJrRuqsLgqL2y18FUqJZbXBJahGoBjlGWoiCLJZJGCGkqlAobMuQc1AKAgLxP68ccYc3nR3G465/ERES0kDGoQERERERERjRsw27BzfwuOnuiGc8wjX55jTMeW9RVYvrgAyUnasI8jMyMFpUVSGSpBEFHX0M0yVEQRYrOPweX2ApBKxKnV556dtWRRHpRKqYRVR/cQrLaxc35MIqKFgkENIiIiIiIiWvAGhx3YfbAFh+s74Rh1yZcbslKxeW0FVi8rRlpK4ryOqapssgyV1TaG1k7LvD4/EUkGLJOlp7INaSF5zJTkBFSWZgOQssPqT/ZAEISQPDYRUbxjUIOIiIiIiIgWrBGrE/tr27G/tj1op3RGejI2rC7DupWl0KUnRWRsKpUSKxYXyA2JW9pNsDtcZ7kXEYVaUD8NfWiCGgBQWqRHWqoULLU7XOyfQ0Q0QwxqEBERERER0YJjH3XhUF0n9hxqxeCwQ748LTURa5aXYNPacmRlpERwhJIMXTLKigwAJspQcTc30Xwac3lgs0sBT116EhITNCF7bKVSiWXVBfL5lg4zRp3sn0NEdDYMahAREREREdGC4Rzz4OiJbuzc1wyTxSZfnpyUgJVLCrFlXUXIysuESmWZEanjpa9sdpahIppP5sHJoGcoszQm6NKTUFIo9c/x+wWcaOoL+XMQEcUbdaQHQERERERERBRuLrcXrR1mdPUOBzXcTkzQoKLUiILcDCiV0bnvT6lUYnlNAfYcaoUoimhpNyPHkC6XrSGi8AkMfuYY0sPyHFVl2TBZbBhzeWEZcqC3fwT5uRlheS4iongQnUdsRERERERERCHg9frR2DKAj/c2obNnSA5oaNQqVFfk4vyNVSjKz4ragMYEXXoSyoulMlSiKOLoiW6WoSIKM5/Pj6GRUQBAUqImbIFEtVqFxVX58vmG5n54PL6wPBcRUTxgpgYRERERERHFHZ/Pj46eIbR3WuD1+eXL1WoVSgr1KC3UQ6NRRXCEs1dRaoRp0A67wwW7w4WWDguqyrIjPSyiuGUeckAQpEBodpiyNCZkG9KQm61Dv8kKj9eHhpZ+rFhcGNbnJCKKVQxqEBERERERUdwQBAGdPUNo7bDA453c6axUKlBckIXyYiO02tj8KjxRhmr3QakMVWuHGdn6NOjSkyI9NKK4ZB60y6fD0U/jVIur8jA45IDX55dKUOVkwJCVGvbnJSKKNdGdX0tEREREREQ0A6Ioort3GB/vbZJKt4wHNBQKBQrzMnH+xirUVObFbEBjQnpaEipKjQCkn7muoYdlqIjCQBRFmC1SUEOtViErIznsz5mgVaO6Ilc+f7yxF34/5zcR0ali+2iOiIiIiIiIFjRRFDFgsaGp1YRRpzvoutxsHSpLs5GakhCh0YVHebEBJosdNvsYHKMuNLeZsagiJ9LDIoorw1anXLrOkJU6b313CvIy0DswgqGRUTjHPGhuM6G6MvfsdyQiWkCYqbGAcPcOERERERHFE8uQA7sPtqK2visooGHUp2HLugqsWloUdwENYLIMlVKpAAC0dVkwYnVGeFRE8cVkscmncwzhLz01QaFQYMmifHl+t3cPwmYfm7fnJyKKBQxqLBBtnRb846MTqD3WxeAGERERERHFvH6zFQeOtAct9mXqkrFxdRnWrihBelp895lIS01ERanUJHyiDBXL1BCFjsniACAFGQxZ8xfUAIDUlASUl0yWmas/2QtRFOd1DERE0YxBjQVAEAS0dJghiiL6TVbU1jOwQUREREREsUsQBDS2DMjn01ITsXZFCTauKUdmRkoERza/yosNcpPwUacbzW2mCI+IKD44Rt1wjknZX1kZKdBoVPM+hvJiA1JTEgEANvsYOroH530MRETRikGNBcA86IBvvA4kAJgG7QxsEBERERFRzOobsMI55gEA6DNTsWVdBYz6+d1JHQ0UCgWWnVKGanhkNMKjIop9gaWnjPrUiIxBqVRi6aI8+XxTm0l+3yMiWugY1FgA+s1W+bRCIR3smgbtqD3WzcAGERERERHFlIlM9AmVpUb5e85ClJaSiKqyySbhdQ29LENFdI7Mg3b5dCQDppkZKSguyAIA+P0CTjT1RWwsRETRhEGNOOf3CzBZpA9jjVqFtStKoFJJf3aTxcbABhERERERxZTe/sksDUNW6oIqN3U6pUV6ZKQnAwCcY240tg6c5R5EdDpujw/DVicAIDUlESnJCREdT1VZDhITNACkYEvfwEhEx0NEFA0Y1IhzpkG7vEsnx5gOQ1Yq1iwvDgpsHDnOwAYREREREUU/QRDQ2jmZpVEx3kh3oVMoFFi+eLIMVUf3IIZYhopoTsyWySyNHEPky9ppNCosrposQ3WiqR9er/8M9yAiin8MasS5voHJ0lN5OToAUs3ZNcuL5QPeATMDG0REREREFP2YpXF6KckJWFQ+WYaqvqEnqLciEc2MKUpKTwXKMaYj25AOAPB4fWho7o/wiIiIIotBjTjm9fphGZI+jBO0amQFHPBLgY2SoMDG0RM9EEUxImMlIiIiIiI6k1OzNCpLsyM4muhUUqhHpm6iDJWHZaiIZsnvFzA47AAgraPo0pMiPKJJSxblQa1WAQB6+oflcRIRLUQMasQxk8UGQZCCFLnZuinN86RSVJOBjX6TFUeOdzOwQUREREREUefULI2M8cV7mqRQKLCspkAuN9zZMwTLEBc+iWZqcHhULuFt1KdNWUeJpMQETVA21rGTffJYiYgWGgY14li/2SafzjWmT3sbQ1YqVi8rDgpsHD3BwAYREREREUUPQRDQ0mGSzzNL4/RSkhNQXZErnz92kmWoiGbKZJlcR4mW0lOBivIzA7Kx3GjtMJ/lHkRE8YlBjTjl8fjkHTlJiZoz7mIy6tOCAht9AwxsEBERERFR9Ojtt2LM5QUgfX9hlsaZFeVnQp+ZCgAYc3lZf59oBkRRhHm8n4ZKpYQhKzXCI5pKoVBgSXW+vH7T2mmB3eGK8KiIiOYfgxpxasBik4MSucappadOZdSnYdXS4MBGXQN7bBARERERUWSdmqVRUWKM4Ghig0KhwNLqfLn+fnffsLxYS0TTs9ldcHt8AKQ+pBNl3KJNWkoiyoql90FRFHHsZC/XbohowYnOd2g6Z/2myZTJvBzdjO6TbQgObPT2jzCwQUREREREEdXTP8IsjTlITtKiuiKw/n4vvF6WoSI6ncDSU9lRWHoqUEWJASnJCQCAEZsTnT1DER4REdH8YlAjDrncXgwOS6WnkpMSkJ6WNOP7ZhvSsHJJUVBgo76BUX8iIiIiIpp/giAE1YxnL43ZKcrPkkvouNwsQ0V0JqaAbCajIbqDGkqlEsuq8+XzTW0mjLk8ERwREdH8YlAjDg2YZ5+lESjHmI6VS4rkklU9/cNMZyQiIiIionl3apaGLn3mG7ZIEliGqqd/GCYLy1ARnco55pF7U2SkJyNBq47wiM4uMyMFRflZAACfz48TjX0RHhER0fyJ2qDGL37xC5SWliIxMREbN27Evn37ZnS/Z555BgqFAtddd518mdfrxXe+8x0sX74cKSkpyM/Pxx133IHe3t4wjT6y+gas8unc7PQ5PUaOMR0rlxbKgY3uPgY2iIiIiIho/jBLIzSSErVYXJkrnz92sodlqIhOYY6hLI1Ai8pz5ACMadCOfrP1LPcgIooPURnUePbZZ/Hggw/i4YcfxqFDh7By5UpcfvnlMJlMZ7xfe3s7vvnNb+L8888PutzpdOLQoUP47ne/i0OHDuHFF1/EyZMncc0114Tzx4gI55gHIzYnACAtNRFpKYlzfqxco46BDSIiojBwe3wsEUBEdBaBWRrZzNI4JwV5mTCO9whwe3w43hSfG/yI5iowgyk7hoIaGo0KixflyedPNPYxaElEC0JUBjUef/xx3Hvvvbj77ruxZMkS/PrXv0ZycjL+8Ic/nPY+fr8fn//85/HII4+gvLw86DqdTod//OMfuPnmm1FdXY1NmzbhySefxMGDB9HZ2RnuH2de9ZsCszRmX3rqVLlGHVYsCQ5sHG/sY2CDiIhojkadbny0pxEf720KKhlJRESTTs3SqGCWxjlbWp0PzXgZqr4BKz+DiMZ5vX4MjYwCAJKTtOe0OTQSco06ubG52+NDY+tAhEdERBR+URfU8Hg8OHjwILZt2yZfplQqsW3bNuzevfu09/v+97+P7Oxs3HPPPTN6HqvVCoVCgYyMjHMdclTpDzgwzTXOrfTUqfKygwMbXb1DONHEWo1ERERz0W+2we8XIAgi6ht6mLFBRDSN7j5maYRaYoIGi6smd3QfO9kLj8cXwRERRQfLkF3euBlLWRqBFi/Kk3vndPUOyUEaIqJ4FXWdjywWC/x+P3JycoIuz8nJQUNDw7T3+eSTT/D73/8etbW1M3oOl8uF73znO7jtttuQnj79wr/b7Ybb7ZbP22xSsEAUxajNUnA43bDZxwAA6WlJSE7ShmysucZ0iDX5qDvRAxFAZ88QAAQdFFP8m3j9R+scIKKZ4VyOrBHr5JdMr8+PI8e6sWF1qbx5gGimOJcpXgmCgJb2ydLD5SXGuH2dz/c8zsvRod9khWnQDo/Xh+ONvVi5tGhenpsoWg0ElJ4yZKXOaT5G+jM5MUGDylIjGpr7AUi9c7asq4BSGXV7mYmiWqTnMklm8t046oIas2W323H77bfjqaeegsFgOOvtvV4vbr75ZoiiiF/96lenvd2jjz6KRx55ZMrlVqs1al/YbZ2DGB2VFkrys1NgtYa2QVRyogIlhek43tgPURRxonEUDocDi8qZCr5QiKIIh8MBYGZvMEQUnTiXI0cURXT3WuDzTdY6Hh0dRa1aQHnJ2Y9jiAJxLlO86u4bwdCw9F3GkJUKiB5YrfGZ1RaJeVyQm4KuXjN8Pj9aRkeRlKBAjjE2d6cTnStBENHRNQCfzw+1WgUlfHNaS4mGz2RdqgpqpQCrfQyjo6M4Ut/G40uiWYqGuUyYUWWlqAtqGAwGqFQqDAwE1wAcGBhAbm7ulNu3tLSgvb0dV199tXyZIAgAALVajZMnT6KiogLAZECjo6MD77///mmzNADgX//1X/Hggw/K5202G4qKiqDT6c54v0hyjJmQkpICAKiqKEBigibkz6HT6ZCWmoa6hh4AwJDVgz7zGGoqp/5tKP5MBPR0Oh3f3IliGOdy5DhGXUhISERCApCSrIXT6YEIwDzkQmmJGpm6lEgPkWII5zLFI0EQYD7WL3+vWbWsDOlp8Vt6KlLzeP0qFY6ekL7TdQ84UFKciwRt1C0PEIXd4LBDPjbLzdYhMzNjTo8TLZ/JG9YkYveBFun4ctiNqooEpMZYjxCiSIqWuUxnF3VHLVqtFmvXrsX27dtx3XXXAZAObLdv3477779/yu1rampQV1cXdNlDDz0Eu92On/70pygqklJpJwIaTU1N+OCDD6DX6884joSEBCQkJEy5XKFQROWL2u5wYdQp7V7K1CUjKVEbtucqyMsEADmw0dE9CIUCqKlkKaqFYGIOROM8IKKZ41yOjBHbmHy6MC8LgiiiqXUAIoC6Ez3Ysq4SGo0qcgOkmMO5TPGmp98K93ifh2xDOnTpyREeUfhFYh7n52ZiwGLHgNkGr9eP4419WLO8eN6enyhamAcd8ukcY/o5zcNo+ExOT0tCeYkRLR1miKKI44192LC6jMcJRLMQDXOZzi7qghoA8OCDD+LOO+/EunXrsGHDBjzxxBMYHR3F3XffDQC44447UFBQgEcffRSJiYlYtmxZ0P0nUlQmLvd6vbjxxhtx6NAhvP766/D7/ejvl+oMZmVlQasNXwBgvvSbJtMj83Iywv58pwY22rsGoYAC1czYICIiOq1hq1M+nZGehAxdMgaHHBgaGcWYy4v6kz1YvYyLSkS0MAmCgNYOs3y+stQYwdHEv6WL8jE84oTH64PJYkPfwMi8fJckiibmQamfhlKpgCEzNcKjCY3yEiP6TDY4x9wYtjrR3TeMovysSA+LiCikorJj0C233IKf/OQn+N73vodVq1ahtrYWb7/9ttw8vLOzE319fTN+vJ6eHrz66qvo7u7GqlWrkJeXJ//btWtXuH6MedU3HtRQKBTIMc5PeayCvEwsqy6Qz7d1WXByvCkVERERTTUyHtRQKhXQpSdBoVBgxZJCaNRSdsaA2Yau3qFIDpGIKGK6eofhcnsBSFka8Vx2KhpotWosqZ7Mtj/e2Cf//okWArvDBeeYVPEiKyMlbrJlVSollgbM7ZMtA5zbRBR3ojJTAwDuv//+actNAcCHH354xvs+/fTTQedLS0ujtrl3KFhtY/IHsT4zZV5roRbmSxkb9SeljI22LgsUCgUWVeTM2xiIiIhigdvjkz+vdWlJUCqlvSWJCRosqynA4fpOAMCJpj5k6JKRxvrHRLSACIKAtk6LfJ5ZGvMj16hDXo4NfQNWeH1+HD/ZizUrSiI9LKJ5YRrP0gAAoz4tgiMJPX1mKgpyM9HTPwyfz48TTX3MBiaiuBKVmRo0O32mEfl0rlE3789fmJ+JpdX58vnWTjOaWgfOcA8iIqKFZ9g6Kp8+tSF4jjEdxQVSWQBBEHH0eDf8fmFex0dEFEnM0oicxZV58sY406AdPX3DER4R0fwwWWzy6WxDfAU1AKCmMhdajTS3B8w2DJhtZ7kHEVHsYFAjxomiiH6T9MGkVM5f6alTFeVnBQU2WjoY2CAiIgo0EthPQze18W11RS5Sx7Mz7A4XTrawpCMRLQynZmlUlWVHcDQLj1arxpJFk9/lGpr7WaomBsVzdYpwcLm9sNrGAABpqYlISoz9Xqun0mhUWFw12ff0RFMffD5/BEdERBQ6DGrEuGGrUz7gNGSmRrQGZFF+VtDBcEuHGU1tpoiNh4iIKJoENgnPnCaooVIpsXJpIZRKBQCgs2eIO+qIaEE4NUsjLZXl9+ZbjjEd+bkZAACvz4/6hp7IDohmzOPx4aM9jfhg50nYHa5IDydmmANKT2UbIrM5dD7k5WTIpbVcbi8aufmUiOIEgxoxrn+8QTgA5OXMf+mpUxUXZGFx1WRDqpZ2E5rbGdggIqKFze8XYLNLCw0pyQmn3YSQlpIY9Dla39DD3bJEFNf8fmZpRIvFlXlITNAAACxDDnT3sgxVLOjqG4ZzzAOP14eGZmZ5zpTZEhDUiLN+GqdaXJUHlUpa/uvsGcLwyOhZ7kFEFP0Y1IhhgaWnVCpl1DS2KinUBy3INLcxsEFERAvbiM0pl4WYLksjUFF+lrxj0Ovz4+jxbpaUIKK41dU7JAdvc4zM0ogkjUYVVFK4oaUfYy5PBEdEM9HbPyKfHhx2YIgL1mfl9wuwDDsAAIkJGqSnxff7TnKSNihgfKyxD4LA3m1EFNsY1Ihhg8Oj8Hh9AACjPg1qdeRKT52qpFCPmsrJ2o3NbSa0dJgjOCIiIqLIGRmv2QycPagBAMtrCuTdskMjo2gN2MVMRBQvTs3SqCxllkakGfVpKMzLBAD4fH7UN/QysB7F7A4XRp3uoMuaWQL6rCxDDgiC9Lo26tOgUCgiPKLwKynUIz0tCQDgGHWhrWswwiMiIjo3DGrEsL6BydJTudnRVwOytMgQFNhoah1AKwMbRES0AAWm+U/XJPxUGo0KK5YUyl+ym9tMQY3GiYjiQVfvENweaZMWszSiR3VFLpISpcD64LADXSxDFbUC1wQmjhmGRkYxOJ6FQNMzBZaeMkRHxYtwUygUWFadL79OWtpNcIy6z3IvIqLoxaBGjBIEASaLVHpKrVbBmBWdH8SlRQZUV0wGNhoZ2CAiogVGFEU5UyNBq0ZKcsKM7peVkYLyEqP8GEeOd8Hr9YdtnERE84lZGtFLKkNVIJ8/2dIP5xjLUEUbURTRZxoBIC1Yn1opgaYniqLcJFylUkKfmRLhEc2f9LQklBbqAQCCIOJ4IzOxiCh2MagRo8yDDnh90sJGtiFNbvoUjcqKDVhUniOfb2wdCPoCQ0REFM8co274xj+zZ5KlEaiy1CiXqxpzeXGssTfk4yMiigRmaUQ3Q1YqivKzAEgBqPqGHi5+RpkRqxNjLqkfjSErFcUFWUhNkebRsNUJyxCzNaYzYnXKZbwNWalQKqN3LSUcKsuykZykBSBl9fT0jUR2QEREc7Sw3r3jSL85oPSUURfBkcxMeYkxKLBxsqUf7V0MbBARUfwbDigbNZN+GoEUCgVWLCmEZrxvVr/Jip4+lgEhotjGLI3YUF2RE7T42dkzFOERUaDegNJT+Tk6KBQKVJQa5cua2gYiMayoZx6cDPZk66Oz4kU4qVRKLFmUL58/2dIvB5iJiGIJgxoxyO8X5BqQGrUKhqzYSJcsLzGiKiCw0dDMwAYREcW/YetkP41M3ew/s5MStVhSPfnl83hTH2sgE1FMY5ZGbFCrVVga8PnT2DowpSk1RYYgCOg3SUENlUoJ4/jifG7AfLLaxuQySzRpYLyMt0KhkH9vC40hKxX5uRkAAK/PjxNNfZEdEBHRHDCoEYNMg3b4/QIA6UtALKVLVkwT2OjoHozgiIiIiMJrosG3SqVEWurM+mmcKi9bh8K8TADS5oYjx7sgCELIxkhENF+mZGmUMUsjmukzU1EyXoOfZaiih2VoVC5HbdSnQT2e0alQKIIyn5rYWyPIqNMtB+Yy0pOg1aojPKLIqanIhVYj/fz9JmtQ83QiolgQO6vhJOsLSDPNy4n+0lOnqigxBn15OdHUx8AGERHFJZfbK9e71qUlndNGhMVVeXKTcbvDhcZWlpUgotgTmKWRm61DWgqzNKLdovIcJCdJnz/DVifau/jdLdImGoQD0saHQIHZTzb7GAbMtvkcWlQLXLjPNqRHcCSRp9Wqg5rLn2jqlXvAERHFgoUblo5RPp8fliHpgzhBq0ZWRmyUnjpVZWk2RBFoaZd2jpxo6oNCoUBxQVaER0ZERBQ659JP41QqlRKrlhZh98EWCIKI9q5B6DNTF2zpBCKKPadmaQTW/6fopVIpsbwmH3sPtwGQejUY9WlITZlb9iGdm1PLURv1qVNuU1majcP1nQCA5nYTsg1pUCgU8zrOaBRYjovHT0B+bgZ6B0ZgGXJgzOVFc7sJNZV5ERmLIAgQBFH6J4rw+wUIogjBL0IQBPjl64Txy0T4J+4jihD80mm1RoXi/CxoNKqI/BxENH8Y1IgxJosdgiCl++Zm62L6wKRqPFtjIrBxvLEXCgVQlM/ABhERxYeRgKBGxjkGNQAgLTUR1RW5cu3juhM92LK+AokJmnN+bCKicOvsYZZGrMrMSEFpkR7tXYMQBBF1Dd3YtKY8pr+PxiqTxXbWctQ5xnSkpyXBZh+D3eHCgMWGXGPsVXkIJY/HJ282SU5KYFBu3JJF+di5vxl+v4D2rkEY9WlITtJOBhgCggcTgQbx1MsFAWJgkOGU09K/gMDEKY8nCGJIy9qNjXmwrKYgZI9HRNGJQY0Y02eaLD2Va4z9dMmqsmxAFNHSYQYAHDvZC4CBDSIiig+BmRoZ6ece1ACAkkI9BoccMA3a4fH6UN/Qg7UrSriwRERRjVkasa+qLAcmiwPOMTestjG0dVpQXsK/43wLLEc90ex5OlVl2Th4tAMA0NxmRo4hfUEfK1iGHPLCeY6BWRoTkpO0qCgxymVN99e2R3ZAIdBnsqKmMlfuNUNE8YlBjRji8fhgGXIAAJISNSHZ8RkNqspzIIpAa+dkYEMBBQrzMyM8MiIiornz+fywO1wApAyLUKbBL6spwK4DLXC5vbAMOdDeNYiyYkPIHp+IZsY8aEdyklbud0On19kzBI+XWRqxTKVSYsXiAuw93AZRFNHcboLRkMa/5TzyeHwwj68JJCZozlja0qhPQ0Z6MkZsTjhGXeg326b031hIBiyTvUWyGdQIUlZsQJ/JKh+3hotCoYBKpYRSoYBSqYBSqYRSqYBKpYAy4DqFcvJ00O0DTqvG76tUKaBUSKe7+4bRb7LC7xcwYLahII9rSkTxjEGNGGKy2OWdBbnG2C49dapFFTkAJgMb9Sd7AICBDSIiilkjtjH5cztUWRoTtFo1li8ukHfTNbYOICsjBbr0pJA+DxGdXkNzH9q7BqFUKrB+ZSkyY7TX3Xxglkb8yNAlo6zIgNZOs1SG6kQPNq0pm7YEEoXegMUmH1vkzaAcdWVZNg4caQcAtLSbkWtcmNkagiBgcHgUAKDVqONmg2ioKBQKrF5WjKa2Afh9ghQ8UAYHGiYuU00EIxRSMEIRGGAYv73ilPuqxu8b7teeWqVE/3h1k94BK4MaRHGOQY0YElh6Ki8n/nZYLKrIgSiKaOuSvvDUn+yBQgF+EBERUUwKLD2VlRH6L8/6zFRUlBjR0mGGKIo4crwbW9aVM9WeaB4MmG1o7xoEAAiCiEN1ndiwpow71k8jMEsjL4dZGrGusswI06AdjlEXbPYxtHZaUFmaHelhLQiBpadmsiZgyEpFpi4Zw9bxbA2TFXk5GWEcYXQaGnHC5/MDkDJYFmJg52ySk7RYuaQo0sM4Jxm6ZCQnJcA55sbgsAPOMQ+Sk7SRHtaCcuxkL/pNVixZlLcg32tofnE7RYxwub0YHJbSTJOTEpCeFp87Masrc1FapJfP1zX0oKdvOIIjIiIimhurbTKooQtxpsaEilKjnAXiHHPj+HgDcSIKnzGXB/UNPUGXeX1+HDzSgTGXJ0Kjil4+nz84S6OEi9+xTqlUYnlNgbww3NJuDnvZGpLee4ZGpGyDlOSZrwlUlk3OuaY2c0gbMscKU0DpKaMhNYIjoXAryMuQT/cOjERsHAuRzT6Grt4heH1+HG/sg9frj/SQKM4xqBEjBsyTH8J52bHfIPxMairzpgQ2evtHIjcgIiKiWRJFUc7USEzQhG2XmFKpxIolhXJ2Rm//CD8zicJIFEUcPd4N7/iO32xDulz2zeX24sCRDng8vkgOMeqcmqWRmsL+I/FAl56E8vFeTqIo4uiJbgiCEOFRxbd+U8CawCwqN+gzU5E1Xh7POeYOyvZYKEwWOwBAqVTAkMmgRjzLD5gbvf0L77UeSd0BG5K9Pj86egYjOBpaCBjUiBH9AaWncuOw9NSpairzUFIYHNjoY5SdiIhihN3hgt8vLe6Eu25zcpIWSxflyeePN/Vh1OkO63MSLVTNbSY5YJmUqMHymgKsXV6C5CRpoX7U6cahuk55/i90Pp9fLtMFMEsj3lSUGpGWKpUSsztcXEAMs6By1LNs+B2YrdHcvrCyNWz2MbjcXgBSgIdlOuNbUqIWhiwpcOUcc2N4PLuJwsvvF6YETDu6BpmtQWHFoEYMGHN55C9PqSmJC6YG7eKqPBQXZAGY2P3DwAYREcWGwH4amfPQjDIvJwMFuVIPKp/Pzx2zRGEwOOxAS4cZgNRUdeWSImg0Kmi1aqxbWYIErdSucMTmxJFjXQtq0fB0mKUR35RKJZYuypfPd/UNRXA08c0x6obNPgZAypJJSZ7dXMrKSIE+c3KhdyFldQ6MZ2kAQLYhLYIjofmSH9DLoWcBvdYjyWSxyVmsSqVUmtDr86O9m9kaFD4MasSAuaaZxoMli/KnBDb6zdwBRERE0W2+gxoAsLgqV94tbrWNoanVNC/PS7QQuD0+HD3eLZ+vKssOysJKTtJi3cpSeQewadCOYyd7532c0SQwS0OhUDBLI05l6JLl3g5W2xistrEIjyg+nUuWxoTKUqN8uqXDvGA2P5gHJ4MaRj2DGvHA5/PD7nChz2RFa4cZ9Q092He4DTt2n8SuAy3IzEiWP4/7zTZmT86D7r4R+fSy6smeSx3dzNag8FFHegB0doEHMLnG+O6nMZ3FVXkQRaCrd0iuY6xerpJTComIiKLNyHhQQ6VSyqU5wk2tVmHV0kLsOdQKQRDR1mWBPiuVn5dE50gURdQ39MA93ivDkJWKsvFeAoHSUhOxelkRDh7tgCCI6O4bRoJWjarynPkeclQIzNLIzU5nlkYcK87PQv3JHgDSdzZdekGERxR/Asu65M4xqJGZkQJDViosQw44xzzo7beiMD8zVEOMSmMuj5zhkp6WhMQETYRHtHD5fH6MubxwON0YG/PAGfjP5YHTKZ0ecwVc7pSuG3UGXz7x2XI6hsxUnLehEmVF0me1yWJDXkD2BoXWqNONwWEHACA5KQH5uRkYtjrR1Ts0vsHBsmCPhSi8GNSIcqPOc0szjQcKhQJLFuVBEET09A9DEEQcru/E+pWlYa9TTkRENFtjLo9cuzkjPVneqTQf0tOSsKg8Bw3N/QCAuhPd2LK+Ui6LQ0Sz1941KO/0TdCqsXxx4WnntT4zFcsXF+LIsS4A0m5orVYd1CtuIfD5/GjrtABglsZCkJudjoaWfvh8fvSZrKiuyIVGw74FoWK1jcE5JvXK0memntPCfGVpNixD0uJjS4cJ+bk6KJXxW8DDPOiQT7P01OzNNBARFIyYYyAilCzDDrz4xiEolAqcv6EK6amJDGqEUWA5u8K8DABAeYlBXr/r6BlCSaEeWn4foRDjKyrKBWdpLKzSU4EUCgWW1eTD6/PDZJHSBw8e7cCGNWULpscIERHFhpGA0lORCL6XFOoxODwK86Adbo8P9Q09WLO8eF6DK0TxYsTqRGPrgHx++eLCswYJ87J18Hh8ONHUBwA40dQHrVY955IxsaijZ0iurc1eGvFPrVYhP0eHzp4hqVmsySqXEKZz12cakU+f6/tIhi4ZRn0azIN2jLm86OkfQVF+/P6tTJbJUt45hoVT9UIQBIy5vHCOeTDqdGPUOfF/8GnHNNdFKhBxJmqVCslJ2mn/JSWOn07WIjlRg7qGXuw60AyVWgmHw4233q/HR3sbce/nz8f1V61BUqI20j9OXBFFUe5bolAoUJAnZX8lJWpRmJeJzp4huRzlogpma1BoMagR5YLTTBfOh/B0FAoFVi0txMGjnRgcdsDr8+PgkQ5sWF2G5CR+MBERUXSIRD+NQNJGgALs2t8Mt8cH86AdHd2DKC2aWi6HiE7P6/Xj6IluueF3RYlxxuXcSgr18Hh8cmPxuhPd0GpUcqPeeObz+dEelKVhPMs9KJxEUYTX64fb64PHI/1ze3xwu33yZe7xy11uH9we7/hl/snberzybaT//VMuy8pIQVmxAYur8tDVO8SgRoiIoiivCSiVCuSEoBx1ZWm2nH3W0m5GQW5GXGZr+Hx+DI2MAgCSEjXzVg70XAmCgFGnZzzg4D5NYGLyvNPpgWMs+Hqn0wMRYkR/jjMFIuRgRPL4+UQtUpKDL0sJCFjMZof/zdcAze0m/PmFPXjv4xNwu30YHfXgV3/agRffPIybr16Hqz+1kmtIIWIZcsgZ6kZ9WtDGj/ISI7r7JrI1BlFaxGwNCi2+mqKY3eHCqFNKM83UJTOiDECpVGL1siLsP9IOq20MLrcXB450YOOaMpbWICKiqDAR1FAoFMhIT4rIGCZK5Bw40g4AaGwdQFZGitzMlYjO7lhjL5xjHgBSKbmK0tktzleV58Dt8clf6A/Xd2Hj6rKYWVibq1OzNBZi+dzZ8PsFdPYMQ21ywuP1y8EFz5SAgxce7/h1gcGJgNu4PdL93O7gy+ZjcXPAYsOBox3ITE/G1o2VqCrLRvYC2hkfLoPDowH9fNJCUtZLl56EbH0aTIN2uNxedPeNxGUQyjLsgCBIr/35ahDu8wnj5cI8cibE2bIlAq93ON0Yc3nmZazTUSmVSElOOGMgIiVZi6QQByJCrbI0G//+zWtw09Xr8ONfvYNjJ3vh9foxYnPit3/5CM++uh83fWYdrr1iFYMb56i7b1g+XZCbEXRdYoJGztbw+wVma1DIcRU4ivUHlJ5i/b9JarUKa5eXYO/htvGDADcOHGnHhlVlrN1KREQR5fX6YXe4AEhNg9XqyH0uGbJSUVZkQFuXBYIgovZYN7asK4/omIhiRVfvkHwsrlGrsHJp4Zx2Mi+tzofH44Np0A6fz48DR9qxcU153C6iMEtj5oZHRvHm+/V49d1aDJisUKlUQIxVCVQpldBq1VAplXA4XdBqVLAMOfDyW7XYub8Fd996Hj596fK4fb3Ph+A1gdCVsKssy4ZpPFujtUPK1lCp4itbw2Sxy6fDGWDz+fz4YNdJPP/aATS29EdsLms1aqQka5GSnCD9S0oIPh90euJ88GVajSquypUurc7HfXdehIbmPnywqxHtnRYoVQpY7WP43d8+loIbV6/DdVesYgB+DtwenzzPErTqafvWnJqtUVKk54ZkChm+kqLYRD8NhSI0aabxRKtVY93KEuw73IYxlxd2hwuH6jqwbmVp3B2MERFR7BixRbb01KmqyrMxODIKm11qMnqiqR/LFxdEelhEUc3ucMn9MABgaU3+nDOmFQoFVi4twv7adozYnHB7fDhwpAOb1pTFZQkGZmmcmSiKaGjux8tv12LH7pPS7yqESRRajRpajRoJWjW0WjUStCokJGhOuWzyn3b8X2KCdL/EBA20WtXkbTVq6f7y7VXyZQlatfy9SxRF7K9tx19f2odd+5shioB50I5fPf0h/vL3Pbj2ilX47BWrI9JnKpYJgoABs9QTQq1WITuE2QbpaUnINqTDZLHB5faiq3corspUiqII8/hiq1qtQlZG6F97LrcXb71fj+deOyD17jiHuZyUoJkScEhOmi4IEXD6lKAFN3hOryA3A0Mjo7jlmnVQq5TYd7gdH+w6CUEUYB914Q/PfILnXj2AGz69Bp+9ajV7ts5Cb/+IXKKzIDdz2oBYYoIGRflZ6OgelLI1Oi2orsyd76FSnIq/I+k4MZGyCAD6zJQFEckURREjVidMFjsGLHaYLDaYJv4flP5Xq1RYv6oUW9ZXYM3yEqxdUYp9h9vg8fowbHXiyLEurFpWFJc1QYmIKPoFNQmPUOmpQEqlEiuXFGLXgRb4/QJ6+odhyEphBijRafj9AmqPdcklS4oLspBrPLfd0SqVEmuWFwdlGR+s68D6laVxlTnl9TJL43TcHh8+2NmAV96pDWo8D0i/qzXLi1BWnC0HCwIDENL/GiRoVZNBiQTNeHAhIDihUUXsO5BCocCG1WXYsLoMr793BM++cgANTf3w+nywj7rw57/vwfOvHsCVly7HTVevPec5tVCYBx1ykDDbkBbyzXtVZdlyI+22TguK8rPiZoPgsNUp/+4MWakhnRt2hwsvv12Ll946BKt9LOi6ksIs5OdmITUl4ZSgxPSnU8dLPcXL7z0a5RjTcbypD36/AIVCge/cfwVuv2kT/vz3vXj/kwYIogCH04U/Pb8LL7xxENdftQY3XLUm7ktFhkJQ6am8jNPerqzYgK7eIQiCiM7eIZQWGxbEGieFH19FUarPNCKfjpeDPpfbC/OgfTxoERCwsEiXmQft8Hh9Z32cN9+vw5vv1yFRq8G6VaVYtbQIKpUSWo0KpkE76hp6sGJxYVylTRIRUWwIahKekRLBkUxKSU7A0kX5OHqiGwBwrLEPuvRklgMhmsaJpj65p11aaiJqQrSbcCLLeO+hNrjcXlhtY6g91oU1y4vjZjNOZ+9klkZ+TgazNAD0m6147d0jeHN7PWyO4MXPtJREXHXpclx92QokJQA6nS4uvr9ctLkGapUKJosNew61obnNBL8gwO314eW3D+PVd47gkq01uPXa9Sgrjp/MgHDoHRiRT+eHYTNCWmoicozpGDDb4Pb40NkzFDd/k4lgDQDkTFMSZy7Mg3a88PpBvPHeUYyNN0aesHF1GW65dj2K81KRkZERF3M5XqjVKuQadejpH4bX54fJYkNRfhb+9YErcfsNm/DXl/fiHztOQBAFjDrd+N8XduPvbxzCZ69cjRs/vYb96E5jxOoM6gF8ps/8xAQNiguy0N4lZWu0dZpRU5k3X0OlOMagRhQSRRH9JulDWKmMjdJTgiBgaMQZlF0xEBCsMFlsU3YxzIYCCugzU2B3uOAeD3y4PF58sq8Jn+xrgiCIMGSlYnFlHmqqcqHVqLG4im+SREQ0fwRBkD/rkhI1SEzQRHhEk/JzM2AesqNvwAqfz4+jx7uxYXVp3CymEoVC38CIvOtQpVJi5ZLQZv8mJWqxdoVUPtXr88My5ED9yV6sWFwYsueIlFOzNMpL4mNhdC5EUcThuk689HYt9hxshSAKQddXlmbjuitW45KtNUjQqiGKIqxW62keLfakpiRAn5kKALjmUytRWqTH9k8a8OZ7dXB5vBBEAe99fBzvfXwcm9dW4LbrNmBpdX6ERx19fD4/zOM9L7QaNfSZ4dkoUVmWLZe4auu0oLggPrI1TBYHAOn9yJB1bkGNzp4hPPvKfrz38Qn4/H75cqVCiYu3VOPma9ehsjQ77uZyPCnIy0BPv/T53jtglTOWC/Mz8e37rsAXrt+Ev760D+/uOAa/IMA55sZfXtyDF988hM9esRo3fmYtdFGQgR1NArM0CvMyz3r7smIjunqH4fcL6OyRyt1F03clik0MakShEasTrvHIvyEzNSpqIzrHPEFZFZNBC+kyy5Aj6AN+tpIStcgxpiNbn4ZsQxqyDenj/0unjVmpUKtVcHt8OHi0Azv3NWPPoVa5drlSqYDJbEd71yDeer8e2cY0nL+xCp+9YjWqyrO5aENERGFnd7jh90uLV9FYN3zpony5vOWIzYmWdjOqynMiPSyiqDDqdONY42QfjSVVeUhNCX2mQVpqIlYvL8aBI+0QBBG9/SNI1GqwqCK252JHz+CCz9IYdbrx7o7jeOXtWnT1DQVdp1apcOGmRbj2ilVYsigv7ndxF+ZnYnBYWlR2uX3457suxu03bMJLbx3GS28dhn3UBQDYfbAFuw+2YHlNIW67bj02rC6L+9/NTA2YbXIZvLyc8GXxpKUkIi9Hh74BKzxeHzq6B1Ee46XjHKNSmT9A2kE+1/WUk839+NvL+/DJvmaIAQ0ztBo1rrx4GW66ei3LecaITF0ykhI1GHN5YRlywOX2Bi2o5+dm4Jtf/RQ+f8NG/O2lfXj7g3r4BQFjLg/++vJevPTWIVxz+SrcfPW6qDzGn28+nx/9Af1+crPPXl0mQatGcX4W2rosEAQRbZ0WbkSmc6YQJ7q60BnZbDbodDpYrVakp4c3c+J4Yy86e6QD4RWLC5GfmxHW5/P7BViGHHJpqIlAxUBAEMPhdM358ZUKJYz6VBgNacjRBwcrJv6fy5dGQRBwvLEPuw60YOf+ZnT3DcPr9WNsbDIVNDFRg/wcHTavq8CWdRVYtbQoLpsyzpeJ3Sfxkh5PtFBxLodHe5cFDc39AIAli/JRXJAV4RFNNWJ1Yu/hNrmp3/pVpfKOWoo9nMuhIQgC9h5ug9UmZVrl5eiwcklRWJ9zwGxD7bEueS7WVObGbJNer9ePj/Y0wuvzQ6FQYOuGygUV1GjvsuDVd47g3Y+OY8zlCbrOmJWGz1y2Ap++dPlpSxLG4zwWBAEf7mqEx+uDQqHARVuq5frpzjEP3txehxdePwjzkD3ofmXFRtx27XpctKU6LrIFzsWBI+2wDEmBoU1rysO6kOoYdWPn/maIogitRo0LNlXFdL+f1g6z3Ltmtu+toijiUF0n/vbyPhyu7wy6LiU5AdddvgqfvXL1tPM5HudyPGlqM6Gl3QQAWFSec8bgXb/Zimde3o+33q8P2rybqNXg6k+txC3XrIuaMrOR0N07jPqTPQCAovysGWfbuT0+fLSnEX6/AKVSgQs2LYrKbA3O5djBoMYMzVdQQxRFfLDzJDxeH5RKBS45ryYkBxRerx+1x7rQOzASlHExYLFhcGh0Slr0bKSlJAZlVuQEBCuyDWnQZ6bMS6ZEZ88Qdu1vxjs7jqG+oRcTmymSkjTy7oykRC02rCrFlvWV2Li6jM2fZolv7kTxgXM5PA7Xd8olHM5bXxm1nzGBX/YTEzTYsq6CAf8YxbkcGg3NfWjvGgQAJCclYMu68nlZ0OvqHcKxk73y+ZVLCmNy129zuwnNbdJCUUFuJpYvLojwiMLP7xew60ALXnmndsrCJyBtTPvslauxZV3FWV9L8TqPG1sG0NppBgBUledMaRzv9fqx/ZMTePaV/ejsDc5syTXqcPM163DFxcsWZDNZt8eHD3edhCiKSE7S4oJNi8L+nEdPdKO3fwTA9H+vWLL3UKvc4+z8jVUzCrIKgoCP9zbhby/vR1PbQNB1WboU3PiZtfjMZSvO+FjxOpfjxajTjY/3NgGQAlTnb6w6631MFjueeWUf3txeJ2cjAkCCRo3PXLYSt1y7bkFuDgqcY5vXVsyqNFfgZ0NxQRaWLIq+8oOcy7GDQY0Zmq+ghmXIgQNH2gEAOcZ0rF5WHJLHfeS/X8NHextnfT+1SgWjXBJKClTkBAQsjPq0qGw0eqC2HW9/WI8TTf1o7TBDq1VDrQ4OrKiUUq3kLesrsHldedw0ZA8nvrkTxQfO5fD4YGcD3B4f1GoVLt1aE7W/W1EUcfBoh7wDNFufhjUrSiI8KpoLzuVzZ7LYcaiuA4BUznTTmvJ5bQoauHNUqVRgzfISGLJiZ4FkoWVpDI+M4s336/HaP47I/Q4mJGo12HbBYlx7+apZle+J13nsHPPgoz3S98/kJC3O31g17c8nCAJ2HWjF317ai4aW/qDrMtKTcf1Va3DN5SuRlhKdGwXCoaN7ECeapHJ4FSXGeSkVOep0S2WWRBEatQoXbFoUFWWwZ8vt8eGDnQ0AgNSURGzdUHnG23s8Pry74ziee+2A3HNhQmFeJm6+eh0uu2DJjDZ/xOtcjidzXYw3D9rxzCv78eb2OnjGe7wCUimyT29bgVuuWQejPjQN6aOdY9SNT/ZJwaGZzLFTeTw+7AjI1jh/YxWSEqNrTZFzOXYsvG0PUa7fNNlYKi8nNIvsn+xrPm1AIyM9eUopqJzxYEW2IQ2ZuuSY7EexblUpkpK0WLdyCG6PD60dZlhtY6g91iXXcPULAg7Vd+BQfQee/OP7qCjJxnnrK7BlfSUqS4188yIiohkbdbrh9khfcjLSk6L6M0ShUGBZTQF27W+Bx+uDadCOzp6hqCyXRRROLrcX9Q098vnqitx5DWgAQFVZNjweH7p6hyAIImqPdWHDqtJ5H8dcLZReGg3NfXjprVrs2H0yaLcuIC18XvOpVfjURUsW1ML72SQnaWHISoVlyAHnmAeWIce0i35KpRJbN1TivPUVOHK8G8+8vA/7xzf5jdic+MMzn+BvL+/DNZ9aieuvWhNTQb+56huYXBPIDdGawNmkJCcgP0dqpuz1+dHRM4jK0ux5ee5QsgQEG7PPsMg86nTj9X8cxQuvH8SQdTTouqqyHHzusxuwdUNlTK6F0OkV5GbKQY2e/uEZBzWM+jQ88MVL8LnPbsCzrxzAa/84Ao/XB4/Xh5feOoQ33juKKy9Zhtuu2xD3wY2eoAbhGbO+v1arRkmBHq2dZrm3RjRma1BsYFAjigiCIJetUKtVMGad+5uhc8yDJ//wvnz+9hs3Y0VNgdR825AW1+m8Sxblwevzo99kxeKqPKjVKnz93m3o6BnEzn0t2HWgBX2mEfn2LR0mtHSY8D8v7Ea2IR2b15Zj6/pKrFhSGNM1RYmIKPwmavED0dkk/FSJCRosqymQd6g3NPchU5cctSWziEJNFEUcPd4t77jM1qehpFAfkbEsWZQHt8cHk8UGn8+Pg0c7sGF1WdQHCLxePzrGy3YpFApUlMZuuZrpeDw+fLDrJF55pxYnT8kgUECBjWvKcN0Vq7F2RTEXPk+jKD9Lzgrs6h0+42KfQqHAqqVFWLW0CE1tJjz7yn7s2N0IQZSa9T776n68+OYhfOrCpbj56nUozM+crx9jXjnHPBixSYuuaamJ8xooKy8xoHdgBKIooqNrECUF+pjL1hiwBAQ1DFNfb8Mjo3jxzcN45d1ajDrdQdetWVaC265bj9XLi6N6cwrNXW52Ok4098HvF9A3YEVNZe6s3r/1mam4766LcOt16/Hcqwfw6ju1cI8HN155pxZvbq/DFRdLwY0cY3h78UaCIAjoHRgBIGWX5s+xZGZpkR6dvUPw+fzo7htGWbEh6rI1KDaw/NQMzUf5qcD091A1KPz1/+zA868fAACsX1mKR//t+gX1AS0IAg7Xd8np4QlatfwlURRFtHcNyo3GT/2yMiElOQEbV5dhy7qKmPiCGU5MwyOKD5zLoVff0IPu8Z1LsdR8O7CXQEpyArasq1jwzVljCefy3AX2gYiG3jJ+v4ADR9rlHaTJSVpsXFMe1RuQAktnFeZlYllNfPTS6Ddb8fq7R/Hm+3Ww2seCrktLScSVlyzDNZ9aGbL+J/E8j0VRxI7djXC5vVAoFLhw8+yawvb2j+C51w7gnQ+PBZV8UUCBCzZV4dZrN2BRRfhLM82nlg4zmsb7Xp2pmbEoirAMOdDUZkJj6wCa20wQBAGLq/KxfHEBFlflzen9I/B4pqI0G1VlsZOt4fcLeH9nA/x+AQlaNS7aUi3Pqb6BETz36gG8Pc1raeuGStx23QZUV+ae0/PH81yOJ4H9Y1YtKzqnMuTDI6N4/vWDePWdWoy5vfLlapUKV1y8FLdetwF52fFT5nzAbJP7SOVm67Bq6dzXLJtaB9DSIfXWmE2z8fnAuRw7GNSYofkIahw53iWnmq5ZXjLtzoLZaG434avf+QsEUYBWo8bv//tO5OdmhGCkseXUL4lJiRpsXFM+5YDaPGjH7gNSBsfh+i74/P4pj6VWqbB6WRG2rKvA5nUVcZ9aeCq+uRPFB87l0PtkXzMcoy4oFApsO39xzAQGBEHA7oOtsDuk0ozR9qWCzoxzeW6GRkaxv7YdoihCoVBgw6pSZGakRHpY8Hr92Hu4DY7xUqnpaUnYsKo0KjOGT+2lcf7GqqjsszdToijicF0nXn6nFrsPtEIQhaDrK0qy8dkrV+Pi86pntSg/0+eO53kcGPya6yL50MgoXnzjEF559wicY8G769cuL8Gt123A6mVFcfH7+3hvk5xBcOHmRUhK1MoBjMbWATS2DqBp/P+J77fTUatUqK7IwbKaAixfXIBl1QUzysZ0jnnw8d4miKIItVqFC2Oot0bgJtGJQGtzu5T18+GuxqB5rVapcNkFS3DLtetQlB+a8pvxPpfjRWAf21D1lbPaxvD8awfw8ju1GHN55MtVSiU+deFSfP76DSELhEfSwaMd8obhdStLg8oBiqKIEasTAxYbnGNe1FTmnvG4wOv1Y8eeRvii8DiCczl2MKgxQ+EOavj9Aj7YdRI+nx8atQoXn1d9TmnMgiDgaw89gxPNUoOxL966FZ+/fmOohhtzvF4/9tW2yYs2qSmJ2LCq9LQ78kadbuyvbceu/S3Yc7h1SmrqhOqKXGxZV4Et6ypQVmyI+zc8vrkTxQfO5dDyev3Y/skJAIAuPQmb11ZEeESz4xh1Y/fBFvj90pf9c921RvOHc3n2PB4fdh1ogWt8R2VlWXZU1Y13ub3Ye6gVYy5pfIasVKxZHn3ljeIlS2PU6cY/PjqOV96uRWfvUNB1KqUSF25ehOuuWI0li/LCNsfifR673F7s2N0IURSRmKDBhZsXzfnndIy68dq7R/D3Nw9OWdCvrsjFrdduwNYNFVE3X2bK7nDhk31NsNrHYLO7oFYp0dRmQlPbmQMYM1VaZMCKmgIsX1yI5YsLTrtB79jJXnSNz4f5alQeChPjnnitvbvjOPbVtgXdJilRi89sW4EbPr0m5BsU430uxwtRFPHRnkaMuaQMsou2VIcsK9JmH8MLrx/Ei28dDgpuKBVKXHbhYnz+sxtRkBebpfOcY268/l4dhkdG4RzzwKhPg8lix4DZJv2z2IKyoNQqFVYuKcSmteXYvK5i2oyVaD2W4FyOHQxqzFC4gxr9Zitq67sAhGYyv/6Po/h/T/0DAFCcn4XfPHZ7RFPqo4HL7cW+w21wjkkfLhnpyVi3suSsu998Pj+OHu/GzvEsDpPFNu3tco06nLe+EudtqMCy6oKY2aU7G3xzJ4oPnMuhFbgzsLRIj5rKvAiPaPa6e4dRf1JqmKxRq7BlfUVc17YddboxODwKURSRY0wP+c7r+cK5PHuHjnbANL7LUJ+ZinUrS6Lud+cYdWPvoVa5IXVejg4rFhdGzTijeXflTHV0D+KVd2rx7o7jQQtPAGDITMWnt63Ap7ctn5dSggthHh+q65S/Q61eVnzOtebdHh/e/fAYnn31QFCPRAAoysvCLdeux7bzF0d9hoEoihiw2NDYYkJz2wD2HW7DieZ+OJ0eJCZqoNWefvxpKYmoKstBVXk2FpXnYNF44KGuoQd1J3pQ19Atl5E6nVyjTsrkGM/mKC7IgkKhwJhLytYQBBEqlRIXbloU9WsJoiji/U8aUNfQjY/3NmPE6gQCppMuLQnXX7UG116+Kmz9wxbCXI4XgaWPqityUVZsCOnj2x0u/P3NQ3jxzUNBG2SVCiUu2VqD22/YFHV9gXw+P8xDDvSbrEGBCum0HV09Q/JaWkKCGgkJs3tPKC3UY9PacmxaU4Gl1XlQKpVRm/XJuRw7GNSYoXAHNQ7Xd8pNwk9N45qt4ZFR3PX1p+FwSlkJj//7zSHpzxEPnGMe7D3UCrdHiiDPdvebKIpo6TBj575m7NzfgpYO07S3S0tJxKa15diyrhLrVpZExRtzKPDNnSg+cC6HVmPLAFo7pS9GsZzlUHusC/0mqQxmpi4ZG1aXxc3rQxAEDFudMA/aYR50BH/BVCpQmJeJ0iJDzH1ecy7PTnuXBQ3NUg81rUaNLesrojagNTwyigNHO+QMqmgKmEbrzsqz8fsF7DrQglffOYJD9R1Trl+xuBDXXbEK562vnNeSXwthHpsH7Th4VPqdG/VpWBuCci+A9Df9aE8j/vby/infy4xZabjhM2vx6UuXR8V7uyiK6Dfb0NQ6gKZWk1xKyuYY79siSgFNQRABhfR9UjH+FTUtJVEOXCyqyEFVeQ5yjelnfb0Mj4yirqEX9Q1SkKO5zTyltFogXVoSltUUYFlNAVLGf2cqlRLlxcao7l3i8/nx2rtH8dRfP4bZYodarURysjT+HEM6br5mHa64eFnY3+8XwlyOF6NONz7e2wRAquKxdUNlWJ7HPurCS28ext/fOCSvzwHjwY3zqvG56zeipFAfluc+ldvjg0kOUkz+67fYMGCyYXB49PTvD4HvTwBSUxOgVE6+xhO1GuQY0+V/ALD3cNtpNwSnpSRi45pybF5bjqzMFLnHSUFuJpYvjvwxBedy7GBQY4bCGdTw+fx4f2cDBEGc0tBqLh792Zt4b7wMxqcuXIrv/PMVoRpqXLA7XNh3uE3e/ZZjTMeqpXOrwdpvtmL3gVbs2t+CI8e74BemfghoNWqsWVaMLeulPhxZUVCzea745k4UHziXQ2vvoVa5LMRFW0Jfb32+eL1+7DrQLJe9ibayPLPl9vhgtthhHrJjcHgUPt/UXlmBFAoF8nJ0KCs2IC0lPLs4Q41zeeZs9jHsOdQqfyFfu6Ik6nujmSx2HK7vxMTXtXDsJp2tWMzSGLE68eb7dXj13SNyLfAJiVoNtl2wGNdevuq0DZnDbSHMY1EU8fHeJnmX7wWbFoX0dSOKIg4c6cDfXt6HI8e7gq5LS0nEtVeswmevWI0MXXLInvNs4+k3WeXARWOLVELKPuo67X38PgGjTg+Sk7WoKDHivPWVqCqXMjFmEsCYCeeYB8cbe3H0RA/qTnSjobk/qFxM0M8giHB7fCjMz0RZkQGfvWoNViwuiKoszjGXB29ur8fzrx9Ad+8w3G7pZ0lK1KCqIge3XrseF2+pnrcg5UKYy/Ek8Ph9y7oKpKclhe25HKNuvPJOLZ5/7UDQ+4ACUvmrL9ywEaVF5/b57hzzTJNhYZMyLyy2cyphl6BRQ6VSQpeehILcTKxbVYocQzpyDGnIzdYhPS1xymteFEW0dVqw51Ar9hxsxfHGPoiYuvysVCigz0zFovIcVFfm4rorViElOWHOYw0FzuXIa++yzGhOMKgxQ+EMavT2j+DoiW4AQHFBFpYsmnuDzkNHO/CtH7wAQDqAe/qJu+ft4C2WnLr7LRS7zCaCJbsOtGDv4bYpqezA5IfW/7n30phZMAnEN3ei+MC5HDqCIOC9j09AEEQkJ2lxwaZFkR7SORkeGcW+KGygPBOiKMJmd8E0aId50A6bfWza2ykUCmSkJ8GoT4PX60dX3/CUgEe2IR0VJUbo0sP3BTcUOJdnxufzY9eBVrnBcFmRAdWVuREe1cwEloYDpGyC/NyMiI0nsGRHtGdpnGzux0tvH8aHu07Km5kmFORm4ppPrcTlFy+N+DH5QpnHLR1mNLUOAAhvn4bjjb145pX92Lm/OejyBI0aV166HDddvTakGZWiKKK3fwRNbabxJt5nD2BMyEhPxqLyHFSVZUOrVUOtVkKXloSVS4rmZZ57PD40tZlQ39CDoye6Ud/QG7Sb3OXywuOR5o5Wq0ZKshZVZdlyT47lNQVhXQg+HZt9DC+9dRgvv10rZ7o4Rt0Q/CKKC7Pw9Xu3YeuGynmfTwtlLseLrt4hHDvZCwAoKdRjcVX4syGdYx68/PZhPP/awcksLUjrRBdsqsIXbtg0bYBdFEU4Rt3oDwhUmCx2DFhscrmombznnI4uLQm52Tpk69OQY0xHbrZuMvPCkI6WDhP6BqRs7pVLCufU9HzE6sS+2jbsPtCK/Ufag9bL3G6fHJQszM/E5RctxeZ15Vi6KH9eMycncC5H1vDIKPYebsMVFy87620Z1JihcAY1Amv7blxdNufFA4/Hh3u/9T9y7cxvfOVTuOqS5SEbZ7wxD0q73yZ27IUyrdbj8aH2WBd2HWjB7gMtsAw7gq7Pz8nAw9+4OuZ2wPLNnSg+cC6HzojViT2HWgEA+bkZWLG4MMIjOneBC09JiRpsWVcZtXXJvV4/LMMOmAftsAw6TrvjVKtRQ5+Vgmx9GgxZaUE/j9frR0fPIDq6BqcsfBqyUlFeYozaLEvO5Zk5crxL/jKuS0/CxtVlMdVIOHBOKhQKrFleHJEsk1OzNC7YVBVVu7YB6Rj8w90n8crbtWho6Q+6TgEFNq4uw7VXrMK6lSVR8xpYKPPY7fHhw10nIYpSdYILNy8K69+gvcuC5147gPc+OhGUTT9R0/7Wa9fPOvNJEAT09lvR1DYgZ2E0tZmCShqeTqZOCmBUluXIpaSM+lQoFAoIgoAPdzXC4/VBpVLOa3ZBIEEQ0N41OF6uqge1x7rQ3jUIiBgviZUAhTL4NVpaqJdLVi1fXBDWEpwmix0vvH4Qb7x3FC6PN2DcIgpyM3D+xiqsXFKETWvLwzaGM1koczleeL1+fLBLqpii1ahx0ZbwvicFco558Nq7R/DcawcwYgvOoDh/QxUWV+VJ2Ram8fJQZtu0m2ZnypCZihxjOrIN6cgNKBMlXZZ2xs/ywN+TRq3CxedVn/Pvyev1o+5EN3YfasXuA63oHRiBw+GCOP5ek5oilbdKTU7E+lWl2Ly2HBtWl4WtH86pOJcj69jJXnT1DjGoEUrhCmp4vVLpKVEUkZigwYWbF8150vzpuV34nxd2AwCWVRfg/z1yc9QcrEervoERHDneLZ8PR1q/IAhoajVh5/5mvPruETmCrtWo8fV7t+Hyi5aG9PnCiW/uRPGBczl02jotODm+cLa0Oh9F+VkRHtG5E0UR+2vbMTQyCkAq07h6WXGERzXJPuqCZVAKZAxbnTjdoWxaaiKy9Wkw6tOgS08662vd5/Oju28Y7V2DcLm9QddlpCejvMSIbEN0lSviXD67nr5h1DVImQ5qtQpb1lVEfbmk6Rxv7EVnzxAAqcb9hlVl855JFJilUZSfhaXVc88uD7UBsw2v/+Mo3th+FNZTsrTSUhJx5SXLcM2nVs5pd2m4LaR5HNi7aeXSIuRlh78H1YDZhr+/cWjKQjgAbF5bgduu2zDta3kigCGXkBoPYExkfJ1Jli5FysCoyJEzMQxZqaf9+wb2HMnN1mHV0ujohymKInbua8In+1vQ0TUI85ADw9bRM97HqE/DippCLKvJx/LFhSgpzDrnNYmO7kE8++r+0waozt9QCcd4YKmqPAcVLCVHMxS46WH1smK5H8R8GXN58No/juK5V/fPuTyUUqGEUR8QtMiWghW5Bul/oz4NWu3smnoH6uwZwvHG8GW0iKKIzp4hvPruEezYfRKdPUNQq1RISgou56tUKLGsJh+b1pRj87oKFOVnhm2ecS5HjiAI+GCnlGHLoEYIhSuoEZhSfi6p8N29w/jSN/8Er88PlVKJ3zx2e8Rr7saKwDdpQAoIFeZnhuW5+kxWfP/x19A4vtsOAD6zbQX++a6Lz+mDZr7wzZ0oPnAuh86huk65Cd55GyojXsYkVFxuL3bua5YzFyIZsPH7BQyNjErZGEMOuSb7qVQqJfSZqVI2hj51zr1NBEFAT/8I2jotU54rLTUR5cUG5GZHx9zhXD4zx6gbuw+2yOVG52sRNRxEUUTtsS4MmKX3G61GjY1ryuat7rTH48NHe5vg8/mhVEq9NCKdpSEIAg4c6cDr7x3F7gOtUxqcVpRk47orV+GS82qiutfRQprHg8MO7K9tBwDoM1OxflXpvD33RMmil946PKVMy/KaQlx/1Rp4vD40tgygqU0KYMxkZ7QhM1XqfVGWjarxDAxDVuqsxhbphdUzcXt8+GhPI/x+AUqlAiuWFKK5zYy6E92oa+hBU6vpjM3H01ISpSyO8UyOqrKcGWd/nq6UmFajxpWXLMNNV69DXrYO+2vbMTheGSGSx2ILaS7HC8uQAweOtAOQSo+uWR6ZTTwutxev/+Monn1lP4ZOCRxq1CpkG6QyUJMZFmnyaWNWGlSq8G1m3nWgRS7pet76yrBlS/h8fny0pwkjNqecAVd7rOu0mXD5ORnYtLYcm9eUY/niwpBmlXMuR06/2Yraeqk3VkwHNX7xi1/gxz/+Mfr7+7Fy5Ur8/Oc/x4YNG856v2eeeQa33XYbrr32Wrz88svy5aIo4uGHH8ZTTz2FkZERnHfeefjVr36FqqqqGY0nXEGNwA/gzWsr5rTjShRFfPs//o5D9dLujluvXY97P39ByMa4EJya1r9yaWHYUmc9Hh9+8fQHeP29o/Jli8pz8L0Hr476L9p8cyeKD5zLofP+Jw3weH3QqFW4ZGtNXP0+B8w2HK7vBAAolQpsXlcxbwsFYy4PzIMOWAbtGBwZlRelT5WclIBsQyoMWWnIykgOaYbqRKPXlg4LHKcsgCUnJaC82ID8XF1Es2I5l0/P7xew51Ar7A7pbxft/R9mYmIRfyKLKjlJiw2ry+ZlwT6asjTMg3a8/cExvPl+nRxUnqBSKnHBpkW47opVWFqdHxPzYqHN44/2NMnZDls3VCE1ZX4bwsrNpV87APOQ/ex3CGDMSkNlWbZcPmpRRc45lyf0+wW8v7MBfr8QstIuoXayuR9tXRYAU3uAOsc8aGjqQ914yaoTjX1TMmICJWjUqKnKw/LFBVixuBCLq/KCsufO1PQ9NXmi6fsquWR3YOWLSPc2W2hzOR6IoogduxvhcnuhUEj9TxMiuNnU7fFh76E2+Hx+OWgR6uPb2bA7XHJQMT0tCVvWVYT1+Vo7zPIG4LwcHZYuykf9yV7sOdiK3Qdb5FL7p0pOSsD6lSXYtLYcG1eXn3MmK+dy5ARuGAx7UMPj8eC9995DQ0MDRkdH8d3vfhcA4HK5YLPZYDAY5jT5nn32Wdxxxx349a9/jY0bN+KJJ57A888/j5MnTyI7+/Q9CNrb27F161aUl5cjKysrKKjxox/9CI8++ij+9Kc/oaysDN/97ndRV1eH48ePIzHx7F/QwxHUCKwrmpyUgAs2zSzAcqrtH5/Af/78TQBAjiEdv3/8zojvnIpFDc19Us1QSIs3a5aXzHqXzWy88+ExPPHUe3L977SURPzb167ChtVlYXvOc8U3d6L4wLkcGo5RNz7Z1wQAyNanYc2KkgiPKPQCS96kpSZi05rysOwGE0URw1YnLIMOmAbtU4IIE5RKBbIyUmDISoVRnzYvu9RFUYTJYkdrpxlWW3BZm8QEDUqL9CjKzwrrLrkzjY1zeXqBr93UlERsXhue1+5883r92FfbJgdr0lITsWFVWVj73kRDlobfL2Dv4Ta8ub0Oew+1TdkZrs9IxWcuW4FPb1sOfWb4jt/DYaHN4/YuCxqapbKNpUV61FSGvznvdCYWw599ZT86eganXG/Up42XjsrBonIpkDHX3pdnElgOOVqDrx6PDzsCsjXO9B7g8/nR1GaSghwnelDf0BPUEPlUSoUSVeXZWFZdgPxcHd7cXo+WDlPQbQyZqbjhM2vxmW0rppQPDPz9RfL1BCy8uRwvGlsG0NopBe1rKnNRWsSKJxNONPWho1t6f1yyKB/FBeHN2p7I1phYIzs186q7dxi7D7Viz8EWHD3eM22WmAIKLFmUh81rK7BpbTlKi/Szno+cy5Ex0RdNEKT2DBdtqT7rfeYc1Hj11Vfx5S9/GWazGaIoQqFQwO+XShTs27cPmzdvxv/+7//ic5/73Kwfe+PGjVi/fj2efPJJANKupKKiIjzwwAP4l3/5l2nv4/f7ccEFF+CLX/wiPv74Y4yMjMhBDVEUkZ+fj2984xv45je/CQCwWq3IycnB008/jVtvvfWsYwpHUKOjexAnmvoAABUlRlSVz75Jtd3hwt3/949y/b0ffOc6bF4b3uhpvBJFEfUNvejpl6K/81GvuLndhEf++zX0DowAkN6Av3DjJtxx46ao26ED8M2dKF5wLodGYAnJSNZwDie/X8Dug61ykOHUHZrnwuPxwTIkBTEGhxxTmnRPSEzQyEEMfWZKRJqnThgcdqC1wyJn2U7QatQoLsxCSYF+Xpuqcy5PLzB1XaVSYtPa8rgpDQdIZSr2HmrFmEvaDZ2VkRLWxteRzNLoN1vx1vv1eOeDY1N21SugwPpVpbjq0uXYvLY8ou8N52KhzePARQuNWoWLtlRHNOAoCAJ2HWjFoboO6DNTpTJSZdlhCWBM59DRDpgGpdf2htVl55z5ES5zfR8QBAGdPUOob+iVS1YNnJJhdTqFeZm45dr12LZ18WlLNQeW7lq/qjSiQc2FNpfjReAmpbTURJy3vjLCI4oOgb0NlEoFLt5SMy/HuIH9Cs/UY8g+6sKB2nbsOdSGvYdap5QVnJBr1GHjmjJsXluBlUsKZ1T2nXM5MgJbA8y0PcOc8qp27tyJG2+8EXl5efjpT3+KPXv24G9/+5t8/YYNG1BZWYm///3vsw5qeDweHDx4EP/6r/8qX6ZUKrFt2zbs3r37tPf7/ve/j+zsbNxzzz34+OOPg65ra2tDf38/tm3bJl+m0+mwceNG7N69e9qghtvthts9WbvNZpM+eEVRPG1Dytma+PAFgNzs9Dk97u/++jGGR6SAxvkbq7BpTXnIxrcQLa3Og9frg2nQDr9fwIGj7diwugypYdoJWlFixC8f/Rwe++U72LW/BSJE/O/zu3G8sRf/9sBV894A8mwmXv98jRHFNs7l0AhslpmRnhSXv0+lUoGVSwqw60Cr3MgvKzMFOYa5bfCw2cdgHrTDPOSYkvUQSJeeBKM+DcasVKSnBX8WRvL3nJWRgqyMFFhtY2jtMMsLUR6vD81tJrR1WlCcn4WSIv28lC/gXJ7KOeZB/XhjcEDadZmanBBXv6MErRprVpRg3+E2eL1+DI2M4ujxbqxYUhjyL98erw/t47s0FQoFyor1Yf9d+nx+7DnUijfeq8OBIx1Tns+gT8WVFy/D5RcvQ25A74FY/RsvtHms0aiQY0xH34AVXp8f/SYr8nMzIjYehUKB89ZX4Lz1wRsD5+Pv4fH6YB7/HEnQqqP6WKK4MAvt3YPw+wV09w2jtEg/JWtiOgqFAiWFepQU6vHpbcsBACaLHXUN3ag7IZWs6ugKzpSprsjFrdetx3nrK+Rg7XS/F0EQ5N+fWq2M+O9voc3leJGSrEV6WhJs9jHYHS7Y7GNh6xsRS/pNVnnDUY4xHWq1cl5e20X5mWjrtMDj9aHfZIW9xIDUaTampCYn4KIt1bhoSzX8fgHHm/qw+0AL9h5uC3pP6TdZ8crbtXjl7VokJmqwbsV4mao1ZcjUTR9E5lyOjIkN5oBUfmwm5vRt6z/+4z+QkZGBgwcPwmAwYHBwarrmunXrsHfv3lk/tsVigd/vR05OcNZCTk4OGhoapr3PJ598gt///veora2d9vr+/n75MU59zInrTvXoo4/ikUcemXK51WoNyQvb5faip0/a6ZCSnAC/zw2rdfoGOKdzsmUAr75TC1EUkZSowe03rIXVaj37HemMSgvTMWK1ydkvO3Yew7qVxWGtV/zgvReivCgT//v3fRAEEfsOteJL33ga375vGxaVn77k2nwTRREOh7Q7lRFrotjFuRwa3b1mOMc8UCoVgOCB1eqL9JDCpjA3BQ3NUo3bvQebsGl1KRISzn4Y6fMJUlmpIQcGh0fh9kz/O1KrVdBnJEOflQp9Zgq04zvBRMEDq/XsjVojobxYh2x9Ijp6hjBgtsvHh/U2O443diI/R4figiwkJYbv+IFzOZggiDhU1wXreEPLHEMa0pKVcXt8XFmcgcP13fALAlpGR+HxuEJ+3NjcbobNJi0aFuZlwOMeg8d9+oDkueg32/CPjxqw/ZNGjIwfh09QKhVYu6IYn7qgBmuWF43v7Bfj4m+7EOexLlWN5lZpY0BDUxdSkhbGz32qnv4ROEal34M+I0veSBmt9Dot2sYXC48ea8PiqrPvop1OggZYtzwf65bnA1gP+6gbJ5r60dM3gopSA5bXSP1w7PYz9zwZGnHCapVuk2NMP+vtw20hzuV4kZ6iQl//5HtSNK3BREpDczdGR6XP4vSUrHn9vNVnaNHcLj1fbV0bli8+e2ZYcV4qiq9eiVuuXok+kw0Hj3Zi/5EO1Df0yb35Rkf92LH7JHbsPgmFQoGqMiPWrSzG+pUlKC3Kkuct5/L8c4550Nsn9W5KTUmA4HcDOPsm7zkFNfbu3Ysbb7wRBsPpa80VFRXhlVdemcvDz4rdbsftt9+Op5566ozjma1//dd/xYMPPiift9lsKCoqgk6nC0n5qaFOC1JSpKhgZVk2dLrZNYj2+wX8/plX5Z0L93zufJSXRq5hX7w5f1Ma9tW2y/WKG9uGsWF1WVh3Xd516wVYvbwMP3jiDQxbnRi2juG7P34D9911Ma6+bEVUvJlOLNgwDY8otnEunzuP1weFUoOUFA106UnIysqM9JDCSqfTwe1RyJkJ7T02rF9VOu3rZ9Tplpp8D9kxNOKUX29qTQLUmsnMx9TkBCkbw5CKjPTkmHwt6nRAQX42nGMetHVa0NM/Iv+8wzYvRmwDyM3RobzEGJasT87lYI0tA/AJSqSkpCApUYMNayrmtRzYfNPpdEhKTsHhuk6IAIasHlhGPCErhefx+jBk7UFKSgoUCgWWLykNeS8Nr9ePnfub8eb2Ohyq65QvV6mkv1uOMR1XXrIMl1+0FEZ9WkifO1osxHms0+nQ3eeAw+mG1w8oVQkLcmf0ydYheU1gUUXBlMzEaLNscSqGbB74fAJsDh/UmsSQ9LbS6YDC/NkvIveanPLvr6I0b9ZrKqG2EOdyvEhOTkXPwChEUYTd6UdaWlpUlgOfL84xDzxeBVJSUpCcpEVp8fz2qlmamobBEQ88Xh9GXcKsPyN0Oh1qqorw+RvOw6jTjUN1nXIWR2CmeEvHIFo6BvHsq4dh0Kdi05pyqUzV0kL5cTiX54d5yCS/n1eV58z4/XxOK7Rut/usC/sjIyNzehMwGAxQqVQYGBgIunxgYAC5uVN3ArS0tKC9vR1XX321fJkgSFE4tVqNkydPyvcbGBhAXt7kZBwYGMCqVaumHUdCQgISEqZ+QCsUipC8qPvNk7sw8rJnP1FeertWqmmpACpKsvHZK9dwsoWQRqPGupWl2HuoDc4xN5xjHhyq68T6laVh/XK8alkxfv3Y7fiP//c66k/2wOcX8LPfb8fxxj58/d5Lo6IB/MQc4OuNKLZxLp+bEevkAXGWLmVB/B6XLy7Ezv3NcLm9GLY60dY1iIoSIwRBwNCIE5YhO0wWB5xj02eeqlRK6DNSYNCnwahPjYrPtFBJSU7AspoCVJZlo6NrEJ29Q/D7BYiQyo32DViRY0xHebEx5KUlOZclliEH2rqkHV4KhQKrlhbPqG5yrMs2pGNZTQHqxktuNbeZkKjVoDD/3AOtHV1DEARpka4oPxPJSaELzHX3DuON7UfxzofH5MwajL+EVUoltqyrwKe3rcDaFcULYmFpIc7j4kK9XDu7u284ZP2aYsWYyyNXBkhJToAuPTnCIzo7rVaN0iIDmttMEAG0dQ5i+eLINTa3DE3upDZkpUXF/FmIczkeaLVq5BjTpZJLXj8sQ6PIMYaml24s6g0olV+Qlznvr2e1WoXyEgMamqXKOi0dZqxeVjynx0pNScQFmxbhgk2LIAgCTjT1Y++hVuw+2Co3iAek95PX3zuK1987igStBmuWFeDu285HRQmzduZDn2nyNZefmzHj19ycjvTLy8uxf//+M95m9+7dqKmpmfVja7VarF27Ftu3b8d1110HQApSbN++Hffff/+U29fU1KCuri7osoceegh2ux0//elPUVRUBI1Gg9zcXGzfvl0OYthsNuzduxdf/epXZz3GczXqdMM2fvCenpY0690NJosdf3p2JwCpQd6D/7Qtos3V4lWCVo31q0qw91AbXG4vbPYxHK7vxNoVJWH9fRuyUvHfD9+E3/75Y/z9zYMAgPc+Pi41Ff/GNSH5kkpEROcmsDRKhi76FyJCQaNRYcWSQuyvbYcoimhuM8FqdWJwZFRO6z5VcpJWbvKdlZES98criQkaVFfmorzEiI6eQXR0Dcr1iAfMNgyYbTBkpaK8xBi1zWBjkdvjQ92Jbvn8ovKcqOtLFk4FeZlwe3xobJU2hR1r7IVWq0a2Ye6ZDR6PDx09UpkZpVKB8hBkf3g8Pny0twlvvHcURwP+XhPyczLw6UuX41MXLeX8WADysnU42dIPv19A74AVi8pzYrbZ+1z0mwI2Oc6wdng0KCnQy59tvQMjKC8xhCRbY7bsDhecY1J5Sn1mSlxn5dH8KMjNQP/4wmrvwMiCDWqIooiePqm3gUKhQEGEeh4V5WehvWsQLrcXA2Yb7A7XOWf0KZVKLK3Ox9LqfHzxtq3oN1ux91Ab9hxsxeH6TvmY3e324pN9Ldh9sB0XbanGHTdtRkmhPhQ/Fk1jeGRUfj83ZKXOqvT/nIIaN9xwA37wgx/gj3/8I+6+++4p1//kJz9BfX09Hnvssbk8PB588EHceeedWLduHTZs2IAnnngCo6Oj8nPdcccdKCgowKOPPorExEQsW7Ys6P4ZGRkAEHT517/+dfzgBz9AVVUVysrK8N3vfhf5+fly4GQ+nZqlMVu/ePoDjLm9AIBrPrUSNZXzmwq2kCQlarFuZSn2HW6TUvBHRnHkWBdWLSsK664xtVqF++66CEur8/DjX72LMZcH7V0WfOVf/oxv33cFLthUFbbnJiKisxtegEENQGqUXV5iREu7CaIoyuWoJigUCmTqkmHUp8GgT0XaNI39FgKNRoXK0myUFurR1TuMjm7pSxkg7QSzDDmQqUtGWbHxnBaeSfryXXeiW+7XYtSnobRo4X3xLC8xSk29uwYhiiKOHO/C+pWlc35/au8alIOVhXmZ59Rbrr3Lgje21+EfO47DPuoKuk6tUuH8jVW46tLlWLW0cEFkZZBEo1EhL1uH7r5h+Hx+9JtsC2rzVuCu1LmsCUSKRqNCabEBTa0DEEURLR1mrFhcOO/jCDz+iNfSdDS/DFmpSNCq4fb4YB60w+PxLYiMz1NZhhzyMatxlgvMoaRSKVFWbMCJpj4AQFObCWuWzy1b43RyjTpce/kqXHv5Krk6y56Drdh9oAWDw9J7zIe7T2LH7kZcurUGt9+4eUF9Ts2XwMyg/JyMWd13TjP0W9/6Fv7+97/jS1/6Ev7617/C7ZbKDHz729/G7t27sWvXLqxatWrazIqZuOWWW2A2m/G9730P/f39WLVqFd5++2250XdnZ+esD3i//e1vY3R0FF/+8pcxMjKCrVu34u2330Zi4vx/2e4L+IPlZs8u+rvrQAs+2dcEAMjUJePu284L6dhoqtSUBKxdUYL9R9rh8/lhGrSj/mQvltcUhD0N78LN1SgrNuKR/34V7d2DGHN58Mjjr+Lmq9fhS587P+53vBIRRSO/X4DNIWVcJiclhLXfUjSqLDViaNghB3YStGoYsqQghiEzlbslA6jVKpQVG1BSmIWe/hG0dVrknUjDVieG6zqQlpqI8hIjco3pLFcxB62dFrkESWKCBsvm4fgsWlVX5MLt8aFvwAq/X8DBox3YuKYcqSmz20UdiiwNl9uLHbsb8cZ7R3FsvMRQoKK8LFy1bTk+dcGSBRUYpmBF+VnoHt8R3Nk7tGAWixyjk5UbdOmzr9wQaSUFWWjvtMDr86NvwIryYuOs32fOlckyuVGUQQ0KBYVCgfycDLR1WSAIIvpM1gW5O3/iPRmQMkEjqSg/E22dFrjcXpgsNtjsY2HrPZScpMXWDZXYuqESLrcXz72yB6++W49hmxMiRLz3yQm8v/Mktl2wGLffsAn5EcpgiTeCIMgZUiqVctabvRTiRDejWRoeHsb999+P5557Dn6/f/IBFQrcfPPN+OUvf4nMzPg5KLHZbNDpdLBarefUKNw+6sLOfc0ApKDExjXlM77vmMuDLz74J/kD/P/72qdxydbZl/iiuRkcduDg0Q65tnBpkX7esmScYx78v9/+A+/vbJAvW7G4EA99/dPQZ6bOyxgAaTek1WplwySiGMe5fG6GR0ax93AbAKAgNzOi9aQjxe8XYLLYkJyUgPS0RL6OZkgURfSbrGjpsMBxyo715KQElBcbkJ+rm/HmnYU+l4dHRrFvvBwaAKxfVTqvx0XRSBAEHDzaicFhKdCTlKjBxjXls9pp2dgyINeZLinUY3HVzI93m9tNeHN7Hd77+ARGncH9dbQaNS7YtAhXXboMKxYXLsjX7HQW+jzedaBFXuDfvLZiQZSOa2ozoaXdBACoqcxFaZEhwiOavdYOs1zyLi9Hh5VLiubtuV1uLz7cdRIAkJaaiPPWV87bc5/JQp/L8SBwvS49LQlb1lVEeETzy+Px4cPdJyEIIhK0aly4eVHEMyg7e4bk/kvZ+jSsWVES9uecmMvahCS89u5RPPPKfnlDGwAoFUpccfFSfP6Gjcg1xk6mXTQaMNtwuL4TwNw+S+a8tTAzMxN/+ctf8LOf/Qz79+/H0NAQ0tPTsX79ejmjgqbqD8rSmN2L/39f2CMHNNYuL8HF51WHdGx0ZvrMVKxcUoTaY10QRRHtXYNQq6XyEuGWnKTFv33tKiytzsev/rQDPr8fR0904yvf/jMe+r+fnteDSCKihS6w9FTmAt1hrFIpkTfL9GCSNv/k5WQgN1sHk8WO1k4zrDbpS5JzzI36kz1objehtEiPovwsZmSegdcrHQtNBDQqSrMXfEADkOpFr15WhH217bDZxzDm8uLAkQ5sXF02oywq9ylZGmXFZ19sdY558OGuk3jjvaNoaOmfcn1poR6f3rYCl12w5JzrYVP8Kc7PQv1JqdF9V+8QdOnxv1Gg7xzWBKJFcYFU797jlbLDykuM81Zy0jLokE9nM0uDQigtJRHpaUmw2cdgs4/BPupaUKVUewdG5E28+TkZEQ9oAEBhXgZaO8xStsagHVbb2LwFv5MStbjl2vW4+lMr8dJbh/H8awdgH3VBEAW8+X4d3t1xHFdesgyf++xGlpOdo57+Efl0Qe7sEyPOuV6CXq/HFVdcca4Ps2D0jTcEUygUszqAae0w44XXpabRWo0aX7vnUkb/IyDHmI5l1fmoa5AOvJvbTNBq1CguyAr7cysUClx3xWpUleXgP/7f6zAP2TFkHcU3H3kB937hfNz0mbV8TRARzYOF2CScQkuhUCDHmI4cYzoGhx1o7bDIO+tdbi8amvvR2mFBSZEexflZLOk1jbqGHoy5pJrPmbpkVJaeeyPreKFWq7B2RQn2HmqFc8wDx6gLh+o6sG5l6VkDZe2dFrmXRlF+1hkzPBpbBvDG9qPY/kkDxlyeoOsSNGpcdF41Pn3pCixZlMdjVDqt3Ox0NLT0w+fzo89kRXVFbly/51ltY3COSVlM+szI1as/VxPlFU+OBzJb2s1YtXR+NtoFlp7KNizMZs4UPgW5GXL2WG/fCKorcyM8ovnT3Tcin4506akJSqUSFaVGHDspZWs0t5uwdh6yNQIlJ2nx+es34trLV+HFNw/hhTcOYtTphs/vx2v/OIK3P6jHVZcux+c+uxGGLG6wmSmv1w/LkNS7JEGrhj4zZdaPEfmw2wISeACTlZEy4xrcgiDgiafeg1+QvmDcdt2GBVNvNBoV5GWiumLyg+14Yy/6Bkbm7fmXVufj1z/6AtYsk97IBVHAb/53Bx7579empPkTEVFoiaIoZ2poNep5ryFN8UefmYr1q0qxaU150OKMx+tDU+sAduxpRGPLgNwIm6RSBBOLWhq1CiuWsJTRqRK0aqxdUSJ/3xi2OnHk+GRmy3TcHh86e4cAnD5LwzHqxqvv1OKfvv2/+Oq//hmvv3c0KKBRUZKNr91zKZ777Vfw7fuuwNLqfP5t6IzUahXyc6TNfn6/ENRAOx71mUbk07HUIHw6xQVZ8ntMv8kKu8N1lnucO79fgGV4so9SetrC2UVP8yMvWwelUvrc6h0YOePnZjwZsTrl0qiZuuSo+o5TkJuBpEQpAGwetAdtMJtPqSkJuOOmzfjLk1/CF27YhKRELQDA6/PjlXdqcfsDv8cvn/4QwyOjERlfrOk3W+XMoLycuZXtm1OmxiWXXDKj2ykUCmzfvn0uTxGX+gMO0GZzAPP2B8fkBnuFeZm49dr1IR8bzU5ZsQE+nx8tHVK94aMneqBWq+atSVmGLhk/euh6PP3sLvzlpb0AgI/3NaGty4J//8Y1MyoVQEREs+dwuuH1Sb3EmKVBoZShS8aa5cWwj7rQ2mFGv8kGURTh8/nR2mlGe7cFhXmZKCs2yF+iFiK7w4WG5j75/LKaggX9+ziTlOQEKWPjcJvcB+d4Yx+WVudPe/vTZWmIoogTTX144706fLjrJFweb9D9khK1uOS8Gnz60uVYVJHDIAbNWlF+Fjp7pIBaZ8/QvGTBR4IoinLpKaVSytiLZSqVEmXFBjQ0S9kaze0mrF5WHNbntAw55EUwoz6N7zcUclqtGkZ9GgbMNrg9PliGHAuiGX1Qg/A5lAEKJ6VSifKSyWyNlg7zvGdrBEpLTcTdt5yHG65ag+dePYCX3joMl8cLj9eHv795EK//4wiuu3I1br56Hb8vnkHvOZaeAuYY1Pjwww/PeL1CoYAoivyACSCKorzrRKFQzLje2vDIKH7754/k8//nS9ugnWGGB4VXVXkOPF4/unqHIIoiao91Yd2KEmRmzD5lai6USiW+eNtWLF6Uh//6+dtwOF3o7hvG/f/2V/zff7oM285fPC/jICJaSEbYT4PCLC0lESuXFKGy1I32rkH09A9DEEQIgojOniF09Q4jPycDZcUGpCQvrMV8n8+P2mNd8oJWSaE+5hcFwy09LQmrlxXj4NEOiKKIrt4hJCSop/SEmy5Lw+5w4b2PT+CN7XVoG28cHqi6IhefvnQ5Lj6vBslJC+u1SKGVlpqITF0yhsd3Cg+PjM7bd6r5NDQyKmfdGbLS4qLMVlG+1FvD5fZiwGyDzT6G9LTw1bs3Wezyadawp3DJz8nAgFnKCO3pH4n7oIbfL6B//OdVq1XIzY6+Y6uCXKm3xpjLK2drRDpgkJ6WhC99/nzc8Ok1eO61A3jl7Vq4vT64vT48++p+vPpOLa6/ag1uunode4qdYtTplqsfpKYkzvn3M6fyU4IgTPtvZGQE77//PjZu3Igbb7wRHo/n7A+2QIxYnXC5pV1NhqzUGQcmfvPnj2AfTwHbdv4SrFke3p0PNDtLFuXJvVH8fgGH6jrnJe020Oa1Ffj1jz4vfzl1ebx49Odv4me/2w4PS1UE8fn8aG43BR0MExHNRmCT8Ix5alJHC1NKcgKWVufjgk2LUFZkkHshiKKInv5hfLKvCbX1XRi2OhdMaYQTTf1yqc30tCRUV+REeESxwZCVihWLJ5svN7eZ0DUewJgwkaUhiiLGXF78v9++h5v/6Td48o/vBwU0kpMScO2nVuE3j92OXz76eXx62woGNCgkivInszO6AnYMx5PABuF5ObFdemrCRLbGhOb2qQHQUBFFEeZBu/y8c6m/TjQTRn0qtBppzc5kscHr9Ud4ROHVb7LCN56JnmtMh1odfQFXpVKJipLJDRnN7aYIjiZYZkYK/un2C/HnX3wJ11+1Rn7tjLm9+MtLe3HbfU/h6ed2yWu7hKBSk/nn8HkY0p4a6enpuOiii/DOO+9g3759+OEPfxjKh49pE1FPYOalpw7Xd+IfHx0HAKQmJ+Irt18QlrHR3CkUCqxYXCA3A/L6/DhwpH3ee1vk5WTgZz+4DVdevFy+7JV3a/F/H35W3mFAwPGmPjS3mXCorkM+ICYimo0Rq9Q4UKlUQMegBs2DxAQNqitzceGmRagozYYm4IvmgMWGQ3Vd+HBXI4439mJoZDRuAxy9/SPo6ZcWOtVqFVYuKYRSyfaAM5WXk4GaysCecH3yMaLb48OJ5j7s3N+Mn//hffz0qffw3sfH4fFObo5Zuigf3/rq5XjuN1/G17506ZRMD6JzlWNMl9/f+k3/P3v3Hd9Wff2P/3U1LVmyhmUNb8dOYjubDCCMBAijjAYoeyakFEqhUChtgbLa8gtdlG/5lEIpCWWvMlvKJgRIgAyyHSfeS7aGtbd07++Pa19biZ3YiW0Nn+fjkUekq6urt2Vf6d573uccT9ZNzmJZVtjnJBIxjFk087ukUCeUq7M5vPB4Q+PyOm5PUPhcytep6DuAjBuRSIRCM3/djmW5rO/1M7j0VHGaNAgfSqFZI0ykcPT60653hV6bi5+sOAXP/vU6LD9zLiRi/jstFI7iudc34qqfPI3n//019cIF0NU9KKhh1h7xdhhunM58fvzjH+P9999Hc3PzeGx+wnm9Xmg0Gng8HuTljS4Vi+M4rNtQj0g0DpGIwaknVB828hmNxvGjO59Du5WfRfWz60/HuafPPuLxk/GVSLDYtK0Fbi8/g1epkGHRvArh4G4ivffpTjz29KfCAV+eSoF7bj0bC+aUH/W2OY6Dx+OBRnNkTXxSKRSOYv3X+4WLPTKpBIsXVqbkd0RIqmXyvpxKkWgcn321FwBfeurYY6akeERkMorHE2jvcqGl3YFINI5AIIDc3IHZqnKZBKaCPJgK8qDX5mbFPh4IRrBxS5Mwi3B2TfFRnQAdLavNgy3bW7F5ewt21HUgFI5BLGIAhoFYJALD8LOIRQwDkYgBI2L6bosg6rstFo9kfQYipu85fcsYEQOxWAQG/HMYhhl4bfEw6w96DZvDB5cnwG9fLMKUEgP2NnZj4+ZGJBIcZDIxcvqacapzc3D6klqcfeos6tc2jug7ecDeBita2p0AgOoqM8pLsufvrsfuxXe72gDwF3Bm1xSneERjq72rV6h3X5CvHpd69/sae9DUlzk2q7oIRWl28ZX25ezi84fx1aYGAIAmT4Hj51emeETjwx+I4Mtv9wPgs4RPOnZqikd0aJ1WF3bu7QTABzcXzi0f89cYq325x+7Fi29+g/99ugsJlhWWq3NzcOn3F2L5WXMnZbar2xPE11ubABz973DcmjOIRCJYrdbDrzgJDK6dWZCvHlEq16vvbhYCGjOmFeLs02aO6xjJ0RGLRUIjRn8gjGAois3bW3HsvIoJr5V69qmzMLXciAf+/C667R54/SH86qE3cO0li3HlhYsm7YyW1g5n0uzVaCyOXXs7MX92GR10EkJGxOUZmA2k01DJA5IaEokYFaUGlBXr0W3zoKG5C6EIhO+4SDSOts5etHX2QiYdCHDk6zIzwMGyLLbv6RACGkVm3YQHNPyBCLbtbsfWHa3YvKNVyBjJSBwQCseEUhqfMQDXtxwMHxSbXVOMc5bNxknHToWcevmRCVRs0QtBjbZOV1YFNbp63MLtQpM2ZeMYLxNR777HMVCBINt7HJDUU6v4Ov8+fxgebwj+QASqXHmqhzXmBjdrTucsjX6FZi0aWx0IhiJwuvzodQegT9MeTKaCPPzsR6fjsvMX4fnXv8aHn+8By7HwBcL450tf4LX/bMZlyxfi+2fOnVSTbZO/D4+uFOO4HKU2NTXhtddeQ3l5+XhsPuMMrp1pHkHpqU6rCy+88Q0AQMSIcOv1yybthehMIpWKsWBOGb79rhnBUBT+QBhbdrRi4dxyoQ72RJk6xYQnfn8VHn7sf/j6uyZw4PDMq19hz74u3P3Tsyddk6JYLIEOqxsAXzJGJpUgHInB0etHc5sDU8oKUjtAQkhGGNwkPNWN6QgRiUSwmLRQ5jDIzVXB0euH1eaFo9cnNNKOxuJo7+pFexcf4DAa1DAbNdBrlRlzbLmvqQdeH1/KJFcpR81U82GecfQSCRb1jd3YvL0VW3a0Ys8+K1iOHXJddW4OCvLVYFkWHAckWFZo7M5yHDjhdvJylu17jOP6ellg2NcYUwygyJGC4zjE4/zrAoBCIcWyk2qw4tITUFqkP/Q2CBknqlw58nUqOF1+4YJVvk6V6mEdtXg8IZS+lUklWdkLor/e/a56fgZ1Q4ttTCoF9AsEI0LJFp1GOeIepYQcjSKzFnsbugHwF/+nZVkvL5ZlhYkaDMOkNAt2pBiGQVV5AXbUdQDg+4QtmleR4lEdmsWowZ03nYkrLliE5/79NT75Yi9YjoXHF8KTz6/Hq+9uxuXnH4tzT5+d9ZNJWJYVrpGLxSKYCo6uKf0RvVvXXXfdkMvj8Tg6Ozvx5ZdfIhaL4Te/+c1RDS4bDK6dKRaLDls7k+M4/HVQ6aCLzj0GlXTBNWPkyKWYP5sPbESicbi9QXy3qw3HzCqd8IsHalUOfvvL5XjprU145pUNYDkW325rxg2/eA4P3PH9rPtCPpS2rl5hhmexRQdTQR42bWsBAOxvtkGvzaULlISQw6Im4SRdSSRiWExaWExa/uJZrx/dNg8cvX4kEvyF8mgsjg6rCx1WF6QSMYyGPJiNfAZHugY4euxeYda2SMRg7oyScWteae1xY8uONmze3oKtu9qGrXcsFokwY3ohFswpx/zZpZg2xTRm7x/HceD6Ah6Dgx8sO3g5i0Tf/URfIIVNHBBQSQqasEgkDl4/Fk9g974u+PxhPvunJB+nnVSb9SfTJP0VF+rgdPkBAO1drqwIavTYvUKw2WLK3tJEhWYNmtrsCIaiQr173RjNoB7cD5GyNMhEsZi0qG/sAcdx6OpxY+oUY1btv3anX6gqYzSoM+YYwGLSoKHFjmAogl53IK2zNQYrsujwq5u/hysuOBbPvb4Rn31VDw4cXJ4gHv/XZ3j1nU24/IJFOPvUWVkbuLU7/Yj1XZszGkZWyehQjuhdeuaZZw75+PTp03HHHXfghz/84ZFsPqs4egNJv7DDzdhft6Eem3e0AOC/rK++6PjxHiIZY7lKORbMKce33zUjFk/A0evHjrpOzKktnvAvQJFIhCsvPBY1VWb87v/9Fx5fCD0OL35670u45bpTcfZps7LqS3koLMuirYO/IMIwDMqK85GrlKOyrACNrXZwHIcddR04fn7lhJcKI4RkjkSChdcXBsB/zmfrgSbJfBKJGBajBhajBokEC3uvD902L+xOnxDgiMUT6Ox2obN7IMBhKsiDQZ8+AY5QOIpdfTWTAaC6yjKmmab9JaW2bG/B5h2tSanwByqx6DF/Thnmzy7D3Bkl41b/mGEYMAyDifoVzJtZim+3tcAfCGPqFFPGXMwg2c3Ud2EtEo2jx+5FJBrP+L/NwU2GLSOo3JCp+GyNAqHefUOLHQvnjs2FRptjIKhhNBzdzF5CRkouk6AgXw2bw4twJAanKwCDPvMDrf06M6RB+IEyMVtjsNIiPe659RxceeGxePa1jfj8630AAIfLj8fWfIqX396Eqy48DmcunZF116jGuhTjER0dDNf8WyQSQavVQq2myHm/bvvgAxjtIdf1ByJ4/Jl1wv2frjptUjaNyQZqVQ6OmVWKzTtakUiw6LZ5IJWIMWN6YUrGc8zsMjzx+6vx27/8B3v2dyEWT+CRf3yE3fu68NNVp2V1/b6ubk/S7INcJV8Hs6rCiF53AC5PEMFQFLv3dWHujJJUDpUQksY8vpDQs0BHmV0kQ4jFIpgLNDAXDAQ4euxe2J1+IYNxcIBDIhHDaFDDZMhDQb4qZQEOjuOwY0/HoIlBeUddEimRYLG3oRubt7dgy45W1O3vPmRJqfmz+SDGMbNLYS7IzouQMpkEixdMQSgcE46PCEk1kUiEIrMOTW385KMOqyujKxdEonE4XXxPLqVClvXZ4eNR7z4ajQvZskqFPCv7GpD0VWTWwtbXz6Wz25U1QY1wJAZ7L58VlyOXZtzPZTFp0NhqRyDIZ2tkYrnC8hID7rv9PDS22vGvVzcIjentTh/+8tRHeOmtb3HVD47D6SfXjFum8kSKxQZKMcplkmH/5jiOg8cbGtH35REFNcrKyo7kaZNOIsEKMwqkEjEM+kN/ma95+Uv09jUhXbygCosXVI77GMn40WlzMXdGCbbubAPHcX31rMWYOiU1ZZ+MBjUeeeASPPHc53jr/e8AAB+s242GZjvuv/1cFGVQZH6kOI5Dc7tDuD+ldOCEiGEYzK4txoZNjYjFE+i2edChU6G4MPveB0LI0RtceoqCGiQTDQ5wsCwLu9OPbrsnKcARjyfQ1e1GV7cbEokYBfkqmAs0MOhVE9ofrKHFLuxzihwpZlUXHdF2rD1ubN7ON/f+boQlpRbMLsPUKca0yVgZbyKRiAIaJO0UF/JBDYCfSTyl1JCx2eXdNo8wKSKbszT6jccMakevX3gPTQaaQEsmVkG+CjKpBNEYnz0WiyWyYvZ8V7db2K+KzNqM+4zt/6zZvmfgsybTghr9KssK8Js7l2NfYw/+9doGfL21CQA/Sf5PT3yAF9/8BldfdBxOO7Fmwvv1jqVuuyepFKM/EBFK43Z2u9HZ93+H1YVAMIJPXr3jsNvM7DzONGfv9QkniaaCvEOeHNU3dOOdD7YDABRyKW5eecqEjJGMr4J8NWbXFAkftI2tdkilYpSXGFIyHqlUjFuuOxUzphXiz098iHA0hsZWG3581wv45U/OwgkLq1IyrvFic/iECxh6bS40B9TAV+TIMKO6ENt2tQMA6hqs0GqUNPuHEHIQahJOsolIxDfmMxXkgWVZOHoDfIDD4ROyI+LxBKw9Hlh7PBCLRSjIV8NszEOB/vDlVI+G0+VHY4sNQN8EhJriEV888Aci+G5XG7bs4Bt8H6qkVGmhHsfMLsOCOWWYUzt+JaUIIaOnVMhQkK+G3ekT+jNkah+F/oaoAGA2ZX9QAzi43v3RzqDu6ZslD/AT9QiZSCKRCBaTBq0dTrAsh267ByWFR5c9mg46rG7hdqZOcDUb+c+aQDAClycIR68/4zJOBptWacJDv7oAexus+NerG/HtNr5KUlePG7//2/t48Y1vcc3Fx2Pp4mkZM/nGH4gIwYovvtmH9i4XnK4A4vEEguHoUW9/REGNZ5999ohf4Jprrjni52a6pAOYQ8zKSCRYPPKPj8CBj1hdc8nio+4AT9KHxaRFLM5iz74uAEB9Yw8K8tUpnRV36onVmFJmwAN/ehft1l4EghHc98e3cfnyRVh52QkZHf0drGVQlsZwgSRzgQYlhQG0d/UikWCxfU87jp8/JWO+JAgh44/jOGHWuFwmoVnNJKuIRCIYDWoYDWqwLAunK4Bumxc2h1cIcPSX0uy2DQQ4TAV5MOaPbYAjEo1jZ91AH42qCuMhm8weUUmpOWWYP6uMjrUJSXPFFp1QpqK9y5WRQY1gKAq3lz9+UKtyoM4du75A6YxhGEytGJsZ1P3fSwAgk0poYglJiSKzFq19fTq7ut0ZH9TodQcQDPGTP/N1qoyd2MEwDKoqjNi+m5+k2tBsy+igRr/qKgtW330hdu3txL9e3Yitu1oBAO3WXjz01//ihTe+xjUXH4+Tjp2aFtetgqHoQZkW/bf7vwNZloPfz//NicTMsBOJRQx/XjLSHi8jCmqsWLFi1KlIHMeBYZhJG9SIxwdqhcmkEuTrhj8he+v9bWjom5E2pbQAF35v3oSMkUyc0iI9QqEomtsd4DgOTa0OzKo5slIKY6W8xIDHH74Sf3z8A6z/hm9M9NLb36KuwYpf33rOIS8iZAK3JyhchFTl5qAgf/gvt+oqM1yeIPyBMHz+MPY2dKN2Wmr6nxBC0o8/EBEyL+lkmmQzkYgPWBTkq8GyFjhdAfTYvXy5hWECHAa9CuaCPBTkq4+q3i/Hcdi1txPhSAwAYNCrMKX04AkJXd1ubNnBl5TaurNNOCk/kEQsRu00y6QsKUVINjAa1MiRS/m6704fwpFYxvUBnCwNwocyVjOoe91B4RjMkK/KuBI5JDvkqRVQ5ebAHwjD5QkiEIxk9CSnjqQG4drUDWQMmAvy0Nj3u3F7g7A7fRkZBB/KzOoi/PG+i7B9TzueeWWDUNavpcOJ3/zlP6goLcC1Fx+PExdVjftnYzgS6wtc8MGLwSWj+tsoHEoslhBuSyViFOTzgYsisxZFFh2K+/6ZC/Igk428qNSI1ly7du2IN0h4dqdPqBVmNuYN+wdmd/qw9pWvAAAMGNx2/bKsaABDDjalrAAdVhdi8QS6etyYUmZI+RehUiHDfbefi3//dyv+8fx6JFgW23a348ZfPo97f3YuZh5hDet00NQ2kKVRUZIPhmHAcRxC4Rhy5JKkCxtisQhzZhRj4+ZGsCyHts5e5OtUNIuTEAKA+mmQyWlwgKN2mgW97iC6bR7YHD5EY3EAfICjP+ghEjEw6NWwGI8swNHS7kxqHjirphgMw8AXCOO7ne3YurMVm7e3wmpzD7uN0kI95vcFMWbXFmfszENCCD8Dt8iiQ2OLra8/oQtTK4ypHtaodHW7hduWSVJ6qt9YzaC2UekpkiaKzFrUN3YD4PftVPVKPVqxWAI9dn6/kkrEMBoy+5oH/1lTIJQUb2ixZU1Qo9+c2hI88sAl+G5nG555dQN291WBaW6z44E/v4OqciNWXLoYxx0z5aiCG9FoHF09biFY0WHlsy66ut2w9/pGvT2DToVCsxbFFh38wQjUuXLk61RYftZcaPPG5px6REGNa6+9dkxebDIZXHrqULMyHn9mHUJ9dcTOWTYLM6bT7PBsJZWKUVaSj4ZmW9pkawD8l8BF587H9EozfvuX/8Dp9sPh8uP2B17FDVcvwYVnz0v7GTGJBAuXJwiXOwCnK4CuHje+/a4ZvkAY4XAM//14B3rdQfS6A4jG4tCoFVgwuxwL5/EXPnTaXKhzc1Az1YLd9fwXxK69nchT50CRQxdECJnsXINmn+g0mZ3FRsiREIn4jAyDXgWO49DrHihRFYnyAQ6W5WBz8MtEIgYGnQpmowYF+erD9sTweEPY19TTtx0WMqkEL7/17WFLSuWpFHxJqb5/dMGLkOxSUqhDU6sdHMeh0+pCVXlB2p+X9PP5w0JvP51GOSnPKcZiBrXNwV9I6/9eISRVCs1a7Gvq4T+Put2oqjBmzOfRYN12DxIJ/rjKYtJkRelxkyEPalUOfP4wPN5QVmVr9GMYBsfMLsO8WaXYvL0Vz7zyFfb2BdkaWmz49e/fwvRKM1ZcshgL55YP+7cZiyXQbff0lYgaCF50drtgc/iEtggjpdMo+UwLsw5FFj6AUWjWosisFb733J6g0Pxcr80ds4AGQI3Cx0UsloDD5QcA5Milw5aq+GZrk1D2R5unxA+vOGnCxkhSo6woH63tzrTK1ug3q6YIT/zhKvzu0f9i+552JFgWj//rM+zZ14U7bjwjJbMdg6EoH6hwB4SARa8rgF5P3//uIHrdfrg9oaQP33A4hmiUT2/LyZEclL7m8YXwyVd1+OSrOgDA1AoTFs0tx4K55TDoVXD0+hGLJ7CzrvOQXwiEkMmhv0m4WCyCWpUen9mEsCwLry8MtzcEMDL+pJThM39FIgYMwwcjGKDvPr+M///Iv9cYhkG+ToV8nQq10yxwefgMjh77AQEOpw82p0+4EGUqyIPRkHdQgCMWS+CTL+uwa28nGppt6OrxDHtCJRGLMWN6IRbMKcOC2eWoqiigklKEZLEcuRQF+WrYHF6EIzHYHL6MyaROmuRo0qZuICl0tDOovb6QUJIwX6eiihYkpeQyCQr0Ktj6yuE5XYGM7N+QXHoqMxuEH4hhGFSWZ3e2Rj+GYbBwbjkWzCnDN1ub8cyrG7C/mZ8YVN/YjbtWv4EZ0wpxxQXHQiRi0NFXKqqr240Oqxs9du+wk4WGk6dSCMEKvkzUQPBiJNczu3rcwu3CMf4+pKDGOOixew9beiocieGvT38q3L/xmiVQqyZH47DJTCoVo7zUgP19Ef50ydbop9fm4o/3XoQ1L3+Jl9/eBABYt7EeTa123H/HeSgrzj/q12BZFh5vqC9QEYTT5ef/Hxy4cPP/+rOYRoNjgWhfvT6GAaRS/mMuT6WAXquEWqVAQ4stadv7m3uwv7kHL7z5DRQ5MpgMapSXGjC1wgi9LhdV5ZmV6k4IGTvhSAyhMH9CrVEr6AIqGRcsy8Lnj8DjC8HrC8HT98/rDcHbN+ss6TFvCP5ABBzHIZFIQCwWA6OIUzDgAxv9wQ+GYSBiGDB9wQ/RoACIECBhRBCJ+BdhRP3rDDwO8JmTkUgckVgcHMslBVIAPriSI5ciVyGHUimDSMSgodmGbhtfBkEsFiFXKUv6WcqK8jF/ThkWzC7DrBoqKUXIZFNSqBNKEHVYXRkR1OA4TiiVxzAMzBkw5vFy4Axqm8M34qw6m3Og3Em2XqAkmcVi1gp/l1097owLavTvhwDfJyRPrUjxiMbO0XzWZCKGYXDc/Ck49pgKfLWpEf96dQOa2uwAgN37unDP798c1fZUyhw+06Iv46KoL3hRZNYd1bVqlmWF43yRiIHZOLbfh0cc1Ghvb8fvfvc7fPzxx+jq6kI0evDFR4ZhEI/Hj2qAmajbPrj0lHbIdV749zfCevNmlmLZSTUTMTSSBkoL9Whpc6RltgbAX1C4/sqTUTO1EL//2/sIhiJo6+rFzXe/iNtvOAPHzDQP+bxoNA6nm8+gcLoCcHn6/ncfnF0x2sjwcCRiMXRaJfK1udBpc5Gvy0U8ziIciUGVK0d1lRnzZ5dDm6dIytaIxxPYva8Lm7e14tttzWhosQmPhcJRNLbYsaOuE+D42q2nnliNpYunY1Z10aiaFhFCMh/10yCjxbIs/IH+AEVYCEIMDkj0Byj6H/f5w6NO9z4aHDhwHAc2cfh1j/AFkEiwiMUTiMVZcOwQPxsDiEUMEgn+MYYBFAop8tQDJaUWzCmjC1mETHIGvQpKhQzBUBR2pw/BUDTtg5tuT1CYEGHQqyb1+QPDMKgqN+K7XW0A+BnUIw5qOAaCGtl8cZJkDpNBDalEjFic70sRn5rIqAyizu7sy9LodzSfNZmMYRicuKgKixdMwZffNuBfr25AS4dzyHUVOTKhITefdcEHLYotOuSpc8alQond6Rd68RkNeWO+vxzRt2tTUxOOPfZYuFwuzJgxA5FIBGVlZcjJyUFTUxNisRjmzJkDrVY7poPNBJFoHE4XX3tbqZBBk3dw5LOl3YFX3uFnwUslYtz6w9OovM0kku7ZGv1OXFSF8uIr8eAj76KpzY5QJIaH/t9/cdKxU2Aq0AnZFP2BCn8wPGavnauUJwUqdBr+f70uN2m5WpX8wRuPJ/D5xn2IxRMQiRicfNw05MilB21fIhFjTm0J5tSWYNUVJ6LXHcDm7S3YtK0Fm7e3wusPQS6TIBKJw+bw4ZV3NuO/H+9EjlyCuTNLsHBuBRbOKUdxYXYdCBBCDuYeFNQYrpwkyV4syyIQjAqBiMEBCq8/BI83PChAEeoLUETGLHg/lFylHBq1Ahq1AqpcORKJOGQyGTiOA8fxM4RZlr/NcqyQPcxyHLj+5SwLDuhbr39dDhzL9j3O9QU+Btbpfz76nsdybNLjwjb61mM5Djl992OxBKKxBGKxhDAePvDBQSRmUFaUj1NPnI5TT6ihklKEkCT9DcP39/Xd6ehyYVplejfotdpG1l9zsjAV5CFPrRC+K3vs3sNm3IQjMXh9AzPKhzqnI2SiiUQiWEwatHX2IpHgZ6BnyjUBlmXRaXUD4GfMZ+Nn05F81mQLkUiEk4+bhhMXVeHzjfuwdWcbNGpFX8YFn3Wh1Sgn/NrzeJaeAo4wqPHggw/C4/Hgk08+wZIlSyASibBy5Urcd999sFqt+PGPf4w9e/bg448/Huvxpr0euxccx5+sDfUhwbIs/vLUx0iw/MnuZecvQkmhfkLHSFKvrCi9szX6FRfq8NhDl+PRpz7GR+v3AADWbdg/6jIXAF+2QqdRQq/LhV6rhF6rSrrdH7TQaZRHfNDaYXUhFuennVqM2hFvR6/NxRlLZuCMJTPAsiz2NfXg2++a8b9Pd6Ox1QaO5RCKRMGIgG++a8Y33zUD4D+UF84tx8K55ZhTW5L2s8YIIaM3OFNjLJuakdSKRuPY19SDDqsrKWOi/5/PFxYyLcYzQKFU9AcocqDJU0Ct4oMVmjz+/zy1Apq8HCGIoVblJM1w4jgOHo8HGo0mIybIcBwHV1+T8W67B8FQFCIRg4rSAtRMtaR6eISQNFVs0aGh2dbXoNeV1sHPwaU2xGLRpJgpPBJV5UZs3dkKYGAG9aG+tyhLg6SrIrMObZ29APjMh0wJavQ4fMK1ElPBwT3OssVoP2uyjUgkwiknVOOUE6pTPRTEYgnY+8q1yaQSGPS5Y/4aRxTU+Pjjj3H22WdjyZIlwjLhQr7FgldeeQWzZs3C3XffjSeffHJsRpohrIOiUOYhghofrNuDXXs7AfAfhlecv2iihkbSiESSGdkaAN+g75c/OQszpxfh/9Z+ikQiuVaFQi6FXqc6qAzUgdkVmrzxrUXPsixaB6XZlZceWf8PkUiE6ioLqqssuOT7C/Hx+jrU7beioYVvXhoMRYR1u3rcePuDbXj7g22QSsSYVV2MhXPLsHBuBcpL8ifVlych2SgeT8Dn57PQ1KqcrD34nwzsTh/27OvC7nor9uzrwv5mG+KJsa29pMiRHRyQSLqfk7RMnTv5/qYYhoFep4Jep0Lt9EJ4fSFEY4mMq0lNCJlYcpkEpoI8dNs8iETj6HH40naWsdMVEEptFOSrM6o0zXgyGtTQ5Cng8fIlF3scXpgLhv8d2gf10zAZJsdMa5IZNHkK5CrlCAQjcHmCCAQjaTlB9UCdWdggfCij/awh42dwv2mLSTMu1wOPKKjhcDhQXT0Q9ZFIJAgGB2YyyuVynH766XjrrbeOeoCZJBSOCjM6Vbk5BzVT8XhD+Mfz64X7t/7wtEldX3Oyy5RsDYC/CHHu6bOxaF45tmxvhMVsgEHHZ1qkS3ZCj90r1K4tyFdDnXvkzYz65cilWDSvAhKJCLNqisAwfAbInn1d+HZbC3bt7RSyrmLxBLbuasXWXa148vn1KNCrsWBOORbNK8e8WaVjMh5CyMRye0PCpA3K0sgcsVgCja02IYCxe19X0sWRkRgcoMhT8ZkSef3BCRWfVXFggIKO6UYvmxpUEkLGV0mhDt19ZZ06ulxpG9QYXGojXceYKlMrTNi8vQUA0NBsh8mQN+QksHg8AafLDwBQ5EiPqkktIeOhyKzFvr6SeF09HkytMKZ4RIcWCkfh6OX3KaVCBr127GfMp5OqciO27OCzNRpbhv+sIeNrvEtPAUcY1DAYDAgEAkn3W1pakjcskcDtdh/N2DJOf5opgCE7uv/j+fXw+vm6kKeeUI35s8smbGwk/RyYrdHYasfsmuJUD+uQCvLVOH5+RVqWuWhqcwi3K0oNY7Zdo0GNsuJ8tHY4wXGAxxfCxectwGXnL0IwFMV3u9qwaRvfj6PbPlA/197rw/8+24n/fbYTIkaE2mkWvlTVnHJMnWJM25R5QsgAt3dgwoZeS0GNdOVyB7B730AAY19jjzBLdjilhXrUTitEVYUROo2yr8zTQACDAhSEEJJe8nUqKBVyBEMROF1++AMRqHLTa0JYIsEKZZOkEjEK8ikLbTCDXgWdRgmXJwh/IIxuu3fIwI/D5Rdm9xbkU+kpkn4KzVrs7yuJ19XtQlV5QdpdHxmso6+XBgAUWXRpPdaxUJCvhjZPCbc3CJ9/+M8aMn6CoSh63XzcIFcpH7Lf9Fg4ojO2qVOnorGxUbi/aNEifPDBB2hqasKUKVNgt9vx+uuvo7KycswGmgkGX9A8cIfZvqcd76/bBYD/hf742qUTOTSSpgZna1h7PKgsK0jbbI105uj1CyViNHmKMZ95ML3ShF53AD5/GIFgBHX7rZhZXQSlQoYTFlbhhIVV4DgOHVYXvv2uBZu3t2Db7nbhohrLsdhV34ld9Z1Y+8pX0OYpsWB2GRbMLceC2WXQZflMicFisQRaOpxgGExIWTJCjsbgJuEaytRIC4kEi6Y2B/bs68Ke+i7s3meF1eY+5HMUcimqp1pQO9WCGdMLUTutkGZ9EkJIBiot0mFvQzcAoMPai+qq9OrFY3N4kUjwWdymgjw6xh1CZblxULaGDeaCg2dQJ/fToNJTJP3kyKXI1+XC0etHKByDyxNM2+yH/sALwFfgKDJrUzugCVJVMfBZ09hiH/Kzhowfq23g+njhOP7NjTioEYlEIJfzF1u/973v4f7774fb7YZWq8Vtt92Gd999F7Nnz0ZNTQ0aGhrg9XrxwAMPjNe4004gGIHHy2dh5KkVSRemY7EE/t9Tnwj3f3j5iWn7gUcmViZma6SjlvaBLI3ykrHL0ugnEokwp7YEG7c0IpFg0WF1IV+vSgpeMgyDkkI9Sgr1+ME5xyASjWPHno6+LI5mtHX1Cuu6vUF8/GUdPv6yDgAwbYoJC+eUY9G8CtRMtUAszs4ToEg0js3bW4QAVAP4fUDf10A+X6+iMl0kbXAcB3ff93qOXJo2pfYmG68vhD2DsjDqG7oRisQO+RyLUdsXvOCDGBUlhqz9XCWEkMmk0MSXfGFZDp1WN6ZWmNLq893aMzEXcTLZ4GyNQDCCbpsHlkFlSTiOg70vqCGRiClTlqStIrNWKOnUaXWn7TU+pysglOk26FXIkUtTPKKJYdCrhGyNQ2WGkfHR1e0Wbheaxu99H3FQw2Kx4IorrsB1112Hm266CUuXLoVYzDe9Wrp0KV5++WU88MAD2LVrF8rKyvC73/0O119//bgNPN102wdKTx24o7z2n81o7eQbGFdXmnHu6bMndGwkvVG2xtHx+cNJ9SHNBeMzm0eVK0ftVAt27u0EAOyu74JGrRj2QqdcJuHLTc0tB7AUVpsHW7a34tttzdi6sw2hcFRYd19TD/Y19eCFN79BrlKO+bPKsHAeX6oqW1Kug6EoNm9vTWq0DvA1c21OH2x99e5z5FLodblCz5bJctBF0o/PH0Y8zjeS1mrohHoisCyLts5e7K7vwp59Vuyu70K7tfeQz5FJJZheaRYCGLVTLZMq+40QQiYTmUwCs1GDrm43YvEEeuzetAkeRKNx2PvOSXLkUujo2GFYU6eY8O13zQCA/c12mI0DpY1dniBifcdfBr2Ksl1I2jIa8iCViBGLJ9Bt96BmqhkSiTjVwzpIx6AG4UUWbeoGkgKDszWGywwjY8/jDSEQ5K/76LW5UOSM3+TAEQc1wuEwHn/8cfz973/HnDlzsGrVKkyfPl14/OKLL8bFF188LoPMBINnZQzup2HtceO5178GAIgYEX52w+n0xUySULbG0Wlqswu3y0sM4/olVWTRweHyw9rjQTyewI49HVg0r3xE+7TFqMG5p8/GuafPRiyWwJ79Xdj0XQu+3daCxlabsF4gGMH6b/Zh/Tf7hJ9pUV8vjpnVRSmp886yLOJxFvEEi0SCRTyeQDzB34/HE8JjA7cTSCRYxPru+/xh1O23IhyJgWU5iMUMLCYt4vEEIpE4GIaBTCaGXCaBVCpBrzuAtg4nxGIRVLk5MOhV0Gtzka/LTcsDRZKdXINKT9GFifERCEawt6Ebu/Z2om6/FXv2W4UD4OEU5Ksxc3oRaqZaMHN6IaaUFUAqpc8FQgiZLEosOmEGaHtXb9oENXocXnAc3wfCYky//oPphD+uV8Hp8iMYiqCr240iiw4AhCwNADBmyeQukp3EYhHMRg3au3qRSLDosXuFv+N0EY3GYXPwE7BlUsmk26cOlxlGxsdENAjvN+KrYz09PXjxxRexZs0abNq0CT/96U9x55134vzzz8eqVatw2mmnjec405ovEIY/wJdT0eYphSgUx3H469OfCnX1L/jePFSVG1M2TpK+KFvjyITCUXTbBr6kJ6I+5IxphfB4QwiGonB7g2hssWPqFNOotiGVijGntgRzakvwwytPgtPlx6ZtLdi8vZUvz9T3eQLwpbVa2h149d3NyJFJMXdmCWbXFEMsFiUHF2JDBRr6/u9bjw9IDFp+wOPDbYfl2CN+vxJxFsFQFH3neBCJGeQqZGBEAyd6bIIbNCYW6FuXEfG/V7lMAplUAplMAlUu32RKl6eEJk8BpVIOZY4UOTlSKHJkyJFLocjh/+XkSKHMkfHLcwaWU2CZjAQFNcYWx/GlQnbv6+orJWVFS5sDXP8OPwSJWIypFUbUTivEjOkW1E4rzJrsNUIIIUdGp82FKjcH/kAYLg/fBDYd+iQNnuRoGcdSG9miqrwAThef2dLY6oDFpIFIJEJPX1CDYRj6zidpr8isRXtfmemuHk/aBTW6etxgWf5Yu9CsmZTnwVUVRmza1gIAaGhJzgwjY49lWeH7UCRiYBqnSir9RhzUUKvVuOGGG3DDDTdgz549WLNmDV544QW8/PLLeOWVV1BaWorrrrsOK1asQElJyXiOOe10D3MA88U3+/HtNj6tskCvxopLF0/42EhmoGyNI9Pa4RRmRJUW6yekpq5Ewgckvt7aJPyu9Dp+ttGRytepcNYpM3HWKTPBsiz2NnT39eJowd6GbuGiXzgaw9dbm/D11qax+nHGVbwvoNF/zVIsFkGplOHAYwiRmIFMLIFMBoDjGwHH+wIwkWgckUh86BdgAIlYBIlEBIlYDJF4ZAcnMqmkL8AhEwIfOfL+IMhAgEQIjsilUChkQl+FHLkUGrUCRRYtHRBlsf4m4WKxKC0ulmSaUDiK+sYePoDRV07K6w8d8jk6jRIzphdhxjQ+gDFtiikl2WmEEELSW2mRHnv2dQHgszVqpxWmdDyhcBS97gAAIFcpR55akdLxZAKdNhcGvQqO3v5sDQ+0GqVQqlanUVImJkl7Wo0SSoUcwVCkL/MomlZ9+DoH9TVIt4DLRMnX8VUfet0BBIIRWHs8aZPhl40cvQFhYr/RkDfun+NHdKZYW1uLP/3pT/j973+Pd999F2vWrMH777+P+++/Hw8++CBOP/10rFq1CsuXL4dUmv310K22gX4a/VGoQDCCv639TFj+k5WnpNWHG0k/lK0xOrFYAu1dfH1IsViE0kL9hL22Jk+BaVNMqG/sBgDs2NOBxQurIB+Di28ikQi10wpRO60Q116yGF5fCFt2tOLbvobjg2ePH/VrMXxAQCwWQSoRQyIWQSwZfFsMaV/AQFinb30+mDD0cqlEDF8gjB67FyKRCGIxA61GianlRkilEkgkIsTjLELhKELhGMLhGILhKMLhGMKRmLA8FI4hEIzA5w/DFwgjEIwiER+UNcKhL9OEBRAHI+JndvePRSQaOuAQjcURjcXh8R36Auvh1FRZ8MMrT8LcGZMrkD8ZhMJRhPuaUWvzlBS8OgyO49Bt92JPfVdfJoYVjS32Q2Z5iRgRKssL+F4Y0wpRO72Q6twSQggZEYtRg/rGbiQSLLp6PJg2xZTSEqXdg64HUJbGyFWVG4XeiE1t9qSLrkYDZWmQzFBk0WJ/Uw8APjMiXaqzeLwh+PwDFWXUuZN3klZVhVHo49PQYofFRNka42UiS08BRxjU6CcWi3H++efj/PPPR09PD5599lmsXbsWH3zwAT788EPk5+fDZrMdfkMZzOsLCbMJ8nUqoant2lc2wNGXTnncMVNw4qKqlI2RZAbK1hidtr7alQCf9jnRs3nLS/LhdPnh6PUjEo1j195OHDOrdMy/HPPUCpxyQjVOOaEaLMuisdWBtg4nRCIGkr5gQv+FfOH/vqBE0u1BQYj+x8Yr/bS5zYH6xm7UTLUA4E/uZlUXHfXrcRwHXyCMTqsbVpsHPTYP/IEIorEEH6iIxhGNJRCJ8rf5TA4xRCIGDIBINI5wJIZgOIZQKIpINIZQKIZwNHZE46lrsOKOB1/FgtnlWHX5iZhWOboyZCR9uQcFD6lJ+MFC4Sj2N9uwd78Vu/dZsae+C72ewCGfo87N4Rt5TyvEjOmFmF5pGtemcYQQQrKXVCqGxahBh9WFeDyBbpsXxYWpm4VstQ2q3GCkoMZIaTVKFOSrYXf6EAxF0dgycO2ISk+RTFFo0gwENbrTJ6gxuEF48STN0uin1+YK2RoH9vEhYycWSyT1cDHoc8f9NcfsKqDJZMKdd96Js846CzfddBO++uorOJ3Osdp82hqqQfi+xh68/f42AIBcKsHN151KUUAyIgdma0wpLYAql7I1DsSyLNo6+M8XhmFQXmKY8DEwDINZNcXYsKkBkWgcdqcPrR3OcR2LSCTC1Aojplakx4HSUPY19iQ1by8rzkd1lXlMPgMZhkGeSoG8qQohYOILhOHs9aPXFUCvJ4h4PDHsczVqBfJ1ucjXq6DNUwhBFpZlEY7E+QyRviBHKBQTZusHQ/z/oVAMoQifWbJlRwta+v4GN+9oweYdLVh6/HSsvPSElJ5Uk7FB/TQGxOMJNLc7Ud/Qjb0N3djb2I3WduchszAYMCgrycfM6YV9Db2LqFwbIYSQMVVapBcu2rV19abs+MsfiMDbl/2ryVNQpv0oVZUbYXfyfTT6a/+rcnPofSQZQ5EjG1RKLQqXOwCddvwv5h5KIsEKwVa+ofn49jXIBIOzNRpbHSg007nJWOuxe4XP8f4+SeNtTIIaPp8PL774Ip5++mls2bIFHMchNzcXl1xyyVhsPm3x5Rb4DwqGYWAy5IFlWTz6z4+Fk/1rL1lMszXIiB2YrdHURtkaQ+nq9iAS5ev0mQryUlbaTS6TYFZNMTZvbwEA7GvqgV6bOynr6HIch931XUkzQqoqjOM+U0WdmwN1bg7KSwxgWRYebwgOVwC9Lj/c3pDQc4XjOLi9Qb65e6sdYrFImLFh0KugypXzf0cjPB9mWRaffLkXz7yyQfgeWLexHuu/3o+zT5uJq35wHM0wy2D9QQ2GYaDNmzz7c38z7/pGPnhR39CN/c02oS7qcJQKOWqnWvhSUtMLUV1loYA8IYSQcZWnViBPrYDXF4LXF4LHG4ImBd/ZlKVxdDR5Chjz1bD1BTYAwEjH0CTDFJm1Qim1zm53yoMaPXavMNnPXKBJaXm+dKHX8n1Q+d4nlK0xHia69BRwlEGNzz77DGvWrMGbb76JUIi/eHTcccdh1apVuPTSS6FSHXnj3Ezg9gQRCvNlSwx6FWQyCd56/zuhzn55cT5+cM4xqRwiyUBlRXq0tjsRjcUpW2MIfLDHIdyvSEGWxmAGvQoVJQY0tzvAshy27e7A4gVTJtWBA8uy2La7Q0g1BIDaaYUoLZq4PicAn8mi0+byB5EVRsTjCfS6g+h1++HoDcAfCAvrJhIs7E4f7E4f6hv59Mh8fS7ytSrk63MPWxZHJBLh9JNrseS4afjvJzvx/L+/htsbBMux+M/HO/Dh53twwffm4fLzF1GT6QwTiyWE+rOqXHlW78u97gCffbHfivrGHtQ3dsM3aD8ZiogRoaLUgJqpZkyvNKNmqgVlxfoJmYlDCCGEDFZaqMeu+k4AfMNwTV7RhI8huXIDBTWORFWFMTmoQf00SIYxGvIgkYj5cnh2L2qmWiAWp+7YOLn0lDZl40g3VeUFcPa1CWhstU9YNsFkEApH0evmyxErFfIJm2Qw6qBGR0cH1q5di2eeeQYtLS3gOA4FBQW48cYbsWrVKtTU1IzHONNSt31QQzCjBo5eP/754pfCsp/96PSsvhhCxodEIkZ5ST72UbbGkGwOn9DHRq/NTcmMrANNnWJErycAj5fvsbNnv3XS/M5isQS+29UmfIGJRHxZrnSYqSaRiGE0qIUTo3Akhl5XAA4XX66qvxE0ACGI2H9iqlTIYdDnQq/jAx1S6dCf5TKZBBd8bx7OXDoD//7vVrzyziaEwlFEY3G88s4m/OfjHbj0+wtx4dnzqH9AhnB7B0pP6VM8y2osBYIR7GvqSSojZR90AWE4RWYdpleahABGZXmB0D+MEEIISSWzMQ97G7sRjydgtXkwvdI87DHbeOg/9geS+2uS0clTK2AxaWDt8UCtykmL8ztCRkMsFsFckCf0+bE5vLBM0Ez1AwWCEeHcPFcpT3nWSDrR9VVp6C8V1tXtodLRY6RrUIC/aAIDaSMOarzyyitYs2YNPv30UyQSCYhEIpx55plYtWoVli9fDolkYpv0phrHcejuSzUViRgU5Kvx+7+9j1A4CgA4+9RZmFk98TNFSHYoLdKjhbI1htTSPihLozS1WRr9RCIRZtcUY+OWJsTjCXR1u2HQqVBo1qZ6aOMqEo1j8/YWYVa7WCzCvJmlMOjTM0svRy5FoVkr/F78gQicLj+crgB63YGkfhzBUARtnRG0dfbyvTzUOTDoVNDrcqHTKA+a0aFUyHD1Rcfh+2fMwYtvfoN3PtyOaCyOQDCCNS9/iTf/txVXX3Q8zj511oSebJPRS2oSnqEn1dFoHM1tDtQ19GVgNHSjrbMXHLhDPk+nUaK6yoLqKj4LY3qlaVKW0yOEEJIZJBIxCk0atHX2CjXkJzJT2GpzC7fTYUJPJptdU4xCkxZ5agXVuScZqcisFTIkOrvdKQtqdFrdwu3J3iB8KFXlRqFUWGOrDYVmytYYC13dbuH2RH4fjjgScfnllwMAKioqsHLlSqxYsQLFxZNjJvJQet0Boaa/Qa/Gtt3t+GzDXgCARq3A9VeelMrhkQx3YLZGY6sNc2pLUj2slHO5A0Kte1VuTlr1LMhVyjFjmgXb93QAAPbst2Z1s8BgKIrN21uF2WkyqQTzZ5dl1MwqVa4cqlw5yorzwXFcXz8OP5y9B/fj8Hj5Ws2NrXaIRAz02lxMm3LwBV9NngI/vnYpfnDOfDz72kZ8sG43WI6FyxPEX5/+BK+9uwUrL12MU06YTgdPacrtDQm3M2FmE8uyaO9y8X0w+rIwmlrtiA0K0g1FkSPDtCkmVFeZUV1lwfRKE4wGNV1IIIQQklFKCvVo6+wFALR19k5YUIPjOCHDVyRiYCqgRrxHg2GYtDq3I2S0dNpcKBUyBENROHr9CIWjE56pz3Gc0NeAYZisn2R5JLQaJQry1bA7fQiFY+jsdqOkcGLLZmcbry+EQJC/LqTTKCe05+2oghqrVq3CqaeeOp7jyRiDa2fm63Jxz8NvCfdvuHoJzWwkR21wtka3zYvKssikz9ZobncKt6ekSZbGYBaTFo7eADq7+bTT7Xs6cNwxFVl38drnD2PLjlahfJMiR4r5s8sz+u+TYRhoNUpoNUpUlfP9OFyeoJDJ0Z+NAgAsy/UdqMZw4qKqIS8CGw1q/PzHZ+Di8+Zj7ctf4Ytv9wPgZ/T9f4+9h5ff3oQfXnEiFs2roIvIaYRlWaH8lCJHmnZlJDiO/9ur29+Nff3NvBt7hODicCRiMSrLCzB9ignTq8yorjKjtIj6YBBCCMl8alUOdBolXJ4g/IEwXO7AhExKOHCSI2XiEkIKzVo0NNsA8OV4KssKJvT17U6/cI5ekK+GXDa5KuqMVFW5USjD29RqR5FZS+dFR2Fwg/Ai88RmB434L/yFF14Yz3FkFJZl0dPXT0MsFuHj9XVC6unsmmKcsaQ2haMj2YKyNZL5AxGhEXWOXAqzMT1nQ9VMNcPtDSIQjMDrC2FfUw+qqyypHtaYcbkD2LqzTZgFnquUY8GcsqzrFyGRiFGQrxZmjEWicfS6+IbjdqdPKC1ld/oP2cywrDgfD/z8+6hv6MY/X/wSW3e1AgCa2uy4++E3MbO6CD+8/CTMqqFyhenA548gkWAB8LN4Us3nD2NfUw/2NnSjbr8V+xp74HT7D/u8Eose06vMQjPvyrICyOikhhBCSJYqKdQL2dztVteEBDUGT3K0mKj0FCEEKDQNBDU6re4JD2okNwin0lPD0eQpYMxXw9aXrdFhdU9o6cJskuqsRTrDPQJOV0C4oJdIsHj13c0A+JmQP/vR6TTrlowZytYYMLiXRllxftpG0iUSMebOKMHGLY1gWQ4t7U7k61RZkU5tc/iwbXcbWJYvy6TNU+KYWaWT4mKpXCaBxaSFxaSFzeHD1p18cKKl3XHIoEa/6VVm/PG+i7B1Ryv++dKXqG/sBgDs2tuJ2+5/GccdMwXXXX7ihB/4kmQuT0C4rdNMbOmpSDSOhmYb9jYMZGEMPjEZToFejWmVfWWkKs2YWmmCOjdnAkZMCCGEpAdTQR6k+8WIxRPotnlQXWke1+PTwZMcJRIxjFlwnE8IOXpKhQz5OhWcLj+CoQjcnuCETZSKRONC9kGOXIqC/PTsc5kuKsuNsA3K1ii2ULbGkXD0+oWsxYL8ic9azP4rUePA2tcgnOM4vP3BdsQTfIDj0uULKbpHxhRla/Ai0biQ0iaRiFFSmN6zDtSqHEyvNKNuvxUAsLOuE4sXVqZdKZvR6Op2Y+feTqHPhEGvwtwZJZBIJl+qfUG+CrlKOQLBCHrdAXh9oRGXHDxmdhn+NqsUX3zTgDUvfYl2K18D+uutTfhmazNOO6kGKy45PmWN5SY716Am4bpxPgHptnvw3c527O1r5t3UakeCZQ/5nFylHNOn8OWjplfxjbyzIWBKCCGEHA2xWIQiixYt7U6wLF9Tvrxk/ErV2p1+YZKj0aCGWEwXwgghvEKTBk4Xn1nd2e2esKBGp9UlnKsXmbU02fowBmdrhCMxtHe5UFacn+phZZzBpacKU3ANg4Iao5RIsLA5+Gjerr1d2N/UAzCAxajFlRcem+LRkWw0OFvD2uNBZZlx0mVrtHU4heyA0kJ9RlxILyvOh7PXD1tfqaKddZ1YMKcsIw8uWtod2NvQLdy3mDSYVV00aWcyMAyD8pJ87K7vAgA0tztGFWxkGAYnHzcVJyysxAef78azr26EvdcHDhw+/mIP1m2oxznLZuHqHxyXEY2qs4m7L6ghkYjH7XM2Go3j+X9/jZfe2gSWGz6IIZNKUFlWIPTAqK4yU71XQgghZBjFFv6cCQDaOl3jGtRI9UUcQkj6MhXkYc9+KxIJFlabB9VV5gkJfHZY3cJtahA+MlUVA9kazW0OlBTq6FxrFOLxhJC1KJNKUpIdREGNUXK4fIjHEwiGovho/R6g7/rkrT88jZrwkHExOFsDwKTL1ojHE2jr5Gezi0QMSoszJxtqZnURNmxuRDgSg9PlR3ObA1MyrLzQ/qYeNLbahfulRXrUTLVkZHBmLBWatNjfZBNKw02bEh11XxGxWISzT52F006swdsfbMOLb3wDXyCMeCKBtz/Yhg8+240fnHMMLvn+wkkXyEyFQDAipM5q8xTj8je+s64Tf37iQyFDpx8DBmXFelRXWTC90oTqKgsqSg3UdJQQQggZIVWuPKnsi9PlR75u7C+wxOMJocSLTCpBvo4moBBCBkgkYpgLNOjsdiEeT8Dm8I57Fr7LHUAwFAEA6LW5yFXSueNI5KkVMBryYHN4KVvjCPTYvcLkY7MxLyUBIboKP0o9dv4A5sPPdyMSjUMiEeGUxdVYOLc8tQMjWW0yZ2t0WF1CerfFqM2oEk4ymQSzaoqwaVsLAGB/sw16bW5aNCA+HI7jsLu+K6mmf1WFEVXlxhSOKn2IxSKUFuvR0GwDx3Fo7XAecUN4uUyCS85bgLNPnYXX/rMZ//7PFoQiMYSjMbzw5jd458PtuOKCRVh+1jwKno8jjzck3B7rfTQYiuKfL36Btz/YJiyTiMX4wTnHYNG8CkybYoJSMbqgGCGEEEKSFRfqhLIv7V2ucQlqDL6IYzFpJv1EH0LIwYosWnR28+fRnd3ucQ9qDM7SoAbhozO1wgibg8824Htr6CAS0ef6SHT1NQgHUpe1SHk1o+To9aO1w4mtO9ogEYugVMhx4zVLUj0skuX6szX6NbbaUjiaicOyLFo7nML98tLMi5rn61RC82eO47CjrgOxWCLFozo0lmWxbXd7UkCjdlohBTQOUFqoFw54Oqzuo/69qnLlWHnpCXjusVU4/6x5kIj5Wfq+QBhPPr8e1/50Dd77ZCcSiUP3XSBHZrz6aXz7XTOuu/2ZpIBGdaUZT/z+KvzoqpMxd0YJBTQIIYSQMWAyqIUJID12r5CBOZb6+2sCgMWoGfPtE0Iyn06jhCKHn4zpdAUQjsTG7bXi8QS67fznkkQihqkgb9xeKxupVTnCexaJxtHe1XuYZxAACIWjwiQCpUKesom7aRnU+Nvf/oby8nLk5OTg2GOPxbfffjvsum+88QYWLFgArVaL3NxczJ07F88991zSOn6/HzfffDOKi4uhUChQW1uLJ5544ojGFovG8c6H2/mSEAyw6vITYNBPfN0wMvmUFukhk/IH6dYeD/yBSIpHNP567F6EwvwBgDFfDXVuTopHdGSqKozCRdJgKIrd+7pSPKLhxeMJbNnRJtRGFIkYzJlRgtKizCn7NVFkMgmKzPxMmHg8kRQEOho6bS5uue5UPPP/VmLZSbVg+uoc2nt9+POTH+K625/B+q/3CY3gyNjoD2owDANt3tEflHl9Iax+7H+4a/UbQpkKuVSCH1+zFI89dDkqSsev1jchhBAyGYlEImGWMsdxY3Zs1i8SjcPpCgAAlApZRmRfE0ImHsMwKDQPfBZ1dbvH7bW6bV5h0pvFqJmQ/h3ZZvDkzeY2B00iHAHr4CwNc+oC/Gn31/7KK6/g9ttvx/3334+tW7dizpw5OPPMM2GzDT0zXa/X45577sHGjRuxY8cOrFy5EitXrsQHH3wgrHP77bfj/fffx/PPP4+6ujrcdtttuPnmm/HOO++MenzffNcMm90HqUSM6ZVmfP+MOUf8sxIyGpMxW6OpzSHcLs/gC4AMw2B2bTGkfQ3Ou20edHSN7UnWWIhE4/h2W4sQcReLRThmVhnNQjuEwTU3WzucYNmxOwCyGDW465bv4R9/vBrHz68UlndYXXjwkXdx010vYMuOVgpujIFYLAF/IAwAyFPnHNXJAMdxWLehHit/9gw+/mKPsHzezFI8/ci1uOjc+dSAjhBCCBknRYNKr3RaXWN6nNRt8wjbo+NjQsihFJoGPiM6xzGoMTh4S6WnjoxalQNz32d6JBoXerqS4aVD6SkgDYMajzzyCK6//nqsXLlSyKhQKpVYs2bNkOsvXboUF1xwAWpqalBZWYlbb70Vs2fPxpdffimss2HDBlx77bVYunQpysvL8aMf/Qhz5sw5ZAbIcD7/eh8YEQOpVIzbfriMLkyQCTWZsjUcvX74/PxFRm2eEnptZjfhU+TIMKO6ULhf12BNq99fMBTFN1ub4fXxfQWkEjEWzimnTLTDUOXKYTTw6arhSAzdNu+Yv8aUsgL87pfn46+/vRyzqouF5fuaevCL372OO3/7OvY2WMf8dSeTsSo95ej1474/voPfPvofuL38NnOVctxx4xn4470XjXs9XUIIIWSyUypkKMhXA+CPbx29/jHb9uCZqWYTBTUIIcPLVcqF84pAMJLUv2+s+AJh4ZxDrcqBJk8x5q8xWVSWFwi3m9spW+NQvL6QMCFQp1GmtJRyWnUcjUaj2LJlC+666y5hmUgkwrJly7Bx48bDPp/jOHz66aeor6/H73//e2H54sWL8c477+C6665DYWEh1q1bh3379uEvf/nLsNuKRCKIRAYuOHq9/IWqRJyFQinC8jPnYuoUI82QJRNKLBahvCQf+5p6AAANLTbMqS0+zLPGDsdxwr/x1txmF26XleRnxb5mMuSh2KJDh9WFRILFtt1tOH7+lJQHR/2BMDZvbxXqDstlEiyYWw6VUp4V7/t4Ky/RDzQXa7PDMk4nubXTLHjkgYuxaVsLnn7pSzS28PvIdzvb8JO7XsRJx07FiksXJ2WPDGci9+VM4HIHhNuaPOWo3xeO4/C/T3fhyefWIxAcOHY4YWEVbll1qhAcpPebjDXalwnJfLQfj70ii1Yo/dje2Tsmk3SCoahw8VCVK6fjZHIQ2pfJgQrNWmHyVIe1F3nqwsM8Y3QGV38oMmvpb+8oqJRymAvy0G33IhqNo73LBZ1Om+phpaXBmUcWk2bc/u4Y5vAN29MqqOFwOJBIJGAymZKWm0wm7N27d9jneTweFBUVIRKJQCwW4/HHH8fpp58uPP7YY4/hRz/6EYqLiyGRSCASifDUU0/h5JNPHnabq1evxoMPPnjQ8gSbQL5OiQu/NxMej2eIZxIyvjQqMaLRMGKxBBqbAyjQyZGrnJjIKMdx8Pv52U4j+YA5Uj5/GG0dfHktpUKGHCmbNfubpUCBji47AsEIAoEANm9jMb3SdPgnjhO3N4TtezoRj/NNrpUKGWqqTEjEwvB4wikbVyYRARAzCXj9YQQCATS3WqHXjl+N5elT9Hj4rvPw5aZGvPjmZiE7ZN2GvVj/dT1OWTwNly2fj4L84U/gJ2pfzhQdXTYEAvzsKREXG9XnjdXmxePPrMfOvQO9cjR5Ctxw1Qk4fn4FGCaRNZ9fJP3QvkxI5qP9eOzJJRzisQgi0ThagkEUmXIhlx/dpY+WdicCAX4ShLlASd/t5CC0L5MDKWRAOBRCgmXR0ByGpUAJkWhs/jZYlsP+pk7EYgmIRAyUOQx9Lh2lAn0OGlus4DgO9R43isxavp8yEXAch4amLkRjcYhEDBRyjNvfnVarPew6aRXUOFJqtRrbtm2D3+/HJ598gttvvx1TpkzB0qVLAfBBja+//hrvvPMOysrKsH79evzkJz9BYWEhli1bNuQ277rrLtx+++3Cfa/Xi5KSEkjFEtxx45mwmAuGfB4hE2FmdZmQrWF3RVBomZi/x/4IrEajGdcDtZZOH3Jz+XJTtdMsI/owyyQnLFJgw+YmcBwHlzeGcIyBqa+E0USyO33Y19wLuTwHcjmQp1Zg/uxSocQZGbmZNWXYvqcDANDriaKizDLur3neGfPxvVPn4n+f7cKzr30tZBus29iArzY34/tnzMEVFxw7ZBryRO3LmYBlWcRZMXJzc6HIkcJoPHymS//z/v3frVj7ygZEo3GIxfwB7xlLavHja5dCrcoZz2ETAoD2ZUKyAe3H46N6ajEa+rJavYEEqkb4/T4cf71dOD+ZVlkERU7qym2Q9ET7MhlKRblZKF0XiTFC74aj1W33QibLgUwGmAvyUGDQj8l2JzONBqisiMDa40YgEEBnTwBzZpSkelhpxe70QSqTQyqTw2TIgyE/tX93aXXlymAwQCwWo6enJ2l5T08PzGbzsM8TiUSoqqoCAMydOxd1dXVYvXo1li5dilAohLvvvhtvvvkmzjnnHADA7NmzsW3bNvzpT38aNqghl8shl8sPWj5nRglOPm4afUmRlCorzkdLuxPRWBzdNg8qywugzp2YC2gMwwj/xkMwFEWPnZ95LpNKUGTWZd3+plYpUDvNgt31/Mzu3Xu7oFmomNCTI2uPGzvqOoWDb4NehbkzSiCR0EyEI2E2arC/2SbUbvYHIxOyT0qlEnz/jLk4/eRavPW/bXjp7W8RCEYQiyfw7/e24r1Pd+GS8xbgonPnH1Trcrz35Uzh80eE/UCnzR3R+9HUasefnvgQ9Y3d/AKGLy/3sx+djoVzy8dxtIQcjPZlQjIf7cdjr7hQj8ZWBziOQ2e3G1UVxiN+f33+MPx95SX5+uEHXycgBKB9mRys2KITghrWHs+Y9djrGlQCqLhQT39zY2TaFBMcvT4wDINuuxcGqxvFhdSAvZ/VNqhBuFmb8r+7tOpyLZPJMH/+fHzyySfCMpZl8cknn+D4448f8XZYlhX6YcRiMcRisYNq1ovFYrDs6Bu/3HzdqSn/pREiFotQUWoQ7je12g+xdmZp7XAKFxhLi/UQi9PqY2rMlBTqYSrgszNi8QR27OmYsBqYrR1ObB/0emajBsfMKqWAxlFgGCapl0VLm3NCX1+RI8PlFyzCC//3Q1y2fKGQbRMKR/Gv1zbg6luexhvvbUW0r28KGTCaJuHRaBzPvLoBN/7yeSGgwYDBBd87Bv/887UU0CCEEELSRI5cKjQMD0disDl8R7ytwQ3Cx+qCJCFkctBr+WxwALD3+oU+lkcjFI4KfYMUOVLk63KPepuEp1TIMGN6kXB/z/4u+AJUlhsA4vGE8F0qlYgPWe56oqTd1cLbb78dTz31FP71r3+hrq4OP/7xjxEIBLBy5UoAwDXXXJPUSHz16tX46KOP0NTUhLq6Ovz5z3/Gc889h6uuugoAkJeXhyVLluDOO+/EunXr0NzcjGeeeQbPPvssLrjgglGPr7Kcyk6R9FBapBcuXFp7PFnxQRuNxtFh5ZtdicUilBZmdwrlzOlFwgGOyxNE4wQEp/Y39aBuv1W4X1qkx5za4pQ3K88GRWYtpH2BIavNjXAkNuFjUKtycP2VJ+O5x1bhvNPnQMTwv1e3N4i/PfMZVty2Fh+s231EQf1sNTiooT1EUGPPvi78+FfP47nXNyLR9/6VFurx/357GW5eecpBmTCEEEIISa2SQbNr27t6j2gbHMfBanMD4CexmAsmvmQsISRzMQyDwr5gKMdxSRkWR2pwo+YiS/ZVtkg1c0Eeii1aAHzvku27O5BI0Plzj90rvA9moyYtriGlVfkpALj00ktht9tx3333obu7G3PnzsX7778vNA9va2tLeuMCgQBuuukmdHR0QKFQoLq6Gs8//zwuvfRSYZ2XX34Zd911F6688kr09vairKwMDz30EG688cYJ//kIGSv92Rr9s4UbW+yYm+H1/tqtLuFDstiig0yWdh9RY0oqFWN2TTG+3dYCjuPQ2GKHXpsLvXbsZ1pwHIc9+6xJJ3SV5UZMrTCO+WtNVhKJGCWFejS12cGyHNo7ezF1SmqawBv0Ktx2/TJcdM58PPPqBny2YS8AoMfhxR8efx+vvLMJF509G8tOnp31+9nhuPuCGlKJGCrlweUkgqEo1r7yFd587ztw4LObxCIRLjt/Ea668NhJ//4RQggh6cqgV0GpkAnlQYOh6KgnIbg9QYTCMWF79L1PCBmtQrNWmMDY2e1OqroxWgcGRorM2qMcHRnK1Aoj9jQ44A9E4A+EUbffipnVRYd/YhbrGpS1WGgam94wR4vhJqreSYbzer3QaDTweDzIy6PZGSQ9JBIsPt+4D9EYn8J4wqKqca3jz3EcPB7PuDQ/SyRYrP96HyLROBiGwUnHTp00M58bW+3Y39f4PUcuxeIFlWN6wsSyLLbv6RB6lQBAzVRLUrkkMjbCkRjWf70PLMtBKhFj6eLpaVFCbX+zDWte+hLfbmvmF3BAIpGATqvCKYunY9nJNaiZapl0s3wCwQi++GY/AKAgX435s8uSHt+yoxWPPPkRuu0DB3DTpphwx41noKqcAoIk9cbze5kQMjFoPx5fTa127Os7zp5SWoBplaObcLJnXxfaOvlJQbNrilFIFxDJMGhfJofyzdYmIUN88YJK5KkVR7Qdp8uPTdtaAPCB1gVzysdohKRf/74slubg6y1NwsTbyfwdEI7EsG5DPQBAqZDj5OOmpnhEvNRfaSGEHLEDe2s0tmRub42uHrdQX9JUkDdpAhoAMKXUIGRnhCMx7OprID4W4vEEtuxoEwIaDMNgTm0xBTTGSY5cCotRC4DvldJfTi3VplYYsfruC/HIA5dgxrRCYbnXF8LbH27DLb9+CStuW4tnX9sIa487dQOdYC730KWnfP4w/vj4B/jF714XAhoyqQQ/uvJk/N9DV1BAgxBCCMkQg0uzdFhdoyrBybIsum38MbRYLILRoB6XMRJCst/gi+GdR1GCavD5ZZGFGliPJ5VSnnTuvHtfFwLBSApHlDqDs4MsaZKlAVBQg5CMV1qkh7xvVn+3LTN7a3Ach+ZBjZWnHEU6ZiZiGAaza4uFHik2h1eYEXY0otE4vt3WAqfLD4A/GZs/u4waHI6z8pKBgNHgxvfpYE5tCf7fby/D6rsvxImLKoW/OYA/QP7Xaxtw1S1P49Z7X8Z/PtoBnz/zPk9Gw+09uEn4+q/347qfPYP31+0SHptdU4yn/ngNLl2+MC0ybwghhBAyMnKZBKa+PhjRWBw9o2gY7nQFhIz4gnw1JH290wghZLTMBRqIRHyA1drjOaIeh7FYQpisKJNKYKJA67grNGtRZOaDR4kEi2272ydlf8p0LD0FpGFPDULI6IjFIpSXZHZvDZvDh2CIj3jn61RHnIqZyXLkUsysLsLWna0AgL0NVug0SqhVR1ZOLBSOYtO2VuF9lUrEmD+77JCNkMnYUKtyYNCrhNrNPQ4vzAXp88XPMAwWzi3HtAodJNIcfPFNAz7+Yg+27+4Qekbsqu/ErvpO/N/aT3HsMVOw7KQaHDuvIuvqSPengItEDBIJFg/86R188e1+4XGlQo4brjoZZ582My0aoRFCCCFk9EoKdei28Rdk2jt7YTGO7Lisa1D26kifQwghQ5FKxTAV5MHa40E0Fofd6RcCriPV1eMGy/Lna4Xm9GjUPBnUTrPA7Q0iEIzA5w9jb0M3agdlcGQ7nz8Mf9/kaW2eErlD9KFMley6OkHIJFVapEdLuwORaJzP1igvGNfeGmOtuc0h3B48y32yMRrUKCvOR2uHEyzLYdvudixeUDnqmeG+QBhbtrciHOGbGubIpZg/pyyj/iYyXXmJAY5ePkOmpc2ZVkGNwXKVcnzv1Jn43qkzYXP48OmXdfho/R60dPCZU7F4Al9+ux9ffrsf6twcLF08HaefXIvaaZnffyMajSMQjIDjONTt78Hf1q6DPziQmXLcMVNw2/XLUJBPM6AIIYSQTJavU0GpkCMYiqDXHYA/EIEq99AXZRIJFra+rA6pRIyCfNVEDJUQksWKzDpY+2a8d3a7Rx3USCo9ZabSUxNFLBZh7swSbNzcCJbl0NbZC70uN23P8cdaZ/fA31269RShoAYhWSCTszVc7oBQAkatypn0FxCnV5rQ6w7A5w8jEIygbr8VM6uLRvx8tyeILTtaEYsnAPAzzRfOLYMiZ/L0KEkHBr0KalUOfP4w3N4gXO4AdH19U9KV0aDGZecvwqXLF6KhxY6PPt+DT7+qE7IZfIEw3v1oO979aDsKTVosO6kGy06qydhari4P/3t5+4PtaO9yISeHPyTSqBW45bpTsXTx9IwP3BBCCCGEV1qkw94G/lypw9qL6irLIde3ObxCc1hTQR7NiCaEHLV8XS5y5FKEIzHYnT5EonGhlPjheH0hoTSwJk9xxBUdyJFR5+agdmohdtV3AgB27e1CnkqR9b1gOY4TAnEiEQPzKANx442+mQnJEpnaW2NwlkZFyeTqpTEUkUiEObUlQnZGh9UFq81zmGfx7E4fNm1vEQIamjwFjjumggIaKTL477m53XmINdMLwzCYWmHETSuW4pUnbsDquy7EaSfUQD6o/0ZXjxvPvr4R19y6Brfc8xLe/mAbvL5QCkc9OizL4rV3N+OxNZ+hscUOSd/+tuzEGqz9y0qcckI1BTQIIYSQLFJk1gn17DutbiFgMRzr4PrhaTYzlRCSmRiGQWFff0v+YrF7xM8dnKVRnKGTyjJdcaFOaJIdjyewfU/299dwugKIRPneUga9Ou3KUafXaAghR0wsFqGi1CDMQMqEbA1/IAKbk0/rVuRIYTamV9Q3VVS5ctROtWDnXn4WwO76LmjUh54FYO1xY0ddp9CUOl+nwryZJdTQMIXMxjzsa+Jn4tgcXgSCkbSqPzkSYrEIi+ZVYNG8CgRDUXzxzX58vL4O3+1qE/pv7NnfhT37u/D4M+uwaF4FTj+5Fscdk779N1raHfjzEx9h07aWpBmYd9x4Oo49ZkqKR0cIIYSQ8SCVimE2atDV7UYszjfbHS5YEY3GYe8rI5ojl0JHPekIIWOk0KxFU5sdAF+CqnwEEzsTCVYItIrFIurxk0IzphXC4w0jGIrA4w1hX1PPYTP/Mtng3lKF5vT7u0vPKw6EkCNSUqhHc1vm9NZoaR/I0igrzqe07kGKLDo4XH5YezyIxxPYsacDi+aVD/ketXY4UbffKtw3FeRhTm0xvZ8pJhKJUFqkx76mHgD87ymTG4opFTKcuXQGzlw6A3anD59+uRcffVGH5r6D8ngigQ2bG7BhcwNUyhwsXTwNy06qxczqwrTIeojFEnj57U144Y2vEYslkOibVXPCoio89KvzMy7gRAghhJDRKbHo0NXtBgC0dfYOG9TocXiFiUIWoyYtjmMIIdlBlSuHJk8Bj5cvJ+Xzhw9bSqrH7hWqMZgLNDRxMYUkEjHmzijG11ubwLIcWtqd0GtVMBqyr4x6vG8CAMD3ljKmYal4uuJFSBbpz9bo19hiT+FoDi0ciQlRX6lETCmUQ5gxrVDIznB7g0P+Pvc325ICGiWFesydUUIBjTRRUqgXDjo7u92I9qVuZrqCfDUuXb4Q//zTNXjyD1fj4nMXIF870EDTHwzjPx/vwG33v4yrbn4aa17+Ch1drkNscXzVN3Tjx3e9gGde/QqxeAKJBIt8XS6uu+IErLr8RApoEEIIIZOATpsrXDx0e4NCffoDDS491V9qhBBCxsrgJt+DmzAPJ7n0lHY8hkRGIU+twPRKs3B/Z10HwpFYCkc0PmwOn1DZwGzUpOU1pvQbESHkqJQUZkZvjbaOXrAsPwNq8IVfMkAiEWNObYkwO6yx1Q6ni0+F5zgOe/Z1obHFJqxfWW7EjOnpMSue8KRSsXDgmUiwaOvqTe2AxkFVuRE3XrMELz9xPX5/zw+w7KRa5MikwuPddg9eeONrXHvbGvzkrhfw1vvfweOdmP4b4UgMTz73OW6+5yUho0TEiHDWqTPxkxWnoKLEQCUlCCGEkEmkpFAv3G4f4rgsHImh1x0AAOQq5chTKyZsbISQycFi1Ag9frq6PYfsyxAIRoTPJKVCDp02d0LGSA6trDgfRgNfPj0WT2D77nYhwy9bJJWeStMAPwU1CMkymZCtEY8nhJMIkYhBabH+MM+YvDR5CkybYhLu79jDzwLYvqcDbZ0DJ2I1Uy2YWmFMxRDJYZQW5QuBpvbO3qxtJiYSibBgTjnuuuV7eO2pG/Grm7+HBbPLIWIGDjX2NnbjsTWf4uIfPYFf//4tfL6xftyyV77b1Ybrf/4sXn13M1iOf88ry4x4fPUVOOPkWkilfCBVS0ENQgghZNKwGDUQi/ljk66+Mq+DUZYGIWS8SaVi4YJ4NBaH3ekfdt3+knkAZWmkm1nVRVDk8JP5XJ4gGppth3lG5ghHYnC6+oNpsrQNplFPDUKyULr31uiwuoSakIUmLXLk0sM8Y3IrL8mH0+WHo9ePSDSOL77ZL6QBMgyD2TVFsJi0qR0kGZZSIYOpIA/dNg8i0Ti6uj0oLszucmtKhQynn1yL00+uhaPXj0+/2ouP19ehsZU/0EuwLDZuacTGLY3IVcqx5LhpWHZyDWZVFx11Wqs/EMFTL6zHfz7eISyTSSW4+qLjcMl5CyAWi/Dpl3uF5apcKj1FCCGETBZSqRgWowYdVhfi8QS6bd6k4zKrbVBQg5rxEkLGSZFZi+6+z5vObjdMBXkHrcNxHDr7ghoMw6CISnanFamUr6zxzXfN4DgOja126LS5MOhVh39ymrP2eAZ6S6XxtSbK1CAkCx2YrZFOEWOWZdHS7hTul5cYDrE2AfgDmFk1xUJZsf6AhlgswjGzStP6S4bwKgb9nTe3O7IuNfVQDHoVLjlvAf7xx6vx1B+vwaXfXwiDbuBALxCM4L1Pd+L2B17l+2+89GVSFtJobNjciFW3P5MU0JhZXYR//PFqXHHBsZBIxPAHI0JQlbI0CCGEkMmntGggS3xwaVB/IAKvjy+RqclTUM8tQsi4MehVwvm9o9c3ZPa63ekXejUU5KuF9Un60GqUSZU1dtZ1IJIFfTQzofQUQJkahGStwdkaPXYvfP6w0BgvlbptXuGL2ZivplnSIySXSTCrphibt7cA4Jurz59dRhdlM4QmTwG9Nhe97gACwQjsTj+MBnWqhzXhppQV4EdlBfjhFSdi2652fLh+D778Zj9CfZ8JPQ4vXnjzG7zw5jeYXmnGspNqcMri6YdNd3W5A/jbM+vw2Ya9wjJFjgzXX3kSzjt9dlL2h8cz0M9Dm0d1sgkhhJDJJk+tgCZPAY83BK8vBI83BE2egrI0CCEThmEYFJq0aG53gGU5WG0elBXnJ60zuEF4kVk7wSMkI1Veko9edwB2pw+RaBw79nRgwZyyjO116vOH4fPzvXm1ecq0DvBTUIOQLNWfrbG3oRsA0NBiw7yZpSkeFT9Lvd/gbBJyeAa9CnNqi+F0BVBeYqCAUIYpK84Xmry1tDsmZVCjn0gkwjGzy3DM7DKEfngaNmxqxMdf1GHz9lah/0V9YzfqG7vx9399jkVzy7Hs5BosXliVNEOJ4zh8/EUd/rb2M/gCYWH5orkVuO36ZUOmcfd6AsJtahJOCCGETE4lFj083k4AfMNwTV5RUj8NMwU1CCHjrNCiFa6PdHa7k4IakWgcdqcPAD/BcTKfO6Y7hmEwq7oIGzY39vWi8KOpzYHKsoJUD+2IDO7jUpjmwTQKahCSxdItW8Pu9CVFfNO12VA6s5i0VG4qQxkNaigVcgRDEfS6A/D6QshTU6aAIkeG006qwWkn1aDXHcCnX+7FR+v3oKGFL5vHciy+/q4JX3/XBEWOTOi/YTZq8Nd/foJvtzUL28pTKXDTiqVYdlLNsDNj3H2ZGiIRAw1lahBCCCGTksWkwd7GbsTjCVhtHpiNGgRDEQBAvk5FPf8IIeNOnZuDPLUCXh+fNeYLhIVeqF3dbqFkcZFZl7Gz/icLmUyC2bXF2LStBRzHoaHZBp1GCX2GXfPiOE7IWmQYBuYhJgmmEwpqEJLF0i1bI6mXRmn+IdYkJPswDIOK0nzsru8CwGctzaktSfGo0otem4uLzp2Pi86dj5Z2Bz5avweffLEX9l5+llIoHMX763bh/XW7DnruKYur8ZMVSw8ZLI1E48IFC41acdRNyQkhhBCSmcRiEQpNGrR19iKRYLFrb6fwGJWeIoRMlCKzVujl02V1Y3qVGcABpacs2lQMjYySXpuLyvICNDTbwHEcduzpwOIFlZBlUC8UpyuQ1Mcl3cdOZ/OEZLmSQr1QrqU/WyMVvL4QnC4/AECpkMNkSO+ILyHjodCkhUzK74/dNi9C4WiKR5S+yksMuP7Kk/Hi4z/EH++9GGctnQlFjuyg9Qw6FX77i/Px69vOOXzvjaTSU5k1a4YQQgghY6ukcKBheP9FHJGIGbJ8JSGEjAeLUQORiM/C6OrhszNcfX0YAb5cbjr3NCDJKssKkK9TAeC/V3bu7RQybjJBUoNwc/oH+CmoQUiW68/W6Ndf0mWiNbUN7qWRT+mTZFISi0UoKeJPoDmOQ2uH8zDPICKRCMfMKsWdN52J15+6Eb++9RwcO68C6twcnHf6HDz9yAosXlA5om25PUHhtpb6aRBCCCGTmlqVc1B/LYNeDalUnKIREUImG5lMgoJ8vl8G30fDj85BPQ2KLboUjYwcCYZhMKumSJjIaHf6kiqWpLNEgkWP3QsAkErEMOanfx+X9M4jIYSMiVT31giGosKHo1wmQSH1hCCTWGmRHs1tdrAshw6rG5VlRjp5HqEcuRSnnFCNU06oPqLnuwYHNaifBiGEEDLplRTqk44PLKb0n5lKCMkuhSatcL2kvasXvW4+u1wiEcNM5fAyTo5citm1xdi8vQUAsK+pBzqNMu0n1dkcXiQSLADAVJCXEaWa03+EhJCjlupsjdYOp5ByV1qcD7GYPnrI5CWXSVBk5mfcxOOJpHqpZPwkEiy8Pr78Xq5Snvb1QQkhhBAy/szGPEgl/OQSSYbMTCWEZJeCfFXSzP7+C8sWo4aunWQog16FyrICAHyFhu172hGLJVI8qkMbnCFUZNambByjQXsHIZNEqnprRKNx4aKtWCxCCaVPEoKy4nzhdmuHEyzLpnA0k4PHFxKCqweWmiCEEELI5CQSiTBnRgkMehVmVRfRBURCyIQTiURD9i+g0lOZrarCKJx3hsIx7KrvTPGIhheOxOB08RlCSoUs7bNK+tE3NiGTRKqyNdq6eoWZBsUWHc2OJgSAKlcOo4FvQhmOxNBt86Z4RNlvcGkJCmoQQgghpJ9Br8KCOeXUIJwQkjL9mfz9VLk50FC53IzGMAxm1xYL2YA9di/aOntTPKqhdds8wgRAi1GTMT1wKahByCRSUqhHjlwKYGKyNRIJFm0d/Ic2wzBJs9MJmezKSwb2h+Z2RwpHMjlQk3BCCCGEEEJIOlKrcpL6nhZbtKkbDBkzihwZZtUUC/f3Nljh9YVSOKKhDS49ZcmgHrgU1CBkEpnobI2uHjeisTgAvl6tUiEb19cjJJPotbnC7BufPwyny5/iEWUvjuOEoIZcJkGuUp7iERFCCCGEEELIgMpyvgeDUiE7KHODZC6jQS1MaGRZDtt2dyAeT5/+Gr5AWJjwrMlTQJWbOefKFNQgZJIptugmJFuD4zg0tzmF+xUlhkOsTcjkVD5ov2hpdx5iTXI0/IEIYn0HjpSlQQghhBBCCEk35gINli6ejuPnV0IqFad6OGQMTZtiEiY0BkMR7N7XleIRDbB2e4TbhRmUpQFQUIOQSefAbI39zeOTrdHj8CIYigAA8nUq5KmpHiQhBzIX5EGRwwcZ7U4ffIHxLQk3WVE/DUIIIYQQQki6y5FLKaCRhUQiEebUlkDS11/D2uNBR5crxaPiJyN39bgB8CXjLcaDG9anMwpqEDIJDc7WsDnGJ1ujuW2gR8DgIAohZMCBvWZa2ihbYzy4PAHhtjaPghqEEEIIIYQQQiaOUiHDjOmFwv26BmvKJzX2ugMIR2IAgAK9CjKZJKXjGS0KahAyCY13tkavOwCPl29+pFblwKBXjen2CckmxRYdpP0zNmxu4aCCjJ3+fhpisQh56pzDrE0IIYQQQgghhIwti1GDkkI9ACCRYLF9dwcSCTZl4+kaVHrKYtambBxHKrNCMISQMVNSqENzmwPhSEzI1lCrxuZiX8ugLI0plKVByCFJJGKUFOrR1GYHy3Jo7+zF1CmmVA8ra4QjMYTCfKBIo1ZAJKL5HKkQi8WQSKRPQ7xswXEcotEowuEwGIZJ9XAIIQcQi8WQSqWpHgYhhBBC0kR1lRlubxA+fxj+QBh1+62YWV004eNIJFj0OLwA+GsSxnz1hI/haFFQg5BJSiTiszXq9lsB8Nkax8wqPert+gJh2Jw+AIAiRwpTQd5Rb5OQbFdarEdLhwMsy6GtsxdTygogFtPF97FA/TRSy+v1wuFwIBKJpHooWYtlWTidVLqOkHQll8thMBiQl0fHxIQQQshkJxbz/TU2bmlEIsGiw+pCvi4Xlglu0m1zeBGP85POzAV5GXn9gYIahExi45GtMbgnQFlxPs2KJmQEcuRSmI0adHW7EYsn0GF1JfXaIEfOPSiooaWgxoTyer3o7OyESqWCwWCAVCqlbIIxxnEcEokExGIxvbeEpBmO4xCLxeDxeNDZ2QkAFNgghBBCCFS5csyYVogddR0AgN37rMhTK5CrlE/YGLp6BkpPFWZg6SmAghqETGpjna0RjsRgtbkBAFKJGMUW3VgMk5BJoaLEgK5uNwCgtcOJ0iI9XaQcA4MzNahJ+MRyOBxQqVQoLi6mv+VxQkENQtKbQqGAWq1GR0cHHA4HBTUIIYQQAoAPJDhdAXR2uxCPJ7BtdzuOnz9lQiYGR6JxOHr9APgKK5la0YCmUBMyyZUU6pAj52v92hxeeH2hI95WW0cvWJYDAJQW6SHpa35MCDk8tSoHBr0KABAMRYX6luTIxeMJ+PxhAPz7K5XSZ9JEicViiEQi0Gg0dLGdEDKpMQwDjUaDSCSCWCyW6uEQQgghJE3UTrMI2Rk+fxh7G7on5HWtPW5wHH/trtCkzdjzNQpqEDLJ9Wdr9GtosR/RduLxBNq6evu2yaCUSucQMmrlJQP74uBSbuTIuL0h4WCNsjQmVn9TcGqQSwghA5+F/Z+NhBBCCCFisQhzZ5RAJOKDCm2dvei2ew7zrKM3uPTURPfyGEsU1CCEjEm2RnuXS2gyVGjSQi6j6naEjJZBrxL62ri9QbjcgRSPKLO5vdQkPNUyddYPIYSMJfosJIQQQshQ1Koc1Ey1CPd37+1CMBQdt9fzBcLCNb88tQKq3Inr4zHWKKhBCDnqbA2WZdHaMTCrfPBsc0LI6JQPynJqbqdsjaNBTcIJIYQQQgghhKSzkkI9zEYNACAWT2D7nnawLDsur2XtHtQg3KQZl9eYKBTUIIQAOLpsjW6bF+EIXyPYaMjL6EgvIalmMWmS9sVAMJLiEWUmjuPg9vKfYzlyKZQKWYpHRAghhBBCCCGEHGzm9EIoFfy1NI83hP1NtjF/DY7jYLW5AfBZpJlcegqgoAYhpI9IJMKUsgLh/miyNZraHMLtihLqpUHI0RCJRCgt0gv3B2dBkZHz+cNCSTzK0iCTBcMwWLp06VFtY926dWAYBg888MCYjIkQQgghhBByaBKJGHNnFAv9NZrbHbA5fGP6Gr3uAEJhfkKyQa/K+LLxFNQghAiKLdpRZ2vYnT74A2EAfM16nTZ3XMdIyGRQUqiHWMx/RXd2uxGNxlM8oszj8lA/DZIaDMOM6h8hhBBCCCGE5KkVmF5pFu7v2tspVEUZC11ZVHoKADI7JEMIGVP92Rp79nUB4LM1jplVesjntLRTLw1CxppUKkaxRYfWDicSCRZtXb2oKjemelgZhYIaJFXuv//+g5Y9+uij8Hg8Qz42lurq6qBUHt3f+6JFi1BXVweDgb7TCSGEEEIImUhlxflwugKwObyIxuLYvrsdi+ZVHPVkqESCRY/DC4DPCjEa8sZiuClFQQ1CSJJiixZNrXaEIzEhWyNPrRhyXY83BKfLDwBQKuQwGtQTOVRCslpZcT7aOnvBcRzaO3sxpdQAkYgSLEeqv0m4WCyCWpWT4tGQyWSosk3PPPMMPB7PuJd0qq6uPuptKJXKMdkOIYQQQgghZPRmVRdhw+YQQuEYXJ4gGpptmDrFdFTbtDm8QnlmkyFPqAyRyTL/JyCEjKnR9NZobh/opTGl1EBlNAgZQ0qFDKYCfvZEJBpPShUlhxYKR4U0XW2ekj6bSFpqaWkBwzBYsWIF6urqcMEFFyA/Px8Mw6ClpQUA8Oabb+Lyyy9HVVUVlEolNBoNTjrpJPz73/8ecptD9dRYsWIFGIZBc3Mz/vrXv6K6uhpyuRxlZWV48MEHwbJs0vrD9dQoLy9HeXk5/H4/br31VhQWFkIul2P27Nl4/fXXh/0ZL730Uuj1eqhUKixZsgTr16/HAw88AIZhsG7duiN56wghhBBCCMlaUqkYc2pLhPPYxlY7HL3+o9pmV8+g0lPmzC89BVCmBiFkCCPJ1giGouix86lrcpkkaz4UCUknFSUGdNv4g4/mdgeKLFq6QD8C7kGlp6hJOEl3DQ0NOO644zBr1iysWLECTqcTMpkMAHDXXXdBJpPhxBNPhMVigd1uxzvvvIOLLroIf/3rX3HzzTeP+HXuvPNOfP755zj33HNx5pln4q233sIDDzyAaDSKhx56aETbiMViOOOMM+ByufCDH/wAwWAQL7/8Mi655BK8//77OOOMM4R1Ozs7sXjxYlitVpx11lmYN28e6uvrcfrpp+PUU08d3ZtECCGEEELIJKLVKDG1woh9TT0AgJ11HVi8sOqImntHonEhKKLIkUKfJb1wKahBCDnIQb01mm04ZnZZ0jot7Q5wHAcAKC3Op7I4hIwDTZ4COo0SLk8QgWAEdqefyryNAPXTIJnkq6++wn333YcHH3zwoMfee+89TJkyJWmZ3+/H4sWLce+99+K6666DXC4f0ets3boVO3bsgMViAQDce++9mDp1Kh577DHcf//9QiDlULq6urBw4UKsW7dOWP+KK67AsmXL8MgjjyQFNX71q1/BarXioYcewt133y0sX7NmDVatWjWiMRNCCCGEEDJZVZQa4PIEYXf6EInGsWNPBxbMKRv1REdrj1u4fmcxZs9ESQpqEEKGlJSt4fTB6wsJdemjsTg6u90A+Hr1pYX6FI6UkOxWXmKAy9MGgA8mUlDj8PqDGgzDQJs3dE8gklobNjciGo2nehiHJJNJsHhB5bi/jtlsxj333DPkYwcGNABApVJhxYoVuOOOO7Bp0yaceOKJI3qde++9VwhoAIDBYMDy5cvxr3/9C/X19Zg1a9aItvOXv/wlKQBy2mmnoaysDJs2bRKWRSIRvPbaazAajbjjjjuSnr9y5Ur84Q9/QH19/YhejxBCCCGEkMmIYZi+/hqNCEdicLr8aGpzoHJQyfiRsNoGSk9ZsqjKCgU1CCFDGipbY96sUgBAe2cvEgm+BndJoQ5SqThl4yQk2xkNaigVcgRDEfS6A0OWgyMDYrEEfP4wAECVK4dEQp9P6SgajQt9Tya7OXPmDJslYbPZ8PDDD+N///sfWltbEQqFkh7v6uoa8evMnz//oGXFxcUAALfbPaJtaLVaVFRUDLmdjRs3Cvfr6+sRiUSwYMGCgzJJGIbB4sWLKahBCCGEEELIYchkEsyuLcambS3gOA4NzTboNUroRlhCyh+IwOPlzyHy1Aqoc3PGc7gTioIahJBhDZWtkUiwaO3sBcBfmCgrzk/xKAnJbgzDoKI0H7vr+YuXze0OzKktSfGo0pfHN3DRN1tqhWYj2RHUgp1oEzVGk8k05PLe3l4sXLgQbW1tOOGEE7Bs2TJotVqIxWJs27YNb7/9NiKRyIhfJy8v76BlEgn/MyYSiRFtQ6MZemaXRCJJajju9fI9t4xG45DrD/czE0IIIYQQQpLptbmoLC9AQ7MNHMdh+54OLF5QOaLzFWuPW7hdaMqeLA2AghqEkEM4KFujxQa5lJ8JDQBmYx4UOYevwU0IOTqFJi32N9kQjcXRbfNi2pQo7XvDcLkDwm0qPZW+JqKsU6YYrqbt008/jba2Nvz2t7/Fr3/966THHn74Ybz99tsTMbwj0h9AsdlsQz7e09MzkcMhhBBCCCEko1WWFaDXFUCvO4BwJIZdezsxb1bpIftjcByHrr6gBsMwsJi0EzPYCZKWnX3/9re/oby8HDk5OTj22GPx7bffDrvuG2+8gQULFkCr1SI3Nxdz587Fc889d9B6dXV1+P73vw+NRoPc3Fxh5hsh5NCKLVrkyKUAALuTr9/Xb0rp6Or4EUKOjFgsQkkR37uG4zi0djhTPKL05fYOZGpoqUk4yWCNjY0AgOXLlx/02BdffDHRwxmV6dOnQy6XY8uWLQdlk3Acl1SqihBCCCGEEHJoDMNgdm0xZFI+P8Hm9KGl/dDXBVyeIEJhvuSvQa+CPAOy5Ucj7YIar7zyCm6//Xbcf//92Lp1K+bMmYMzzzxz2Jleer0e99xzDzZu3IgdO3Zg5cqVWLlyJT744ANhncbGRpx44omorq7GunXrsGPHDtx7773IycmeOmKEjJf+bI1+/VkaBr1KaBxOCBl/pUV6iET8LIwOq1vYF8kAlmXh9vJNwhU5UspmIRmtrKwMAPDll18mLX/xxRfx3nvvpWJIIyaXy3HRRRehp6cHjz76aNJjzz77LPbu3ZuagRFCCCGEEJKhcuRSzK4tFu7va+oR+mUMpavbLdzOttJTQBoGNR555BFcf/31WLlyJWpra/HEE09AqVRizZo1Q66/dOlSXHDBBaipqUFlZSVuvfVWzJ49O+kE8J577sHZZ5+NP/zhD5g3bx4qKyvx/e9/f9g6v4SQZMUWLRQ50qRl5SWGFI2GkMlJLpOgyKwDAMTjCXRYXSkeUfrx+SNIJPi6/pSlQTLd1VdfDY1Gg1tuuQWXXHIJ7rzzTpxxxhm4+uqrceGFF6Z6eIe1evVqmEwm/OpXv8LZZ5+Ne+65BxdddBFuuOEGnHXWWQD4iROEEEIIIYSQkTHoVULVFL6/RvuQEx4TCRbddr7PnUQihtFwcH+9TJdWZxLRaBRbtmzBsmXLhGUikQjLli0bUZo6x3H45JNPUF9fj5NPPhkAP2vzv//9L6ZNm4YzzzwTRqMRxx57LN56663x+jEIyToHZmuocuUw6FUpHBEhk1NZcb5wu7XDmdSYlwAuz0A/DZ2GmoSTzFZcXIzPP/8cp512Gj7++GM8+eSTiEaj+PDDD3HeeeeleniHVVJSgo0bN+Liiy/Ghg0b8Oijj8Jms+HDDz9EVVUVgKGblxNCCCGEEEKGV1VRAF3fJL5gKIpd9Z0HrWNz+hCP88EOkyEPYtpIWOgAADXHSURBVHFahQDGBMNxHJfqQfTr6upCUVERNmzYgOOPP15Y/otf/AKff/45vvnmmyGf5/F4UFRUhEgkArFYjMcffxzXXXcdAKC7uxsWiwVKpRK/+93vcMopp+D999/H3Xffjc8++wxLliwZcpuRSCSpBrDX60VJSQncbjedgJFJiWVZfLerHdYeBxYvrIZeSxcMCUmFrTtbYXf6AQCza4qOqNkXx3HweDzQaDSHbCyWabbtakePg5+NsnhBJZXIS6FwOIyWlhahRxoZP4lEAmKxONXDGJWTTjoJGzduhNvthkpFkyRI9jvUZ2K2ficTMtnQvkxIdsiUfTkUjmLD5kbE4/xEx5qpFpT29eEEkq8bLJhThnxdZh1zj+S9z4oOIWq1Gtu2bYPf78cnn3yC22+/HVOmTMHSpUuFWazLly/Hz372MwDA3LlzsWHDBjzxxBPDBjVWr16NBx988KDlHo8HaRQHImRCVZZqYNKLIUIMHo8n1cMhZFLSa2RoaeMzEnbWtUKZM/oDLY7j4PfzBzjpfKA2Wh1ddkRjcUjEIiTiYXg8kcM/iYyLaDQKlmWRSCSQSFD/l/GUzhlbVqsVFosladkLL7yAr776CqeffjoUCgX9fZBJIZFIgGVZ+Hy+pIlzQPZ+JxMy2dC+TEh2yKR9uawwDzvq+CyNrTsaIWLiUOfKEY0l0NpuA8dxkMskEDPxjLuGp9VqD7tOWgU1DAYDxGIxenp6kpb39PTAbDYP+zyRSCSksc+dOxd1dXVYvXo1li5dCoPBAIlEgtra2qTn1NTUHNR4cbC77roLt99+u3C/P1NDo9FQpgaZtPoDeukesSYkm2k0GljtIXh9IXAA4qx41LMusnFfDoaikMrkkMrkyNfljuggiIyfcDgMp9MJsViccVkEmShd3+O5c+di3rx5qKmpgVgsxvbt27Fu3Tqo1Wr86U9/SttxEzLWxGIxRCIR1Gr1kJkaQHZ9JxMyGdG+TEh2yKR9WaPRIM6K0drhBAA0t3tw/PwpcHe7oVTy5akqSgxZe26cVkENmUyG+fPn45NPPsH5558PgJ999sknn+Dmm28e8XZYlhVmwMhkMixcuBD19fVJ6+zbtw9lZWXDbkMul0Mulx+0nGGYtP+jJmQ89e8DtB8QkjoVpQZs390OAGjt6IVBrx71NrJtX/Z4Q8JtnTY3a36uTNX//mfT31g6Gpw9nI7v84033oh3330XmzdvRiAQQEFBAa644grce++9qK6uTvXwCJkwh/tMzLbvZEImK9qXCckOmbQvT680weUJwusLIRiKYs9+K4KhqPB4oUWbET/HkUiroAYA3H777bj22muxYMECLFq0CI8++igCgQBWrlwJALjmmmtQVFSE1atXA+DLRC1YsACVlZWIRCJ477338Nxzz+Hvf/+7sM0777wTl156KU4++WShp8a7776LdevWpeJHJIQQQo6KuSAP+3KkCIVjsDt98AXCUOdO7r4FLk9QuN3fNI0QkloPPfQQHnrooVQPgxBCCCGEkKwkEokwp7YYG7c0IR5PwNozUGZKrcrJ6usEaRfUuPTSS2G323Hfffehu7sbc+fOxfvvvw+TyQQAaGtrg0g00LE9EAjgpptuQkdHBxQKBaqrq/H888/j0ksvFda54IIL8MQTT2D16tX46U9/iunTp+Pf//43TjzxxAn/+QghhJCjxTAMyorzsbehGwDQ2u7EzOqiFI8qtfqDGgzDQJtHQQ1CCCGEEEIIIdkvVynHjOmFQjWHfkVmbWoGNEEYjrpej4jX64VGo4HH46GeGmTS4jgOHo8nI2oLEpLt4vEEPt+4D7F4AiIRg5OPm4YcuXREz822fTkWS+CTL+sAAJo8BY6fX5niEZFwOIzm5mZUVFQcVD+ejB2O45BIJCAWi7NiXyYkWx3qMzHbvpMJmaxoXyYkO2Tyvry7vgvtXb0A+Ml+S44f+TWCTCQ6/CqEEEIISTcSiRglhXoAAMtyaO/sTfGIUodKTxFCCCGEEEIImcyqq8zQ5CkAABaTJqsDGkAalp8ihBBCyMiUFuvR3O4Ax3Fo6+zFlLICiMWTb76CxzsQ1NBSUIMQQgghhBBCyCQjFotw7LwKeH1h5KmzP1t+8l35IIQQQrJEjlwKi0kDAIjFE+iwulI8otTodQeE29RPgxBCCCGEEELIZCQSiaDVKJP6UWer7P8JCSGEkCxWUWIQbrd2ODHZWmWxLAuPLwQAUCpkWZ9iSwghhBBCCCGETHYU1CCEEEIymFqVA4NeBQAIhqLocXhTPKKJ5fWFwbJ8IIdKTxFCCCGEEEIIIdmPghqEEEJIhisflK3R0uZM4UgmHjUJJ4QQQgghhBBCJhcKahBCCCEZzqBXQZXLNwJze4NwDeoxke0GBzUoU4NMdgzDYOnSpUe1jXXr1oFhGDzwwANjMqaxdNttt8FgMMDn86V6KCPW0tIChmGwYsWKVA+FjJGrrroKZWVlCIfDqR4KIYQQQsikRUENQgghJAtUlOQLt5vbJ0+2hrsvqCGViKFSylM8GkL4wMJo/pGR2b9/Px5//HH8/Oc/h1qtFpY/88wzwnt50UUXDfv8v//978J6FGAYsH79euF9ee2111I9nIxw3333obOzE48++miqh0IIIYQQMmlJUj0AQgghhBw9i0mD/c02hCMx2BxeBIIR5Gb5Rf5AMIJoLA6Az9KgC8QkHdx///0HLXv00Ufh8XiGfGws1dXVQak8uoylRYsWoa6uDgaD4fArT6Df/va3kEql+MlPfjLk4xKJBO+++y4cDseQY3/66achkUgQj8fHe6hJioqKUFdXB41GM6GvO1JPP/00AD4Yt2bNGlx88cUpHlH6mzZtGpYvX46HH34Yt9xyC3Jzc1M9JEIIIYSQSYeCGoQQQkgWEIlEKCnSY39TDwCgtcOJ2mmFKR7V+HJT6SmShoYq2/TMM8/A4/GMe0mn6urqo96GUqkck+2MJafTiVdffRUXXXRRUpbGYN/73vfw7rvv4vnnn8dtt92W9NiOHTuwZcsWfP/738c777wzASMeIJVK0+797Of1evH6669j9uzZMJlM+PDDD9He3o6SkpJUDy3tXXXVVXjjjTfw8ssvY9WqVakeDiGEEELIpEPlpwghhJAsUVqoh1jMf7V3drsRjU7sjOSJRk3CSSYb3Guhrq4OF1xwAfLz88EwDFpaWgAAb775Ji6//HJUVVVBqVRCo9HgpJNOwr///e8htzlUT40VK1aAYRg0Nzfjr3/9K6qrqyGXy1FWVoYHH3wQLMsmrT9cT43y8nKUl5fD7/fj1ltvRWFhIeRyOWbPno3XX3992J/x0ksvhV6vh0qlwpIlS7B+/Xo88MADYBgG69atG9F79dJLLyESiRwyi2Dx4sWorq7G2rVrD3pszZo1EIvFuPbaa4d9fmtrK1atWoWioiLIZDIUFxdj1apVaGtrS1rvtNNOg0gkQmtr65Db+elPfwqGYfDRRx8BGL6nxtKlS8EwDGKxGB544AGUl5dDLpdj2rRpePzxx4fctsPhwI9+9CMYjUYolUosXLgQb775plCC65lnnhn25xvKSy+9hGAwiGuuuQbXXHMNWJY9aBurVq0CwzBYv379kNt45JFHwDAMnnrqqaTlO3bswGWXXQaLxQKZTIaysjLccsstcDqTyyOOx34AAE8++SRmzJiBnJwclJSU4Be/+AXC4fCwfWd8Ph/uv/9+zJgxAwqFAlqtFmeeeSa+/PLLIbd/zjnnQKlUjvo9J4QQQgghY4OCGoQQQkiWkErFKLboAACJBIt2qyvFIxpf/UENkYiBRq1I8WgIOTINDQ047rjjYLfbsWLFClx77bWQyWQAgLvuugu7d+/GiSeeiFtvvRUXX3wx6uvrcdFFF+Gxxx4b1evceeed+O1vf4vjjz8eN954IwA+q+Tee+8d8TZisRjOOOMMfPjhh/jBD36Aq666Co2Njbjkkkvw4YcfJq3b2dmJxYsX49VXX8Wxxx6Ln/70pzAYDDj99NPxzTffjGrsn3zyCQDguOOOO+R6K1euFLIy+kWjUbzwwgs488wzUVg4dPbavn37sHDhQqxZswbz58/HHXfcgXnz5mHNmjVYsGAB9u3bJ6x79dVXg+M4vPDCCwdtJx6P4+WXX0ZhYSFOO+20Ef1sl19+OdasWYMzzzwTq1atQm9vL37yk58cFCTw+/1YsmQJnnrqKUydOhW33norqqurcdlll+GNN94Y0Wsd6Omnn4ZYLMaVV16JCy+8ECqVCmvXrgXHcUk/LwA8//zzQ27jueeeg1wuTwo4vfPOO1i0aBHeeecdLF26FLfddhtmzZqF//u//8Pxxx8Pl+vg76ax3A/uu+8+3HjjjXA6nbj++utx8cUX49VXX8Ull1wy5M/Q29uL448/Hr/5zW+g0+lw44034gc/+AG2bNmCU045BW+99dZBz5HJZJg/fz6+/vprBAKB4d9kQgghhBAyPjgyIh6PhwPAeTyeVA+FkJRhWZZzuVwcy7KpHgohZBiBYIR7/7Nd3P8+3cl9+mUdl0gkDlonG/blSCTG/e/Tndz/Pt3JbdzcmOrhkAOEQiFuz549XCgUSvVQ0kJZWRl34GF3c3MzB4ADwN13331DPq+x8eC/bZ/Px82aNYvTaDSc3+/nYrGYsC8D4JYsWZK0/rXXXssB4CoqKriuri5hud1u57RaLadWq7lIJCIs/+yzzzgA3P333z/kz7B8+fKk9T/++GMOAHfmmWcmrX/VVVdxALiHHnooafnTTz8t/NyfffbZkD/3gQoKCriioqIhH1u7di0HgFu9ejVntVo5iUTC3XTTTcLjr776KgeA+/e//81t3LiRA8Bde+21Sds45ZRTOADck08+mbT8b3/7GweAO/XUU4VlXq+XUygUXG1t7UFjeffddzkA3M9//nNhWf/v+cDXXLJkCQeAO/bYY5POL/bu3ctJJBJu+vTpSev/+te/5gBwP/rRj5KW97//ALi1a9cO+R4NZceOHQf93q655hoOAPfxxx8Ly1iW5UpLSzmdTseFw+GkbezcuZMDwF100UXCMofDweXl5XFFRUVcS0tL0vovvfQSB4C7+eabhWVjsR8EAgFheX19PScWi7mioiKup6dHWO71erna2toh95ErrriCA8A99dRTSct7enq4kpISrqCgYMjPsp/97GccAO7TTz8dctyDHeozMRu+kwkhtC8Tki1oX84c1FODEEIIySJKhQymgjx02zyIROPo6vaguFCX6mGNOSo9ldl+/Kvn4XIHD79iCum0Svz94avG/XXMZjPuueeeIR+bMmXKQctUKhVWrFiBO+64A5s2bcKJJ544ote59957YbFYhPsGgwHLly/Hv/71L9TX12PWrFkj2s5f/vIXYQY9wJdjKisrw6ZNm4RlkUgEr732GoxGI+64446k569cuRJ/+MMfUF9fP6LXi0ajsNvtOOaYYw67rtlsxtlnn42XXnoJf/7zn5GTk4M1a9agoKAA5513XlIGR7+2tjZ89tlnqK2txfXXX5/02I033ojHHnsMn376qdBrQq1W4/zzz8dLL72ErVu3Jo3rueeeA8D3Wxip1atXIy8vT7g/ffp0nHDCCfj888/h8/mEHiLPP/88ZDIZfvOb3yQ9/7TTThOyZ0ajv0H4NddcIyy75ppr8Oyzz+Lpp58WMk0YhsGVV16J1atX47///S8uvPDCQ/68zz77LLxeL/7v//4PZWVlSa952WWX4Y9//CNefvnlgzIsjnY/WLJkCQC+pFYikcAdd9wBo9EorK9Wq/HrX/8aV1xxRdJ2HA4HXnnlFZx66qn44Q9/mPSY0WjEnXfeiZ/+9Kf4+OOPce655yY9bjKZAAAdHR1DjpsQQgghhIwfCmoQQgghWaa8OB/dNg8AoLndgSKLFgzDpHhUY4uahGc2lzsIe68v1cNIC3PmzEkKEgxms9nw8MMP43//+x9aW1sRCoWSHu/q6hrx68yfP/+gZcXFxQAAt9s9om1otVpUVFQMuZ2NGzcK9+vr6xGJRLBgwQLI5fKkdRmGweLFi0cc1OjvwaDVake0/nXXXYd33nkHb775Jk4++WR8+OGHuPXWWyGVSodcf9u2bQCAJUuWHPQ5KRKJcPLJJ2Pv3r3Ytm2b0ED76quvxksvvYTnnntOCGp4vV68++67mDVrFubMmTOisQKH/72o1Wp4vV60tLSgtrZWuJA+2AknnDCqoEYkEsHzzz8PtVqNCy64QFh+yimnoKSkBG+++SZcLhd0Op3w865evRrPPfecENRgWRYvvvgi8vPzcfbZZwvb+PrrrwEA33zzDRobGw967XA4DIfDAYfDAYPBICwfq/1g+/btADBksO+EE044aNmmTZuQSCQQiUQO6iMDAPv37wcA7N2796Cghl6vB8AHRgghhBBCyMSioAYhhBCSZbQaJXQaJVyeIALBCOxOP4wGdaqHNabcXgpqZDKdNv1/ZxM1xqEuUgN8nf+FCxeira0NJ5xwApYtWwatVguxWIxt27bh7bffRiQSGfHrDM4G6CeR8KcCiURiRNvQaDRDLpdIJEkNx71eLwAkzZQfbLifeSgKBd8vJxwOj2j9c845ByaTCWvWrEFTUxNYlsV111037Pr9Yx1uTP3ZLf3rAcAZZ5wBk8mEl19+GX/6058gFovx+uuvIxQKCT0oRmokv5exfD8B4K233oLT6cTKlSuF9xfggzhXXnklHn74Ybz44ov4yU9+AgCoqanB/Pnz8d577wnBjnXr1qGjowM33XRTUsCot7cXAPC3v/3tkGMIBAJJQY2x2g8O9V4N9Rr94/3qq6/w1VdfHXK8B+oPriiV6f95RgghhBCSbSioQQghhGSh8hIDXJ42AEBrhzOrghosy8Lj67uYpJBDLqPDmUwzEWWdMsVwWVRPP/002tra8Nvf/ha//vWvkx57+OGH8fbbb0/E8I5I/4V6m8025OM9PT0j3pZWq4VUKhUuPh+ORCLBNddcgz//+c/YvXs3Fi1ahJkzZx52rMONqbu7O2k9ABCLxbj88svx6KOP4uOPP8aZZ56J5557DiKR6KDyRmNhLN9PYKD01Nq1a7F27dph1+kPagB8tsZtt92GV199FTfccINQeurAIE7/WHfu3HnI9/1AY7UfDH6vDix/NdT71L/+HXfcgT/96U8jHi8wEBApKCgY1fMIIYQQQsjRE6V6AIQQQggZe0aDGkoFX/bF6fLD6wsd5hmZw+MNgWU5ANRPg2Sv/tI9y5cvP+ixL774YqKHMyrTp0+HXC7Hli1bDsom4TguqVTVSMycORPNzc2IRqMjWv+6664Dy7KwWq2HzNIAgLlz5wIA1q9fD47jDhrr+vXrk9br138x//nnn0d7ezs+//xznHLKKSgqKhrRGEcjLy8P5eXlaGhoGDKwsWHDhhFvq7W1FZ988glMJhNWrVo15L+Kigp89913+O6774TnXX755ZBIJHj++ecRCoXwxhtvoKqqCscdd1zS9o899lgAGPXveDij3Q/6S38NlXUx1Pu0cOFCMAxzROPtL6E20n40hBBCCCFk7FBQgxBCCMlCDMOgojRfuN/cnj01v6lJOJkM+meZf/nll0nLX3zxRbz33nupGNKIyeVyXHTRRejp6cGjjz6a9Nizzz6LvXv3jmp7S5YsQSQSEfolHE51dTX+97//4c0338SVV155yHVLS0txyimnYPfu3VizZk3SY//4xz9QV1eHU089Vein0e+YY45BbW0t3nzzTTz55JPgOG7UpadG48orr0Q0GsX999+ftHzdunX44IMPRrydtWvXgmVZ3HDDDfjnP/855L9f/epXAAYyOgC+nNMZZ5yBr776Co8++ii8Xu+QDdFXrlwJtVqNe+65B7t37z7o8WAwKPTdGInR7geXXXYZRCIR/vznPyf1uggEAnjooYcOWt9sNuOSSy7Bhg0b8Mc//vGgwBbA9wcJBoNDLrdYLJg6deqIfx5CCCGEEDI2KKhBCCGEZKlCkxYyKV+aqdvmRSg8slnO6SaRYOHxhtDR5ULdfivauwbK0FA/DZKtrr76amg0Gtxyyy245JJLcOedd+KMM87A1VdfLTRrTmerV6+GyWTCr371K5x99tm45557cNFFF+GGG27AWWedBYDv4TAS/c2sP/rooxG//llnnYXzzz8fKpXqsOv+/e9/h8FgwPXXX4/zzz8fd999N5YvX44bb7wRBQUF+Pvf/z7k866++moEAgH84Q9/gFKpxA9+8IMRj2+0fvnLX6K6uhpPPPEETjrpJNx999245pprcNZZZ+G8884DcPj3k2VZrF27FgzDYMWKFcOud+mll0KhUOCFF15I6mVy9dVXg+M4IbAyVFCjoKAAL730Evx+P+bMmYNzzz0XP//5z3HLLbfgvPPOg9lsHrIh93BGux9Mnz4dv/rVr9DR0YFZs2bh1ltvxR133IGZM2cKTe4PfJ8ef/xxzJ07F7/4xS8wZ84c3HDDDfjlL3+JK664AtOmTcNxxx2X1FMF4DNImpub8f+3d+9hUZf5/8dfwyiDwDCEooIKYnjIlDQzI1HKI7p6QUqtWq241mZrrZUd1r6ZuuXawWs127XTZdkBtbTUtdKUEjyhqyW1prJKiCdUUhkOykH4/P4g59cE6qjoMPh8XBfXxdyf+3N/3p9P3XOP8+a+74SEBJfvBQAAALWHpAYAAPWU2eylVi2CJFUto5Jz8LibIzo/wzB06nSZjuYVaO++Y9q+Y7/Wbd6jNet2Kv3bLO3IPKScg8d1uqRckuTdsIH8/Sxujhq4Mlq2bKm0tDT17dtXKSkpeuutt1RWVqbVq1c7vsSuy1q1aqX09HTdfffd2rRpk2bPnq1jx45p9erVioyMlFTzJtk16d27tzp27Kjk5OQrEmv79u21bds2JSUl6T//+Y9effVVbd26VWPGjNHWrVvVrl27Gs+799575eXlpfLycsXHx7uUQLlUVqtV69at09ixY7V7927NmjVLO3fu1MKFCxUbGyvpws8zJSVF+/fvV+/evR1f8NfEZrNp2LBhys/P12effeYoj4+PV0BAgMrLyxUdHa3rr7++xvN/97vfafv27UpKStKOHTv0+uuvKzk5WTk5ORozZoxeeOEFl+/7UvrB9OnTNXfuXF133XV688039cknnygxMVFz586VVP05BQUFadOmTXrllVfk7e2t5ORkvf7669q8ebNuvPFGffDBB06bmktVy45J0kMPPeTyvQAAAKD2mIya5tiimoKCAtlsNtntdpf/AQbUN4ZhyG63y2aznXNDRwB1S2nZGaWlZ6qy0lCDBmbF3tZODRp4ub0vl5dXqKi4RIXFpSosKlFhUYmKTpXqzJkKl873sTRU++ubKaRZ4JUNFJekpKRE2dnZioiIkI+Pj7vDqbcMw1BFRYXMZrNHjcsxMTFKT0+X3W53OREwb948PfDAA9qwYYN69ux5hSP0LPfdd5+Sk5O1c+dO3XDDDe4Op85KSUlR//799fTTT+vll1++5HbOnDmjtm3bKiIiQt98841L55zvPZHP10D9QF8G6gf6sudo4O4AAADAlWPxbqDQZoE6mHtSZ85U6GDuSbVu1fjCJ9YSwzBUfKpMhcUlKvoleVFYXOKYbXEhXl4mWf19ZPXzkb+fxfG7tzcfYYC6Ljc3VyEhIU5lH330kTZu3KgBAwZc1MyGpKQkzZo1S9OmTdPq1atrO1SPUNPzTEtL06JFi9S+fXsSGr/Iy8tTUFCQzGazoyw/P1+TJk2SpMteMur9999XTk6OPv3008tqBwAAAJeObwQAAKjnWrdqooO5JyVJOQePK6zFdVfkOmVlZ1RY/Evioqi0KpFRXKLKStcmhfo28pa/n4+sfhb5/5K88PP15i9kAA/VqVMnde3aVR07dpTZbFZGRoZSU1NltVo1c+bMi2rLbDbr3Xff1cqVK1VYWCir1XqFoq67Bg8erEaNGqlLly7y8/PTzp07tWrVKpnNZr3++uvuDq/OSE5O1syZM9WnTx+FhoYqNzdXq1at0rFjx5SUlKTo6OjLat9kMumdd97RzTffXEsRAwAA4GKR1AAAoJ7z97OoaWOrjh0vVElpuY7mFcjX59ITBZWVlVWzL34186KouFQlpa7NvmjQwFyVuPDz+WXmRdXvDRuaL3wyAI8xbtw4rVixQtu2bVNxcbGCg4M1atQoTZ48WR06dLjo9m699VbdeuutVyBSzzB69GglJydr0aJFKiwsVGBgoIYOHapJkyapR48e7g6vzrj99tvVrVs3paSk6MSJEzKbzbrhhhs0efJk/fnPf77s9v/4xz/WQpQAAAC4HOyp4SL21ABYWxDwZCfyi/Wf7dmSqpIcndoFu9SXS0rLqyUviopL5erHBz/fX5aM8veR/y+/+zbyvuz7Qd3GnhpXh6fuqQFca9hTA6j/6MtA/UBf9hzM1AAA4BoQFOinAGsjFRSeVlFxqU7kn5LNZnMcr6ioVNHZTbsdS0iVqNzFjbsbNjArwNpIVn+L/H1/SWL4WWQ2e12pWwIAAAAAANcgkhoAAFwjIlo11vc7D0qSsvf/rErDrMLiUhUWlerU6VKX2jCZTPL3s1Rt2n12+Sh/H/lYGl7J0AEAAAAAACSR1AAA4JrRvKlN//vpqE6XlCu/4LTK9+Wdt76PpWG15IWfr7e8vJh9AQAAAAAA3IOkBgAA1wiTyaTWrZpo155cp3IvL1PVpt1+PrL6/7IHhp+PvL35mIDLw9ZtAMB7IQAAQG3j2woAAK4hYS2CZBiGjp/IV2jzJrL6N5KfrzeboKFWmc1mSVJ5ebkaNWrk5mgAwL3Ky8sl/f/3RgAAAFwekhoAAFxDTCaTwls2VqC1gWw2G8kMXBENGzaUxWKR3W6X1Wrl/zMA1yzDMGS322WxWNSwIftPAQAA1AaSGgAAAKh1TZo00aFDh3Tw4EHZbDY1bNiQ5EYtMwxDFRUVMpvNPFugjjEMQ+Xl5bLb7SoqKlKLFi3cHRIAAEC9QVIDAAAAtS4gIECS9PPPP+vQoUNujqb+qqyslJeXl7vDAHAOFotFLVq0cLwnAgAA4PKR1AAAAMAVERAQoICAAJWXl6uiosLd4dQ7hmGosLCQJb6AOspsNrPkFAAAwBVAUgMAAABXVMOGDfli7wowDEOlpaXy8fEhqQEAAADgmsFcdQAAAAAAAAAA4BFIagAAAAAAAAAAAI9AUgMAAAAAAAAAAHgEkhoAAAAAAAAAAMAjkNQAAAAAAAAAAAAegaQGAAAAAAAAAADwCA3cHYCnMAxDklRQUODmSAD3MQxDBQUFMplMMplM7g4HwCWiLwP1A30Z8Hz0Y6B+oC8D9QN9ue6wWq3n/W9AUsNFhYWFkqRWrVq5ORIAAAAAAAAAAOonu92ugICAcx43GWenIOC8Kisrdfjw4QtmiYD6rKCgQK1atdKBAwfO+8YCoG6jLwP1A30Z8Hz0Y6B+oC8D9QN9ue5gpkYt8fLyUsuWLd0dBlAnBAQE8OYO1AP0ZaB+oC8Dno9+DNQP9GWgfqAv131sFA4AAAAAAAAAADwCSQ0AAAAAAAAAAOARSGoAcJnFYtGUKVNksVjcHQqAy0BfBuoH+jLg+ejHQP1AXwbqB/qy52CjcAAAAAAAAAAA4BGYqQEAAAAAAAAAADwCSQ0AAAAAAAAAAOARSGoAAAAAAAAAAACPQFIDwAVNnTpVJpPJ6adDhw7uDgvABaxbt05Dhw5VaGioTCaTli1b5nTcMAw9//zzCgkJUaNGjdSvXz/t2bPHPcECqNGF+nFSUlK1MTouLs49wQI4pxkzZqh79+6yWq1q2rSpEhISlJmZ6VSnpKRE48ePV+PGjeXv76/hw4fr6NGjbooYwG+50o/vuOOOauPyuHHj3BQxgJq88cYbioqKUkBAgAICAhQdHa2VK1c6jjMeewaSGgBccuONNyo3N9fxs2HDBneHBOACiouLddNNN+lf//pXjcdfeeUVzZkzR2+++aa2bNkiPz8/DRw4UCUlJVc5UgDncqF+LElxcXFOY/TChQuvYoQAXJGWlqbx48dr8+bNWrNmjcrLyzVgwAAVFxc76jz++ONasWKFFi9erLS0NB0+fFjDhg1zY9QAfs2VfixJDz74oNO4/Morr7gpYgA1admypV566SV9++232rZtm/r06aP4+Hj9+OOPkhiPPYXJMAzD3UEAqNumTp2qZcuWKSMjw92hALhEJpNJS5cuVUJCgqSqWRqhoaGaOHGinnzySUmS3W5Xs2bNNH/+fI0YMcKN0QKoyW/7sVQ1UyM/P7/aDA4AdVteXp6aNm2qtLQ09e7dW3a7XcHBwVqwYIESExMlSbt379YNN9yg9PR03XbbbW6OGMBv/bYfS1UzNbp06aLZs2e7NzgAFyUoKEivvvqqEhMTGY89BDM1ALhkz549Cg0NVZs2bXTvvfdq//797g4JwGXIzs7WkSNH1K9fP0eZzWZTjx49lJ6e7sbIAFys1NRUNW3aVO3bt9fDDz+s48ePuzskABdgt9slVX2JIknffvutysvLncblDh06KCwsjHEZqKN+24/PSk5OVpMmTdSpUydNmjRJp06dckd4AFxQUVGhRYsWqbi4WNHR0YzHHqSBuwMAUPf16NFD8+fPV/v27ZWbm6tp06apV69e2rFjh6xWq7vDA3AJjhw5Iklq1qyZU3mzZs0cxwDUfXFxcRo2bJgiIiKUlZWlZ599VoMGDVJ6errMZrO7wwNQg8rKSj322GPq2bOnOnXqJKlqXPb29lZgYKBTXcZloG6qqR9L0qhRoxQeHq7Q0FD98MMPeuaZZ5SZmanPPvvMjdEC+K3//ve/io6OVklJifz9/bV06VJ17NhRGRkZjMcegqQGgAsaNGiQ4/eoqCj16NFD4eHh+uSTTzR27Fg3RgYAwLXt10vFde7cWVFRUbr++uuVmpqqvn37ujEyAOcyfvx47dixgz3qAA92rn78pz/9yfF7586dFRISor59+yorK0vXX3/91Q4TwDm0b99eGRkZstvtWrJkiUaPHq20tDR3h4WLwPJTAC5aYGCg2rVrp71797o7FACXqHnz5pKko0ePOpUfPXrUcQyA52nTpo2aNGnCGA3UUY888og+//xzrV27Vi1btnSUN2/eXGVlZcrPz3eqz7gM1D3n6sc16dGjhyQxLgN1jLe3tyIjI9WtWzfNmDFDN910k1577TXGYw9CUgPARSsqKlJWVpZCQkLcHQqASxQREaHmzZvr66+/dpQVFBRoy5Ytio6OdmNkAC7HwYMHdfz4ccZooI4xDEOPPPKIli5dqm+++UYRERFOx7t166aGDRs6jcuZmZnav38/4zJQR1yoH9ckIyNDkhiXgTqusrJSpaWljMcehOWnAFzQk08+qaFDhyo8PFyHDx/WlClTZDabNXLkSHeHBuA8ioqKnP4qLDs7WxkZGQoKClJYWJgee+wxvfjii2rbtq0iIiI0efJkhYaGKiEhwX1BA3Byvn4cFBSkadOmafjw4WrevLmysrL09NNPKzIyUgMHDnRj1AB+a/z48VqwYIGWL18uq9XqWJfbZrOpUaNGstlsGjt2rJ544gkFBQUpICBAjz76qKKjo3Xbbbe5OXoA0oX7cVZWlhYsWKDBgwercePG+uGHH/T444+rd+/eioqKcnP0AM6aNGmSBg0apLCwMBUWFmrBggVKTU3VV199xXjsQUyGYRjuDgJA3TZixAitW7dOx48fV3BwsGJiYjR9+nTWBAXquNTUVN15553VykePHq358+fLMAxNmTJFb7/9tvLz8xUTE6O5c+eqXbt2bogWQE3O14/feOMNJSQkaPv27crPz1doaKgGDBigF154Qc2aNXNDtADOxWQy1Vj+3nvvKSkpSZJUUlKiiRMnauHChSotLdXAgQM1d+5clrsA6ogL9eMDBw7ovvvu044dO1RcXKxWrVrprrvu0nPPPaeAgICrHC2Acxk7dqy+/vpr5ebmymazKSoqSs8884z69+8vifHYU5DUAAAAAAAAAAAAHoE9NQAAAAAAAAAAgEcgqQEAAAAAAAAAADwCSQ0AAAAAAAAAAOARSGoAAAAAAAAAAACPQFIDAAAAAAAAAAB4BJIaAAAAAAAAAADAI5DUAAAAAAAAAAAAHoGkBgAAAAAAAAAA8AgkNQAAAAA4mEwm3XHHHZfVRmpqqkwmk6ZOnVorMV3rWrdurdatW7s7DAAAAKBOIKkBAAAA1DEmk+mifoBLtWHDBplMJi1fvtxR1rx5cz344INujAoAAAA4twbuDgAAAACAsylTplQrmz17tux2e43HatOuXbvk6+t7WW3ceuut2rVrl5o0aVJLUeFKWbt2rby8vBQbGytJ2rFjh44ePao+ffq4OTIAAACgZibDMAx3BwEAAADg/Fq3bq2cnBzx8f3ac3bpqX379tV623379pXdbte2bdskSXPmzNGECRN0+PBhhYSE1Pr1AAAAgMvF8lMAAACAh9q3b59MJpOSkpK0a9cu3XXXXWrcuLFMJpPjC/ClS5dq5MiRioyMlK+vr2w2m3r16qVPP/20xjZr2lMjKSlJJpNJ2dnZmjNnjjp06CCLxaLw8HBNmzZNlZWVTvXPtafG2b0hioqKNGHCBIWGhspisSgqKkpLliw55z3+/ve/V1BQkPz9/RUbG6t169Zp6tSpMplMSk1Ndfl5ZWdn64EHHlBYWJgsFotCQkKUlJSknJyccz6HgwcPauTIkWrSpIl8fX3Vs2dPpaSk1Nj+zz//rMcee0wRERGyWCxq2rSp7rnnHu3YsaPG+mVlZZo1a5a6d+8uq9Uqf39/dezYUU888YROnjxZrf7FPLdzOXHihPbu3au9e/cqMzNT6enp6ty5s6Ns5cqVCgsLU3Fxsfbu3atDhw5dVPsAAADAlcbyUwAAAICH27t3r2677TZ17txZSUlJOn78uLy9vSVJkyZNkre3t2JiYhQSEqK8vDz9+9//VmJioubMmaNHH33U5es89dRTSktL05AhQzRw4EAtW7ZMU6dOVVlZmaZPn+5SG+Xl5RowYIBOnjyp4cOH69SpU1q0aJHuuecerVq1SgMGDHDUPXTokG6//Xbl5uYqLi5OXbt2VWZmpvr373/RyyNt2bJFAwcOVHFxsYYMGaK2bdtq3759Sk5O1sqVK5Wenq42bdo4nXPy5En17NlTwcHBeuCBB5SXl6ePP/5YcXFxWrJkiRISEhx18/LyFB0draysLN1xxx0aMWKEsrOztWTJEn3xxRf66quvFBMT46h/+vRp9e/fXxs3blTbtm01ZswYWSwW7dmzR2+99Zb+8Ic/6Lrrrruk53Y+c+bM0bRp05zK5s+fr/nz5zuVtW3bVpIUGxt7UYkjAAAA4IozAAAAANR54eHhxm8/vmdnZxuSDEnG888/X+N5WVlZ1coKCwuNzp07GzabzSguLnY6JsmIjY11Khs9erQhyYiIiDAOHz7sKM/LyzMCAwMNq9VqlJaWOsrXrl1rSDKmTJlS4z3Ex8c71U9JSTEkGQMHDnSqf9999xmSjOnTpzuVz5s3z3Hfa9eurfG+f62srMxo3bq1YbVaje+++87p2Pr16w2z2WwMGTKk2nOQZIwaNcqorKx0lH///feGt7e3ERwcbJw6dcpRPmbMGEOSMWnSJKd2vvjiC0OSERkZaVRUVDjKJ06caEgy7r//fuPMmTNO5+Tn5xuFhYWO1xf73M7nxx9/NBYvXmwsXrzYuOuuuwyz2WwsXLjQWLx4sTF9+nRDkvHMM8846qSmprrcNgAAAHA1sPwUAAAA4OGaN2+u//u//6vx2G9nH0iSv7+/kpKSZLfbtXXrVpevM3nyZKd9Fpo0aaL4+HgVFhYqMzPT5XZmzZrlmEkiVe3rEB4e7hRLaWmpFi9erKZNm2rixIlO548ZM0bt27d3+Xqff/659u3bp6eeekpdu3Z1OhYTE6P4+Hh9+eWXKigocDpmNpv197//XSaTyVEWFRWl+++/X3l5efryyy8lVS0jtXDhQjVu3FjPPfecUxuDBw9W//79tXfvXm3cuFGSdObMGb399tuy2Wx67bXXZDabnc6x2Wzy9/evdh+uPLcL6dixoxITE5WYmKiCggJ16dJFI0aMUGJiouOaEyZMcNQ5u4E4AAAAUFew/BQAAADg4W666SanL7t/7dixY3rppZe0cuVK5eTk6PTp007HDx8+7PJ1unXrVq2sZcuWkqT8/HyX2ggMDFRERESN7aSnpzteZ2ZmqrS0VLfccossFotTXZPJpNtvv93lRMrmzZsdbf52nw9JOnLkiCorK/W///1Pt9xyi6M8LCxM4eHh1er36tVL8+bN0/bt2zV8+HDt3r1bJSUluvPOO+Xr61ut/p133qk1a9YoIyNDvXr10u7du1VYWKh+/fo5LTF1Pq4+N1eVlZUpPT1dDz/8sKMsLS1Nbdu2ZYNwAAAA1GkkNQAAAAAP16xZsxrLT5w4oe7du2v//v3q2bOn+vXrp8DAQJnNZmVkZGj58uUqLS11+ToBAQHVyho0qPonRUVFhUtt2Gy2GssbNGjgtOH42VkTTZs2rbH+ue65JidOnJAkJScnn7decXGxS9c4W263251iPVf9s0mCs/XOnteiRYsLxn6Wq8/tfJYtW6aMjAxJVcmuU6dO6aeffnIketauXauQkBDH6y5dujjtGwIAAADUBSQ1AAAAAA/36+WRfm3evHnav3+/XnjhhWrLIr300ktavnz51QjvkpxNoBw7dqzG40ePHr3otlasWKEhQ4a4fN65rnG2/Gyi4Wz756p/5MgRp3qBgYGSqjZCv5qWLVum999/36ls6dKlWrp0qeP1yZMnHRuJjx49mqQGAAAA6hz21AAAAADqqaysLElSfHx8tWPr16+/2uFclPbt28tisejbb7+tNpvEMIyLWnKpR48eknTRyzTt379fOTk51crPPruz+3N06NBBPj4+2rp1q06dOlWtfmpqqqSqmQ9S1b0FBARo69atOnny5EXFdDnmz58vwzBkGIbi4uJ08803O17/85//lCQdOHDAUTZ//vyrFhsAAADgKpIaAAAAQD11dj+IDRs2OJUvWLDAscl1XWWxWJSYmKijR49q9uzZTsc++OAD7d692+W24uPjFRYWpn/84x9at25dtePl5eXVnpFUtaTWs88+K8MwHGU//PCDPvzwQwUHB2vw4MGSJG9vb40cOVI///yzZsyY4dTGqlWr9NVXXykyMlI9e/aUVLVk1EMPPSS73a4JEyZUW7rLbrerqKjI5fu7WBUVFdq4caN69+7tKFu3bp3atGnj2CMFAAAAqKtYfgoAAACop+6//369/PLLevTRR7V27VqFh4fr+++/19dff61hw4bps88+c3eI5zVjxgylpKTor3/9q9LS0tS1a1dlZmbq888/V1xcnFatWiUvrwv/nZbFYtGSJUs0aNAgxcbGqk+fPurcubNMJpNycnK0fv16NW7cuFqiJCoqShs2bFD37t3Vr18/5eXl6eOPP9aZM2f09ttvq1GjRo66L7/8stLS0vTiiy9q06ZN6tGjh/bt26fFixfL19dX7733nlOsf/vb37R582Z9+OGH2rx5swYNGiSLxaKffvpJq1at0oYNGxwzO2rbd999p8LCQqekxvr16xUXF3dFrgcAAADUJmZqAAAAAPVUy5YtlZaWpr59+yolJUVvvfWWysrKtHr1ag0dOtTd4V1Qq1atlJ6errvvvlubNm3S7NmzdezYMa1evVqRkZGSat68vCbdu3fX999/rwkTJujAgQN688039e6772r37t1KSEjQ3Llzq51z3XXXaePGjYqMjNQ777yjBQsWKCoqSqtWraq210RwcLC2bNmiv/zlL8rKytLMmTO1Zs0aJSQkaMuWLYqJiXGq7+PjozVr1mjmzJny8/PTO++8ozfeeEO7du3SuHHj1Lp160t6Zq5IS0uTyWRSr169JEl79uxRbm6uYmNjr9g1AQAAgNpiMn49lxoAAAAAPEBMTIzS09Nlt9vl7+9f6+2bTCbFxsY69sMAAAAAUDcwUwMAAABAnZWbm1ut7KOPPtLGjRvVr1+/K5LQAAAAAFB3sacGAAAAgDqrU6dO6tq1qzp27Ciz2ayMjAylpqbKarVq5syZ7g4PAAAAwFVGUgMAAABAnTVu3DitWLFC27ZtU3FxsYKDgzVq1ChNnjxZHTp0cHd4AAAAAK4y9tQAAAAAAAAAAAAegT01AAAAAAAAAACARyCpAQAAAAAAAAAAPAJJDQAAAAAAAAAA4BFIagAAAAAAAAAAAI9AUgMAAAAAAAAAAHgEkhoAAAAAAAAAAMAjkNQAAAAAAAAAAAAegaQGAAAAAAAAAADwCCQ1AAAAAAAAAACAR/h/Jr4GbvVGVXYAAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" } ], - "source": [ - "f = bf.diagnostics.plots.loss(history=history)" - ] + "execution_count": 11 }, { "cell_type": "markdown", @@ -364,51 +412,66 @@ }, { "cell_type": "code", - "execution_count": 19, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:35:17.556053Z", + "start_time": "2025-04-26T02:35:17.058765Z" + } + }, + "source": [ + "df = simulator.sample(5000, n=10)\n", + "print(f\"{df['n']=}\")\n", + "print(f\"{df['x'].shape=}\")" + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "10\n", - "(5000, 10)\n" + "df['n']=10\n", + "df['x'].shape=(30000, 10)\n" ] } ], - "source": [ - "df=simulator.sample(5000, n=10)\n", - "print(df[\"n\"])\n", - "print(df[\"x\"].shape)" - ] + "execution_count": 12 }, { - "cell_type": "markdown", "metadata": {}, - "source": [ - "To apply our approximator on this dataset, we simply use the `.predict` method to obtain the predicted posterior model probabilities, given the data and the approximator." - ] + "cell_type": "markdown", + "source": "To apply our approximator on this dataset, we simply use the `.predict` method to obtain the predicted posterior model probabilities, given the data and the approximator." }, { + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:35:17.599619Z", + "start_time": "2025-04-26T02:35:17.563524Z" + } + }, "cell_type": "code", - "execution_count": 20, - "metadata": {}, + "source": "pred_models = approximator.predict(conditions=df)", "outputs": [], - "source": [ - "pred_models=approximator.predict(conditions=df)" - ] + "execution_count": 13 }, { - "cell_type": "markdown", "metadata": {}, - "source": [ - "We inspect the model comparison calibration now." - ] + "cell_type": "markdown", + "source": "We inspect the model comparison calibration now." }, { + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:35:17.772515Z", + "start_time": "2025-04-26T02:35:17.612303Z" + } + }, "cell_type": "code", - "execution_count": 21, - "metadata": {}, + "source": [ + "f=bf.diagnostics.plots.mc_calibration(\n", + " pred_models=pred_models,\n", + " true_models=df[\"model_indices\"],\n", + " model_names=[r\"$\\mathcal{M}_0$\",r\"$\\mathcal{M}_1$\"],\n", + ")" + ], "outputs": [ { "name": "stderr", @@ -422,33 +485,38 @@ }, { "data": { - "image/png": "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3sW8R48e6NatG5RKnuxAREQklYyMDAghYGpqCqVSiR9//JG1l4ioBOAnfRF0PzQGK3+9AAAYMaAZXF3s8zx21apVaNOmDaKionTTWPCJiIiko1arMXjwYIwYMQIajQYAay8RUUnBT/siJi1dg+k//om0dA18PF3RtV3tPI0TQmDOnDmYNm0aHj58iN27d0sbKBERESEhIQH9+vXDwYMHcejQIfz333/GDomIiAoRTykvYv635SLuPnwOezsLfD2sVZ6u+9JoNJg8eTI2btwIAPj666/xxRdfSB0qERFRifb8+XP07dsX169fh42NDdatW4d69eoZOywiIipEbLiLkKv/RmDznqsAgIlDW8KxtNVbx6SlpWHkyJHYt28fFAoFZs+ejc8++0ziSImIiEq28PBw+Pr64v79+3BwcMDmzZtRv359Y4dFRESFjA13EZGYpMaMn45BCKBTm5po4V31rWOSk5PRv39/nDp1Cqampli6dCk+/vjjQoiWiIio5Lp9+zZ69eqFJ0+eoEKFCggKCoKbm5uxwyIiIiNgw11ELFrzF548S4RLOVuMGuSTpzFqtRqPHz+GlZUVfvnlF7Ro0ULiKImIiCg2NhaxsbHw8PBAUFAQnJ2djR0SEREZCRvuIuD4mXs4eOI2lEoFpox6H9aWZnkaV7p0aWzduhVRUVFo2LChxFESERERAHh7e+PXX39FrVq14ODgYOxwiIjIiHiXcpl7FpOEuT+fAgD0+6QB6td6817yR48e4ejRo7qfK1SowGabiIhIYvv370dISIju5+bNm7PZJiIiHuGWMyEEZi09gfhENWpUK4NBPRu/cflbt25h/PjxiI+Ph5WVFXx88nbqORERERXcr7/+iokTJ8LJyQkHDhxA+fLljR0SERHJBI9wy9iOA//i/NVHMDNT4bsxbWBqqsp12StXrmDUqFGIi4uDm5sbatWqVYiREhERlTxCCCxZsgQBAQEQQqBt27YoW7asscMiIiIZ4RFumQp9HItlG84CAIZ91hRVKpbOddng4GBMnToV6enpaNCgAWbNmgUbG5vCCpWIiKjE0Wq1mDFjBlauXAkAGDlyJL7++msoFAojR0ZERHLChluG0tM1mP7jMaSlaeDVoCK6fVQ312X379+POXPmQKvV4t1338X06dNhZpa3m6oRERFR/mVkZGD8+PHYtm0bAOC7777Dl19+aeSoiIhIjthwy9Da3y7j1r1nsLUxx+ThraBU5ry3/MaNG5g1axYAoEOHDpgwYQJMTPgrJSIiktKPP/6Ibdu2QaVSYcGCBejZs6exQyIiIplidyYz/9x8go07/wYABHzZAmUdcz81vE6dOujWrRtMTU0xbNgwnsZGRERUCAYPHozg4GAMGzYM7dq1M3Y4REQkY2y4ZSQpJQ3f/3gMWq1A+5bV8b6PW7ZlNBoN0tPTYWFhAYVCgdGjRwMAm20iIiIJJSUlwdraGgBga2uLXbt2sfYSEdFb8S7lMvLTL2cQ8TQe5crY4KvPm2ebn5aWhmnTpmHy5MlIT08H8LLRZsEnIiKSzqNHj9CuXTssW7ZMN421l4iI8oINt0wEX3iA3/+8CYUCmDL6fdhYm2eZn5SUhICAAJw4cQJXr17FzZs3jRQpERFRyXHr1i107twZDx48wIYNG5CYmGjskIiIqAhhwy0DMXHJmL38JACgd5d30LCOS5b5cXFx+Oqrr3D58mVYWVlh3rx5qFevnjFCJSIiKjEuX76Mrl274unTp6hevTr27NnDx24SEVG+sOE2MiEEZi07gbj4VLhXdsQXvb2yzH/69ClGjBiBkJAQlCpVCosXL4anp6eRoiUiIioZTpw4gZ49e+LFixfw9PTE7t27Ub58eWOHRURERQwbbiPbeyQEZy6HwdREie/GvA8zU5VuXlhYGIYNG4bQ0FCULVsWy5YtQ61atYwYLRERUfG3d+9e+Pn5ISUlBS1btsTWrVthb29v7LCIiKgIYsNtRI8i4vDT2jMAgC/7ecOtsmOW+ampqUhMTISrqytWrFiBypUrGyNMIiKiEiUmJgbp6eno3LkzNmzYACsrK2OHRERERRQfC2YkGRotvv/xGFLVGWhU1wU9O9XPtkz16tWxYMECVKhQAaVLlzZClERERCXPgAEDUKlSJbRq1QoqlertA4iIiHLBI9xGsmHHFfx3Jwo2Vmb4dtT7UCpfPl7k1KlTuHHjhm65unXrstkmIiKSkFarxfLlyxEbG6ub1qZNGzbbRESkNx7hNoL/7jzFum2XAQDjBr+HcmVe3vH0999/x7x582BtbY3Vq1ejQoUKxgyTyKBu3bpl7BBypdVqjR1CsREdHQ0hBJRKee7PrVGjhrFDIJlJT0/H2LFjsWPHDhw4cAC7d+82SKMt5888gJ97hsLPPCJ6GzbchSwlNR3fLz4GjVagTXM3tH3PHQCwefNmrFixAgDQokULg90JtSgUfIVCYewwiIioBEpNTcWQIUNw5MgRqFQqDBgwgEe1iajIkfuOn5KODXchW7b+LB5FvkBZB2uMH9ICALBixQps3rwZANC3b18MGTKETSgREZGE4uPj4efnh/Pnz8Pc3ByrVq1C27ZtjR0WEREVM2y4C9GZy6HYdeg/AMA3I1vDykKFOXPm4I8//gAADBs2DL179zZmiERERMVeVFQU+vTpg//++w92dnZYv349vL29jR0WEREVQ2y4C0nsixTMWnYCANCjYz00eacitmzZgj/++ANKpRITJ05Ehw4djBskFVk8lYiIKO9GjhyJ//77D2XLlsWWLVtQu3ZtY4dEJAleWkhkfPx2XgiEEJi74iRi4lJQpVJpDO33ci96t27d4O3tjcDAQDbbREREhWT27Nnw9PTEnj172GwTEZGkeIRbIhqNFtdCIvE8Nhl3HkTj1IWHMDFRYsIXTWFm9vKGLGZmZpg3b16J37NXFI7O8i6fRERFW3x8POzs7AAAVatWxd69e0t8/SWivJHzmQJ84oD8seGWwIlz97F4zV949jwpy/Sm9R0x94dJaNGiBb788ksAYLEnIiKS2PHjxzF06FCsWLECrVu3BsD6S0REhYMNt4GdOHcf38w9nG26Rh2L37dvho1FBtRqNfr06aPb007yxz2bRERF0+7duzFq1ChkZGRg8+bNuoabiOShKJzpSKQPvrMNSKPRYvGav7JNz0iJQsLjfdBmJCFNa42lS5ex2SYiIpLYunXrMHz4cGRkZKBr165Yvny5sUMiIqIShg23AV0Licx2Gnl6cjgSHv8BrUYNE4uysHDuiMjnGiNFSEREVPwJIbBw4UJMnjwZQggMHDgQS5cuhampqbFDIyKiEoanlBvQ89jkLD+nJTxA0pNjEEILUysX2Li0hUJplm05IiIiMgytVoupU6dizZo1AIBx48Zh7NixvGabiIiMgg23ATmWtsrysxAaCKGFmU1VWDu3hkKhynE5IiIiMgyFQoHExEQAQGBgIAYNGmTkiIiIqCRjw21A79RyRllHa91p5eZ27lCaWMLE0hkKxcuz950crfFOLWdjhklERFRsKRQKzJs3D927d0fz5s2NHQ4REZVwsr2GW61WY+LEiXBxcYGlpSW8vb1x5MiRPI0NDw9Hz549YW9vDzs7O3Tp0gX379+XOGJAqVSgvms0tBn/dx23qVUFXbMNAKP9m0Olkm3aiYiohCuK9ffFixeYP38+MjIyAAAmJiZstomISBZke4R7wIAB2L59O8aMGQMPDw+sW7cOHTp0wPHjx/Huu+/mOi4xMRGtW7fGixcvMHnyZJiammLRokVo2bIlrl69CkdHR0nizcjIwLx583D88H44lXEGSndAdGyqbr6TozVG+zdHq6bVJFk/ERGRIRS1+vv06VP06dMHISEhePHiBWbMmCHJeoiIiApClg33hQsXEBQUhHnz5mH8+PEAgP79+6Nu3boICAjAmTNnch27fPly3LlzBxcuXECTJk0AAB999BHq1q2LBQsW4IcffjB4vGlpaZg+fTpOnz4NlUqFLwcPRLt2H+JaSCSexybDsbQV3qnlzCPbREQka0Wt/j58+BC9e/dGaGgonJyc0KdPH4Ovg4iISB+y7AC3b98OlUqFwYMH66ZZWFjA398fZ8+exaNHj944tkmTJrpiDwA1a9ZEmzZtsG3bNoPHmpSUhAkTJuD06dMwNTVFYGAgPvroI6hUSjSqWwFt3/NAo7oV2GwTEZHsFaX6GxISgq5duyI0NBSVK1fGnj17UKtWLYOvh4iISB+y7AL//vtvVK9eHXZ2dlmme3l5AQCuXr2a4zitVovr16+jcePG2eZ5eXnh3r17SEhIMFic0dHRmDJlCq5evQpra2ssWLDgjafbERERyVlRqb///PMPunfvjqioKNSqVQt79uxB5cqVDfb6REREhiLLU8ojIyPh7Jz9Tt6Z0yIiInIcFxMTA7Va/daxNWrUyHG8Wq2GWq3W/RwfHw8A0Gg00Gg02ZYPCAjA/fv34eDggPnz58PDwwNarfYtW0ev0mq1EEIwb3pgDvXHHBpGUchjTp/lcqLRaKDVat8Yp0qlkmz9xqi/+a298fHxmDx5MpKSktCkSROsW7cOdnZ2svvdyvnvACgaf69yxxzqjznUH3OoP61WC4VC8dY6UtD6K8uGOyUlBebm5tmmW1hY6ObnNg5AgcYCwKxZszB9+vRs0+/duwcbG5ts0wcNGoTQ0FBMnjwZrq6uub4u5U6hUGT5R/nHHOqPOTSMopDHO3fuGDuEN9JqtYiJicHdu3ehVOZ8ElrNmjUlW78x6m9+a69Wq8WIESNw8uRJfPfdd3j69CmePn2ayxYZj1z/BjIVhb9XuWMO9ccc6o851J9CoXhr7QUKXn9l2XBbWlpm2dudKTU1VTc/t3EACjQWACZNmoSxY8fqfo6Pj0elSpXg5uaW7fQ6AKhWrRqWL18Od3d3SY84FGcajQZ3795lDvXAHOqPOTQM5lF/xs6hMepvfmtv5hGIoUOH8n2mB2O/14oD5lB/zKH+mEP9SZ1DWTbczs7OCA8PzzY9MjISAODi4pLjOAcHB5ibm+uWy89Y4OWe+Zz2zqtUqlyTr1Qq3zif3o451B9zqD/m0DCYR/0ZM4fGqL+svcbDPOqPOdQfc6g/5lB/UuZQljdNa9CgAW7fvq27jivT+fPndfNzolQqUa9ePVy6dCnbvPPnz6NatWqwtbU1eLxERETFAesvERGRYcmy4e7evTs0Gg1WrVqlm6ZWq7F27Vp4e3ujUqVKAICwsDDcvHkz29iLFy9mKfq3bt3CsWPH0KNHj8LZACIioiKI9ZeIiMiwZHlKube3N3r06IFJkyYhKioK7u7uWL9+PR4+fIg1a9boluvfvz9OnjwJIYRu2rBhw7B69Wp07NgR48ePh6mpKRYuXIhy5cph3LhxxtgcIiKiIoH1l4iIyLBk2XADwIYNGzBlyhRs3LgRsbGxqF+/Pn7//Xe0aNHijeNsbW1x4sQJfPXVVwgMDIRWq0WrVq2waNEilC1btpCiJyIiKppYf4mIiAxHIV7dPU1ZvHjxAvb29nj06FGud0q9d+8e3NzceJOCAmIO9ccc6o85NAzmUX95zaGtrW2xffwLa2/hYB71xxzqjznUH3Oov/zksCD1V7ZHuOUgISEBAHTXrBEREcnBixcvcmxGiwPWXiIikquC1F8e4X4DrVaLiIiIXPdkZD4rNLe98PR2zKH+mEP9MYeGwTzqL685LM5HuFl7CwfzqD/mUH/Mof6YQ/3lJ4c8wm1gSqUSFStWfOtydnZ2fIPriTnUH3OoP+bQMJhH/ZXkHLL2Fi7mUX/Mof6YQ/0xh/qTKoeyfCwYERERERERUVHHhpuIiIiIiIhIAmy49WBubo6pU6fC3Nzc2KEUWcyh/phD/TGHhsE86o85fDvmyDCYR/0xh/pjDvXHHOpP6hzypmlEREREREREEuARbiIiIiIiIiIJsOEmIiIiIiIikgAbbiIiIiIiIiIJsOHOgVqtxsSJE+Hi4gJLS0t4e3vjyJEjeRobHh6Onj17wt7eHnZ2dujSpQvu378vccTyU9Ac7ty5E76+vqhWrRqsrKxQo0YNjBs3DnFxcdIHLTP6vA9f1bZtWygUCowYMUKCKOVN3xxu3boVzZo1g7W1Nezt7eHj44Njx45JGLE86ZPHo0ePonXr1ihTpgzs7e3h5eWFjRs3ShyxvCQmJmLq1Klo3749HBwcoFAosG7dujyPj4uLw+DBg1G2bFlYW1ujdevWuHLlinQBGwlrr2Gw/uqP9Vd/rL/6Y+3Vn2zqr6BsevXqJUxMTMT48ePFypUrRbNmzYSJiYkIDg5+47iEhATh4eEhnJycxJw5c8TChQtFpUqVRMWKFUV0dHQhRS8PBc2ho6OjqFevnpgyZYpYvXq1GDVqlDAzMxM1a9YUycnJhRS9PBQ0h6/asWOHsLa2FgDE8OHDJYxWnvTJ4dSpU4VCoRA9evQQP//8s1iyZIkYMmSI2LBhQyFELi8FzeOePXuEQqEQPj4+YsmSJWLp0qWiRYsWAoBYuHBhIUVvfA8ePBAAhKurq2jVqpUAINauXZunsRqNRvj4+Ahra2sxbdo0sXTpUlG7dm1ha2srbt++LW3ghYy11zBYf/XH+qs/1l/9sfbqTy71lw33a86fPy8AiHnz5ummpaSkCDc3N9GsWbM3jp0zZ44AIC5cuKCbFhISIlQqlZg0aZJkMcuNPjk8fvx4tmnr168XAMTq1asNHaps6ZPDV5evUqWK+P7770tkwdcnh2fPnhUKhaLEFaac6JPHtm3bChcXF5Gamqqblp6eLtzc3ET9+vUli1luUlNTRWRkpBBCiIsXL+ar4G/dulUAEL/99ptuWlRUlLC3txe9e/eWIlyjYO01DNZf/bH+6o/1V3+svYYhl/rLhvs1EyZMECqVSrx48SLL9B9++EEAEGFhYbmObdKkiWjSpEm26e3atRNubm4Gj1Wu9MlhTuLj4wUAMXbsWEOGKWuGyOH06dOFq6urSE5OLpEFX58c+vr6CmdnZ6HRaIRWqxUJCQlShytb+uTR29tb1KlTJ8fp3t7eBo+1KMhvwe/Ro4coV66c0Gg0WaYPHjxYWFlZZflCVZSx9hoG66/+WH/1x/qrP9ZewzNm/eU13K/5+++/Ub16ddjZ2WWZ7uXlBQC4evVqjuO0Wi2uX7+Oxo0bZ5vn5eWFe/fuISEhweDxylFBc5ibJ0+eAADKlCljkPiKAn1zGBYWhtmzZ2POnDmwtLSUKkxZ0yeHf/75J5o0aYKffvoJZcuWha2tLZydnbF06VIpQ5YlffLYqlUr/Pvvv5gyZQru3r2Le/fuYcaMGbh06RICAgKkDLvY+Pvvv9GoUSMolVnLtZeXF5KTk3H79m0jRWZYrL2GwfqrP9Zf/bH+6o+11/gMWX9NDB1cURcZGQlnZ+ds0zOnRURE5DguJiYGarX6rWNr1KhhwGjlqaA5zM2cOXOgUqnQvXt3g8RXFOibw3HjxqFhw4bo1auXJPEVBQXNYWxsLKKjo/HXX3/h2LFjmDp1KlxdXbF27VqMHDkSpqamGDJkiKSxy4k+78UpU6bgwYMHmDlzJgIDAwEAVlZW2LFjB7p06SJNwMVMZGQkWrRokW36q/mvV69eYYdlcKy9hsH6qz/WX/2x/uqPtdf4DFl/2XC/JiUlBebm5tmmW1hY6ObnNg5AgcYWNwXNYU42b96MNWvWICAgAB4eHgaLUe70yeHx48exY8cOnD9/XrL4ioKC5jAxMREA8Pz5cwQFBcHX1xcA0L17d9SrVw+BgYElpuAD+r0Xzc3NUb16dXTv3h2ffvopNBoNVq1ahX79+uHIkSNo2rSpZHEXF4b8PJUz1l7DYP3VH+uv/lh/9cfaa3yG/Dxlw/0aS0tLqNXqbNNTU1N183MbB6BAY4ubgubwdcHBwfD398eHH36ImTNnGjRGuStoDjMyMjBq1Ch89tlnaNKkiaQxyp2+f8umpqZZjuoolUr4+vpi6tSpCAsLg6urqwRRy48+f88jRozAuXPncOXKFd0pWT179kSdOnUwevToEv+lNC8M9Xkqd6y9hsH6qz/WX/2x/uqPtdf4DFl/eQ33a5ydnREZGZlteuY0FxeXHMc5ODjA3Ny8QGOLm4Lm8FXXrl1D586dUbduXWzfvh0mJiVr31BBc7hhwwbcunULQ4YMwcOHD3X/ACAhIQEPHz5EcnKyZHHLiT5/yxYWFnB0dIRKpcoyz8nJCcDL095KioLmMS0tDWvWrEHHjh2zXP9kamqKjz76CJcuXUJaWpo0QRcjhvg8LQpYew2D9Vd/rL/6Y/3VH2uv8Rmy/rLhfk2DBg1w+/ZtxMfHZ5meuTeoQYMGOY5TKpWoV68eLl26lG3e+fPnUa1aNdja2ho8XjkqaA4z3bt3D+3bt4eTkxP2798PGxsbqUKVrYLmMCwsDOnp6WjevDmqVq2q+we8/DJQtWpVHD58WNLY5UKfv+UGDRrg2bNn2YpS5jVTZcuWNXzAMlXQPD5//hwZGRnQaDTZ5qWnp0Or1eY4j7Jq0KABrly5Aq1Wm2X6+fPnYWVlherVqxspMsNi7TUM1l/9sf7qj/VXf6y9xmfQ+puf26mXBOfOncv23LvU1FTh7u6e5Vb6oaGhIiQkJMvY2bNnCwDi4sWLumk3b94UKpVKTJw4UfrgZUKfHEZGRopq1aoJFxcX8eDBg8IKWXYKmsOQkBCxa9eubP8AiA4dOohdu3aJiIiIQt0WY9Hnfbho0SIBQKxatUo3LSUlRVSrVk3Url1b+uBlpKB5zMjIEPb29qJ69epCrVbrpickJIiKFSuKmjVrFs4GyMybHksSEREhQkJCRFpamm5aUFBQtueAPnv2TNjb2wtfX9/CCLlQsPYaBuuv/lh/9cf6qz/WXsMzZv1lw52DHj16CBMTEzFhwgSxcuVK4ePjI0xMTMTJkyd1y7Rs2VK8vr8iPj5euLm5CScnJzF37lyxaNEiUalSJeHi4iKioqIKezOMqqA5fOeddwQAERAQIDZu3Jjl3+HDhwt7M4yqoDnMCUrgc0CFKHgOk5OTRZ06dYSpqakYP368+Omnn0STJk2ESqUS+/fvL+zNMLqC5jEwMFAAEA0bNhSLFi0S8+fPF7Vq1RIAxK+//lrYm2FUS5YsETNmzBBDhw4VAMSnn34qZsyYIWbMmCHi4uKEEEL4+fkJAFmanYyMDNG0aVNhY2Mjpk+fLpYtWybq1KkjbG1txc2bN420NdJg7TUM1l/9sf7qj/VXf6y9hiGH+suGOwcpKSli/Pjxonz58sLc3Fw0adJEHDx4MMsyuX3QPnr0SHTv3l3Y2dkJGxsb0alTJ3Hnzp3CCl02CppDALn+a9myZSFugfHp8z58XUkt+Prk8OnTp8LPz084ODgIc3Nz4e3tnW1sSaFPHjdt2iS8vLyEvb29sLS0FN7e3mL79u2FFbpsVK5cOdfPtswCn1PBF0KImJgY4e/vLxwdHYWVlZVo2bJllqO5xQVrr2Gw/uqP9Vd/rL/6Y+01DDnUX4UQQuT9BHQiIiIiIiIiygveNI2IiIiIiIhIAmy4iYiIiIiIiCTAhpuIiIiIiIhIAmy4iYiIiIiIiCTAhpuIiIiIiIhIAmy4iYiIiIiIiCTAhpuIiIiIiIhIAmy4iYiIiIiIiCTAhpuIiIiIiIhIAmy4qUioUqUKFApFln/m5uZwdXWFr68vgoODjR2izrRp06BQKDBt2rQs09etWweFQoEBAwYYJS5DyG3b3uTEiRPZfncKhQK2trZ455138PXXXyMqKkq6oF+Tuf7CkLntrVq1yvfY3OJs1aoVFAoFTpw4kWV6QX43RERvw/orD6y/+cP6S3LChpuKlObNm8PPzw9+fn746KOPoNVqsW3bNrRs2RILFy40dniFJvML0MOHD40dSr5k/u769+8PT09P3Lp1C3PmzEH9+vVx8+ZNY4dXLOnzpYOIKBPr70usv5RXrL+UycTYARDlx+eff55lD3VqaiqGDBmCDRs2ICAgAJ06dUL16tWNF+AbfPLJJ2jatClKlSpl7FCMZt26dVl+vn37Ntq0aYPHjx9j8ODBOHXqlHECk6GQkJB8LT9ixAj06tULZcqUkSgiIirJWH+LNtbfvGP9JUPjEW4q0iwsLLBs2TJYW1tDo9Fg586dxg4pV6VKlULNmjXh7Oxs7FBko3r16pgxYwYAIDg4GJGRkUaOSD5q1qyJmjVr5nn5MmXKoGbNmiz4RFQoWH+LNtbf3LH+kqGx4aYiz8bGBjVq1ACALKd4vXoNztq1a9GsWTOUKlUq26lgERERGDt2LGrVqgUrKyvY2tqiSZMmWLp0KTIyMnJcZ0pKCqZNmwYPDw+Ym5vD2dkZfn5+CAsLyzXOt11DFh4ejgkTJqBevXqwtbWFtbU1qlevjgEDBuDMmTNZXiM0NBQAULVq1SzXZb1+XVFhbZs+PD09df/P3K5XcxUTE4MxY8bAzc0N5ubmWU7NysjIwM8//wwfHx+UKlUKFhYW8PDwwKhRoxAeHv7Wda9evRqenp6wtraGvb09OnTogHPnzuW47H///YepU6eiefPmqFChAszMzODo6IgPPvgA27Zte+u6kpOTMXnyZLi7u8PCwgIuLi7w9/fPNc78XuuW0zVkrVq1QuvWrQEAJ0+ezPJeqVKlCgCgZcuWUCgU2LJlS66vPXfuXCgUCvTs2TPP8RBR8cf6y/rL+sv6S2/HU8qpWIiPjwcAmJubZ5s3cuRILF++HD4+PujYsSPu37+v+yA9deoUunbtitjYWFSpUgVt27aFWq3GhQsXMHLkSOzbtw+///47TE1Nda+XnJyMNm3a4Ny5c7C2tka7du1gaWmJQ4cO4Y8//kDHjh3zHf+ff/6J7t27Iy4uDk5OTmjTpg3MzMzw8OFDbN68GQDg4+MDd3d3+Pn5Yfv27UhKSkK3bt1gY2Oje53y5cvr/i+XbXubzN8dkP33Fx0djcaNGyMuLg7vvfcePD09YWZmBgBQq9Xo1KkTjh49CgsLC7Ru3Rp2dnY4c+YMlixZgi1btuDQoUNo1KhRjusdO3YsFi9ejObNm6NLly74559/cODAARw5cgTbtm3DJ598kmX5hQsXYs2aNahZsybq1asHe3t7hIWF4fjx4/jzzz9x7ty5XK9jTEtLQ5s2bXD9+nW0atUKjRo1wunTp/HLL79g//79OHXqFDw8PPRJY47at28PCwsLHDp0COXKlUP79u118zL3xI8ePRqnTp3C0qVL0bt372yvodVqsWLFCgAvT5sjInoV6+9LrL+sv69i/aUsBFERULlyZQFArF27Ntu8a9euCaVSKQCIX375RTcdgAAg7OzsxNmzZ7ONi4yMFI6OjkKhUIjly5cLjUajmxcdHS3ef/99AUBMnz49y7jx48cLAKJmzZoiPDxcNz0pKUl06dJFt96pU6dmGbd27VoBQPj5+WWZHhYWJkqVKiUAiK+//lqo1eos858+fSqCg4NzzMeDBw9ySlehb9ubHD9+XDcuJ5nrtLCwEMnJyUKI/8sVANGmTRvx4sWLbOMmTpwoAAg3N7cseUhLSxP+/v4CgKhatWq2fGa+rqWlpfjzzz+zzJs7d64AIEqVKiWePn2aZd6JEyfEvXv3ssVx8+ZNUbFiRQFAnD9/Ptdtd3d3F6Ghobp5KSkpolu3bgKAaNq0abbXzS1nLVu2FADE8ePHs0yfOnVqjr+bzBhatmyZ7bWEECIjI0P3frpy5Uq2+fv27RMARP369XMcT0TFG+sv6+/rWH+PZ5nO+ktvw4abioScCn5cXJz4448/hJubmwAgXFxcRGJiom5+5gfm999/n+NrZhaMESNG5Dj/8ePHwtTUVJQtW1ZotVohhBDJycnC1tZWABAHDhzINiYyMlJYWFjkq+CPGTNGABAff/xxHjLx0tsKfmFv25vkVPC1Wq0IDQ0VgYGBwsTERAAQo0aN0s3PzJWpqWmORTYlJUXY2NgIAGLv3r3Z5iclJYly5coJAGLTpk1Z5mXGMmbMmBzjbdy4sQAgZs6cmedtXLlypQAgJkyYkOu27969O9u4p0+fCisrKwFA/PXXXznG+TpDF3wh/u+Ljr+/f7Z5H374oQAgVq5cmet4Iiq+WH+zYv1l/WX9pfziNdxUpAwcOFB3DYy9vT06duyIe/fuwc3NDfv374e1tXW2Md27d8/xtf744w8AgK+vb47zK1SoAA8PDzx79gx37twBAFy5cgUJCQkoU6ZMltODMpUvXx7t2rXL1zYdPHgQADB48OB8jXsTuWzb6zJ/d0qlEpUrV8a3336LjIwM9OnTB3Pnzs22fMOGDVGtWrVs0y9duoTExEQ4ODjg448/zjbfysoKvXr1AgAcP348x1j8/PxynN6/f38AyHY9HgAkJibit99+w+TJkzF48GAMGDAAAwYMwI4dOwAAt27dyvE17e3t0blz52zTnZycdLnOaX2F5fPPP4eVlRU2b96M2NhY3fS7d+/i8OHDsLe3R79+/YwWHxEZH+tv3shl217H+psV6y8VJl7DTUVK8+bN4e7uDgAwMzODk5MTmjZtivbt28PEJOe3c+bNKV53//59AMB777331vU+e/YM1atXx+PHj9/4msDLG6nkR+aNSvJzR8y3kcu2vS6zyCoUClhZWaFq1apo37496tatm+PyucWSeaOTN8Xj5uaWZdnX5TY2c3pmPjLt27cPAwcOxPPnz3Nd56vXw70q87mt+VlfYSpdujQ+++wzrFy5EmvWrMH48eMBAMuXL4cQAgMHDoSVlZXR4iMi42P9zRu5bNvrWH/zvr7CxPpbMrDhpiLl9eeA5oWlpWWO07VaLYCXe+Bz2jP/KkdHx3yt09jkum2vPwf0bXL73RUGIYTu/+Hh4fD19UVKSgoCAgLQt29fVKlSBTY2NlAqlTh8+DA+/PDDLGP0WZ8xjBo1CitXrsSKFSswduxYpKamYu3atVAoFBg+fLhRYyMi42P9zRu5bhvrb97WZwysv8UfG24qsSpVqoQ7d+5g4sSJaNy4cZ7GVKhQAUDWx5+87k3zcuLq6opbt27h5s2buqMH+pLLtkklM9YHDx7kukzmUYbMZV/34MEDNGjQINv0zG2sWLGibtq+ffuQkpKCTz75BHPmzMk2JvO0wNzkJaevrs8YateujQ8++ABHjx7FgQMHEBERgbi4OHz00Ue6oxVERIYglxrF+pt/rL+Gx/pb/PEabiqxPvroIwDI0zMcM3l6esLGxgbR0dE4fPhwtvlPnz7NcfqbZF5DtHr16jyPyXw0R27P8pTLtkmlcePGsLGxQUxMDPbu3ZttfkpKCoKCggBA9xzM123cuPGN01993mhMTAwAoHLlytmWF0LoHh2Tm7i4OOzbty/b9GfPnumuIXx1fYb0tvfKq0aPHg0AWLp0KZYtWwaAjyIhIsOTS41i/c0/1t+8Y/2lTGy4qcSaMGEC7O3tsXDhQixYsABpaWnZlnnw4AF+/fVX3c+Wlpa6m6t89dVXiIyM1M1LSUnB0KFDkZKSkq84xo4dC1tbW+zduxfffvst0tPTs8yPiorC6dOns0zL3Bv777//ynrbpGJhYaE7zWrcuHG66/AAID09HaNHj8aTJ09QtWrVXG/as2LFimw3Slm0aBEuXLgAW1tb+Pv766bXqlULALB9+/YsedFoNPjuu+9w5syZt8Y8bty4LNeJqdVqDB8+HElJSfDy8kLz5s3fvuEFkPleuXPnTrb31us6dOgAd3d3HDx4ENeuXYObm5vuyyMRkaHIpUax/uYf62/esf6SjrFuj06UH296Dmhu8IZnT2Y6efKkKFOmjAAgnJycxPvvvy/69u0rOnXqpHvcibe3d5YxiYmJwsvLSwAQNjY24uOPPxY9evQQ5cuXF46OjqJ///75eiyJEEIcOnRI90iQcuXKia5du4oePXoILy8vYWpqmm3M0qVLdev/9NNPhb+/v/D39xc3b940yra9ydueA5qTN+UqU2pqqmjTpo3umZ4dOnQQvr6+wtXVVQAQjo6O4tKlS9nGZcYyZswYoVAoRIsWLUTv3r1FvXr1BAChUqnEb7/9lmVMenq68PT01OWlY8eOomfPnqJy5crC1NRU9xiY1x/9kbntzZo1E97e3sLKykp06tRJ9OzZU7i4uOh+N6/+3l6P83X5fSyJEP/3qJUaNWqIvn37Cn9/fzFx4sQc87p48WLduhcsWJDjMkRUcrD+Zh3D+sv6y/pL+cWGm4oEqQq+EC+fxThlyhTRqFEjYWtrK8zMzETFihWFj4+PmDp1qrh+/Xq2MUlJSWLKlCnCzc1NmJmZiXLlyom+ffuKBw8e5PrB+7YiFhoaKkaPHi1q1KghLCwshI2NjahevboYNGiQOHv2bJZlNRqNmDVrlqhTp47u2Zw5FYHC2rY3kargC/GyEC9fvlw0bdpUt31ubm5i5MiR4vHjxzmOeTWWFStWiAYNGghLS0thZ2cn2rdvn+15nJkSEhLE5MmTdb8fJycn0bVrV3Hp0qVcn7X56vTExEQxYcIEUbVqVV1eBwwYIMLCwt4a56sKUvBDQ0NFnz59hLOzs+65q5UrV85xvSEhIQKAsLKyErGxsTkuQ0QlB+sv629OWH//D+svvY1CCCPfmo+IiGTj22+/xcyZMzF48GCsXLnS2OEQERGVCKy/xRcbbiIiAgBERkaidu3aiI+Px40bN3TXzhEREZF0WH+LNz4WjIiohPv6668RHh6Oo0ePIi4uDl9++SWLPRERkcRYf0sGHuEmIirhqlSpgrCwMJQvXx6+vr6YPXs2zM3NjR0WERFRscb6WzKw4SYiIiIiIiKSAJ/DTURERERERCQBNtxEREREREREEmDDTURERERERCQBNtxEREREREREEmDDTURERERERCQBNtxEREREREREEmDDTURERERERCQBNtxEREREREREEmDDTURERERERCSB/wf/yQVSElJ8IgAAAABJRU5ErkJggg==", "text/plain": [ "
" - ] + ], + "image/png": "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" }, "metadata": {}, "output_type": "display_data" } ], - "source": [ - "f=bf.diagnostics.plots.mc_calibration(\n", - " pred_models=pred_models, \n", - " true_models=df[\"model_indices\"],\n", - " model_names=[r\"$\\mathcal{M}_0$\",r\"$\\mathcal{M}_1$\"])" - ] + "execution_count": 14 }, { - "cell_type": "markdown", "metadata": {}, - "source": [ - "And the confusion matrix to inspect how often we would make an accurate decision based on picking the model with the highest posterior probability." - ] + "cell_type": "markdown", + "source": "And the confusion matrix to inspect how often we would make an accurate decision based on picking the model with the highest posterior probability." }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-26T02:35:17.851048Z", + "start_time": "2025-04-26T02:35:17.784250Z" + } + }, + "source": [ + "f=bf.diagnostics.plots.mc_confusion_matrix(\n", + " pred_models=pred_models,\n", + " true_models=df[\"model_indices\"],\n", + " model_names=[r\"$\\mathcal{M}_0$\",r\"$\\mathcal{M}_1$\"],\n", + " normalize=\"true\",\n", + ")" + ], "outputs": [ { "name": "stderr", @@ -462,23 +530,16 @@ }, { "data": { - "image/png": "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BQUF2LdvX6XteXl5KterU3E/cOXKlRrXMTMzA4Aqn4WsL+dWU3r16gWpVIqUlBScPn260varV6+K93779Okjrg8MDAQAfPvttyr3+/XXX6tcX/F5bt26FYIgVCt2Ik0xUVKde/vtt2Fvb4+YmBh8+umnKC0trVQmLS0NGzZsEH+2sLAQB928+eabuHr1qritpKQEU6dORUlJiVZxREREwMbGBtu3b0dUVBQePHigtP3atWtITExUWte0aVMAqPK5v/pybjWlWbNmGDFiBARBwOTJk5Ue9i8uLsakSZNw7949BAQEICAgQNw2fvx4WFtbIykpCV988YXSPhMSEhAbG6vyeEOGDMFTTz2F5ORkhIeHq7wPefPmTcTGxlZ58UKktboedkuGRd1zlFWBimH+/3XgwAHB2dlZACC4uLgIzzzzjDBmzBjh+eefFx878fPzU6pz584dwdfXV3zkYdCgQcKIESMENzc3wcnJSRg7dqxWj4cIgiDs3btXfDTD1dVVGDp0qDBixAjB19dXMDU1rVTnq6++Eo8/bNgwYfz48cL48eOF1NTUOjk3ddSdtyAIQnR0tNp9hoWFqfzdFxQUCF26dBEACHZ2dsLQoUOF4cOHC40aNRIACC1atFD5+Mx3330nyGQyAYDg5eUljBo1SujVq5cgkUiEN998s8q/mytXrgje3t4CAMHKykoICAgQXnzxRWHYsGGCt7e3uM9HH3Hh4yFUHWxRUr3Qq1cvnDt3DrNmzULTpk1x7NgxbN26FSkpKXB1dUV0dHSlLlErKyvs378fs2bNgqurK/bu3YuDBw+ib9++OH78uE4jQ5999lmcPXsW06dPh729Pfbs2YPdu3fj1q1bePnllzFlyhSl8lOnTsX8+fPRvHlz7Nq1C6tXr8bq1auVWoH15dxqipOTE44cOYL58+ejRYsW2LdvH3bs2AFnZ2f873//w4kTJ1SO4H3xxReRkJCA4OBgZGRk4KeffkJRURFiY2PVvoGmSZMmOHr0KGJjY+Hr64sLFy7g+++/F1v7U6ZMwd69e1U+4kKkC4kgsKOfiIioKmxREhERqcFESUREpAYTJRERkRpMlERERGowURIREanBRElERKQGEyUREZEaTJRERERqMFESERGpwURJRESkBhMlERGRGkyUREREavw/x/61+CyIJrgAAAAASUVORK5CYII=", "text/plain": [ "
" - ] + ], + "image/png": "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mTNHad2ECRMQERGBdevWITExEb///juHhhMR6Zmuo16zs7OVLpGp+4xvii1btsDT07PRgT+NaZOJUt82btyI0NBQODg4oGvXroiNjeWtIUREeqZr16um96zrMilFvYqKCuzatQtvvvmm5gH+rU12vepbp06dsG/fPlRWVuLSpUsNJlEmIqKma+5J0XWZlKJeXFwcqqur8fTTT2t9Xh2iRUlERM1P28l2dLmNUttJKept2bIFEydOFKc51QYTJRER6UVLzMyj7aQUAHDx4kUcPXoU33//vdbHA5goiYjIwCxYsAALFixQuS0pKanBun79+qEpc+swURIRkV601BR2LY2JkoiI9IKTohMREanTEqN5WgETJRER6UWHblE++OCDOh9AIpGofVAyERG1D9pOdK7LpOitQaNEqWoUkaYM5RsDERE1TYcezHPw4MHmjoOIiAxch+56DQwMbO44iIjI0GnZojSQnteOMdcrERGRrpo86vWPP/7A8ePHUVhYiIEDB2L8+PEAbk9UW1tbCxMTkyYHSUREbV977XrVuUWZnZ2NoKAgeHp6Yu7cuVi6dCni4+PF7Zs2bYKZmRlHvBIRdRAS/DOgR6OltQPWkE6J8vr16wgMDMRPP/2EgQMHYv78+Q3m0XviiScglUrxzTff6CVQIiJq2yQ6/BgCnRLlO++8g4yMDLzyyiv47bffsH79+gZl7Ozs4OnpiaNHjzY5SCIiavua+3mUrUWnRLlnzx64u7vj7bffVnuiPXv2RF5ens7BERGR4dCq21XbEbKtSKdEmZmZiXvvvbfBM7/+zcTEBNevX9cpMCIiMizttUWp06hXuVyOsrKyu5bLysqCjY2NLocgIiID015n5tGpRenh4YFTp06hoqKi0TJFRUX47bffMHjwYJ2DIyIiw9FeW5Q6JcrHH38c165dQ3h4OBQKhcoyr776KiorKzF16tQmBUhERNSadOp6ff755/HJJ59g8+bNOHnyJCZNmgQA+OuvvxAdHY24uDikpKTA29sbM2bM0Ge8RETUVvF5lP+Qy+U4cOAApkyZguPHj+P06dMAgKNHj+Lo0aMQBAH33Xcf4uPjYWxsrNeAiYioberQj9lSpXPnzjh69CgOHDiAvXv3Ii0tDQqFAm5ubhgzZgwmTJhgMP3PRETUdO11ME+T53oNDg5GcHCwPmIhIiID1l7nem1y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2KIkIiJSg9coiYiI1GCiJCIiUkfbeekMI0/qNtcrERFRR9HkFmVJSQl+/fVXFBYWonv37ggICNBHXEREZGDaa9erzi3KsrIyzJo1C05OTggODsbTTz+NzZs3i9s3b96MLl264JdfftFLoERE1LY199NDWotOibKqqgojRozA1q1bYWdnhzFjxkAQBKUyjz76KAoKChAfH6+POImIqI2rb1FqsxgCnbpeo6Ojcfr0aUybNg0ff/wxLCwsGjxiy8XFBf3798fBgwf1EigREbVt7fU+Sp1alLGxsXBxccGWLVtgYWHRaLm+ffsiJydH5+CIiMhwSKBli9JAOl91SpR//fUXfH19IZfL1ZYzNzdHUVGRToEREZFhqW9RarMYAp26XmUyGW7dunXXcjk5OWpbnERE1H6011GvOiXKXr164bfffkNtbS2MjFTvory8HL///rtWT5EmIiLDJdFyUvR23fU6fvx4XLlyBatWrWq0zKpVq1BSUoLHHntM5+CIiIham04typdeegnbtm3DypUrcebMGTzxxBMAgIKCAuzevRtffPEF4uLi4O7ujnnz5uk1YCIiapva66hXnRKlra0tEhISMH78eHzzzTf49ttvIZFIkJCQgISEBAiCgO7du+Pbb7/lNUoiog6C1yj/ZcCAATh37hy2b9+Offv2IS0tDQqFAm5ubhgzZgzmzJkDc3NzfcZKRERtGFuUKsjlcsybN4/dq0RExBYlERGROmxREhERqdU+H0ip0+0hPXv21Hjp1auXvmMmIqI2qKVm5tmwYQPc3d0hl8vh5+eHlJQUjert2rULEokEEydO1Op4OrUoMzIy7lpGIpFAEASD6YMmIqIm0vaJIDrkh9jYWISHhyMmJgZ+fn5Yu3YtgoODcfHiRTg5OTVaLyMjA6+88goeeOABrY+pU4syPT1d5fLXX3/hp59+Qnh4OIyMjLB8+XKkpaXpcggiIqIGoqOjMXv2bISFhWHAgAGIiYmBubk5tm7d2miduro6TJ8+HStWrEDPnj21PqZOLcru3bs3uq1Hjx4YMWIE/Pz8MG3aNAQGBqotT0RE7cPt7lRtRr3e/m9paanSelNTU5iamjYoX1NTg5MnT2Lx4sXiOqlUiqCgICQnJzd6nDfffBNOTk6YOXMmjhw5onF84jG0rqGhKVOmoH///oiKimquQxARURui6zVKNzc32NjYiEtjeaOoqAh1dXVwdnZWWu/s7Iz8/HyVdY4ePYotW7Zg06ZNOp9Xs4567d+/P3744YfmPAQREbURut5HmZ2dDWtra3G9qtakLsrKyvDMM89g06ZNcHR01Hk/zZooc3NzUVNT05yHICKiNkLXm0Osra2VEmVjHB0dIZPJUFBQoLS+oKAALi4uDcr/9ddfyMjIwLhx48R1CoUCAGBkZISLFy9qdGdGs3W9fvbZZ0hOTuZjtoiIOoj6FqU2izZMTEzg4+ODxMREcZ1CoUBiYiL8/f0blPfw8MDZs2dx5swZcRk/fjxGjhyJM2fOwM3NTaPj6tSifPbZZxvdVlZWhtTUVPzxxx+QSCRYuHChLocgIiID0xIz84SHhyM0NBRDhgyBr68v1q5di4qKCoSFhQEAQkJC4OrqiqioKMjlcgwaNEipvq2tLQA0WK+OToly+/btdy1jbW2NFStW4Omnn9blEEREZGBaYq7XqVOnorCwEMuXL0d+fj68vb2RkJAgDvDJysqCVKrfzlKdEuW2bdsa3WZiYgJXV1f4+vpCLpfrHBgRERmWlpoUfcGCBViwYIHKbUlJSWrratLQ+zedEmVoaKgu1YiIiAyOTu3TZ599Fq+99pq+YyEiIgPWUnO9tjSdEuVnn32G9PR0fcdCREQGTKLDjyHQqevVxcWFk50TEZGy9vmULd1alA899BCOHTuGW7du6TseIiIyUBJoeR+lgWRKnRJlZGQkqqurMXv2bJSVlek7JiIiMkDt9RqlzreHjB49Gv/73/+wd+9eBAUFwd3dHWZmZg3KSiQSLFu2rMmBEhFR29ZSt4e0NI0S5YMPPojRo0eLI10jIyPFE7x27RpiY2Mb1Lnzwc1MlERE7V9LzMzTGjRKlElJSXB3dxd/X758ucF8EyAiopbRoVuU/xYZGannMIiIiNqmZn3MFhERdRza3htpKKNemSiJiEgvOvQ1SiIiortpr9coNb6P8pNPPoFMJtN6MTJiLiYi6hC0vYfSMPKk5i1KQRCaMw4iIjJw7bVFqXGiHD16NF5//fXmjIWIiAxYh79G6eLigsDAwOaMhYiIDFh7HfWq01yvREREHQVH2hARkV50+GuUREREarXT51EyURIRkV506BalQqFo7jiIiMjAdfhRr0REROq01xYlR70SEZFeSHRYdLFhwwa4u7tDLpfDz88PKSkpjZbdvXs3hgwZAltbW1hYWMDb2xuffvqpVsdjoiQiIr2ob1Fqs2grNjYW4eHhiIiIwKlTp+Dl5YXg4GBcvXpVZXl7e3ssWbIEycnJ+P333xEWFoawsDAcOHBA42MyURIRkV5oM8+rttcz60VHR2P27NkICwvDgAEDEBMTA3Nzc2zdulVl+REjRuCxxx5D//790atXLyxcuBCDBw/G0aNHNT4mEyUREbWq0tJSpaW6ulpluZqaGpw8eRJBQUHiOqlUiqCgICQnJ9/1OIIgIDExERcvXsTw4cM1jo+JkoiI9ELXrlc3NzfY2NiIS1RUlMr9FxUVoa6uDs7OzkrrnZ2dkZ+f32hcJSUlsLS0hImJCcaOHYt169bhoYce0vi8OOqViIj0QgLtRrLWl8zOzoa1tbW43tTUVK9xWVlZ4cyZMygvL0diYiLCw8PRs2dPjBgxQqP6TJRERKQXut5HaW1trZQoG+Po6AiZTIaCggKl9QUFBXBxcWm0nlQqRe/evQEA3t7euHDhAqKiopgoSf++3x+H7+I/R0nxNXRz74PQWS+jd5+Bd613/Oj3WB+9DD6+w/HyotXiekEQ8OWuj3Hwhz2oqCxHX4/BeHbOa+jcpVtznga1ccP9+mDUA/1hbWmG3PwbiPvuJDJzrjVa3kxujHEPecFroBvMzUxwo7gCX+49hT8u5em8T9JNcz89xMTEBD4+PkhMTMTEiRMB3J4QJzExEQsWLNB4PwqFotHroKoY1DXKf3+LUKexocKkm+SjP+Czbe9j0hMz8d93P0E39954+82FKCm+rrZe4dU87Nj+ATwGeDfY9u3Xn+LA3i/w7LzXsfLtLZCbyvH2yoWoqdH8DUzty72e3fDYI/di/0/n8M6G/cjNL8bzM0bC0kJ1V5xMJsWCsAdhb2eBLTuOYOV732HH1ykoKa3UeZ+ku5YY9RoeHo5Nmzbhk08+wYULFzB//nxUVFQgLCwMABASEoLFixeL5aOiovDDDz8gLS0NFy5cwJo1a/Dpp5/i6aef1viYBpUo58yZgytXrty13JEjRzBr1qwWiKjj2PftTox8aAJGjBqHrm49MXPuIpiaynHop28braOoq8OG9yIw+ck5cHJ2VdomCAISvtuFiY+HYYhvILq598H8/0Si+HoRTqQcau7ToTbqwaEeOH7iL/x8Kg35haXYtScFNbdq4e/TS2V5f5+eMDczwcefHUZaVhGuF1fgz4yryM0v1nmfpLuWuI9y6tSpePfdd7F8+XJ4e3vjzJkzSEhIEAf4ZGVlKeWJiooKPPfccxg4cCCGDh2Kr776Cp999plWOcKgul5PnTqFw4cPY+rUqY2WycjIwOTJkzFu3LgWjKx9q711C+l/pWL8pFBxnVQqxaDB9+HyxbON1tsdtwXWNnYYGTQeFy+cUdp2tSAPxcXXMMjLV1xnbmGJXn0G4vLFswgY9rDez4PaNplMCrcu9vj+0B/iOkEALv6Zjx7dHFXW8fToivTsIkwdfx88+7uivKIaJ37LwA+HL0AQBJ32SU3QQk8PWbBgQaNdrUlJSUq/r1q1CqtWrdLtQH8zmBbl1atXkZOTg0OHGm9tlJeXY8qUKSgsLISrq2uj5dSprq5ucE9PR1dWVgyFog42tvZK621s7VHcSNdr6oUzSPrxG8x67g2V20uKb18fsrFpuM+SG+q7c6l9sjQ3hUwmRVn5TaX1peU3YW0pV1nHwd4C9wzsBolEgg8/SULCwXMYNaw/Ro8cqPM+SXct0aJsDQaTKOvn8ktMTFS5XRAEzJ07FxMmTAAAdO3aVafjREVFKd3P4+bmplvAHVhVVQU+fD8Ss557A9bWtq0dDrVjUokEZRU3sTM+Bdl5N3DqbBYOJJ3HMN8+rR0atSMG0/V67NgxeHp64uzZs8jKykK3bsojI5cuXYrJkyeLA350bVEuXrwY4eHh4u+lpaUdPllaWdlCKpU1GLhTUnwdtv9qZQJAQX4uCq9ewbtvvSKuE4Tbj2p7+vEArFn/BWxsHW7vo+Q67Oz/6QIrKb6O7j34IdcRlVdWo65OAat/tfSsLeUo/VeLsF5JWRXq6gQIgiCuyy8sgY2VGWQyqU77pCbQdoCOYTQoDatFuWjRIgDAd999p7Tt888/h1wux6RJk5CTkwNAuUX54Ycf4t5774WxsTEiIyPVHsfU1FS8p0fTe3vaOyNjY/To5YHzv/8qrlMoFDj/+6/o08+zQfkurt3xzns7ELXmU3G5974HMGCQD6LWfAoHB2c4OXeBra2D0j4rK8vx1+XzKvdJ7V9dnQLZedfRr9c/s65IJEDfXi5IzypSWSctswidHCyVPpydHKxRUlqJujqFTvsk3Ul0+DEEBtGiLCkpgbGxMcaMGQOZTIa4uDg899xzAG4n0AMHDuCTTz4BADFR3tmi7Ny5MyIjI7Fjx46WD76deGTcNMSsexM9e/dHrz4DsP/bXbhZfROBDz4KANj4fiTsHTrhyaefh4mJKdy6K48otLCwAgCl9aMffRJff7kNLp3d0Mm5C+J2fgRbe0cM8Q1suROjNuWnY6l4ZrI/snKvIyPnGkYG9IOpiRF+PpkGAHjmcX+UlFbim+9/AwAcSbmM4ff3xeNjfXAo+RI6OVrh4REDcCj5ksb7JP1pr8+jNIhEuX//fowePRp2dnbw8/PD4cOHkZmZCSMjI6xYsQJxcXHiC56TkwO5XA5Hx3+68+pvTN23b19rhN8u+A97CKWlxfhy58coLr6G7j36YtGytWIX6rWiAkil2nVQjHvsGVRXV2FzTBQqK8rRt78XFi17HyYmvL+tozp1NguWFnKMHTUYVlZy5F65gQ3bD6Ks4nY3qb2NuVI3a3FJJTZuP4hJj9yLxS88guLSSiQdv4gfDl/QeJ+kP7rOzNPWSYQ733Vt1JNPPono6Gh06dIFmzZtwpw5c7BgwQKkpaUhJiZG6Rpiv379UFtbi7/++qvBfubNmwcXF5e7dr/eqbS0FDY2Ntj8WSLMzS31cTpEjTp24s/WDoE6gJrqSmxaMxslJSV6ubxU/zl56Pg5WFpaaVyvvLwMgQGD9BZHc2mT1yhra2uxfft2VFRUID09HTKZDF26dAEATJs2DVZWVvj444+xZMmSBgNt8vLyGswsT0REza8lZuZpDW2y6zUlJQVhYWHYsWMHCgsLla4tWlpa4qmnnoKfnx8CAgKU6ikUClRVVSE1NRXx8fGoqqrCtGnTWjp8IqIOidcoW9CgQYPg4+OD06dPY/369ejfv7/S9vfff1/lY1ikUik2btyIHTt24Pvvv8fy5ctbKmQiog6vhSbmaXFtMlFaW1vjxIkTjW5X96yyOXPmYM6cOUrramtrUVtbi7q6OtTW1uLmzZswNjaGTCbTW8xERNQ+tclrlPq2atUqmJmZYfPmzfjvf/8LMzMzfPrpp60dFhFRu8Ip7AxYZGQkBEFQWmbMmNHaYRERtSvtNVG2ya5XIiIyPO31PkomSiIi0guOeiUiIlKnnQ57ZaIkIiK90Haic06KTkREHQqvURIREakhgZbXKA2kRdkhbg8hIiLSFVuURESkF+x6JSIiUoO3hxAREanBRElERHQXhpH6tMPBPEREpBctNdfrhg0b4O7uDrlcDj8/P6SkpDRadtOmTXjggQdgZ2cHOzs7BAUFqS2vChMlERHpRf1gHm0WbcXGxiI8PBwRERE4deoUvLy8EBwcjKtXr6osn5SUhGnTpuHgwYNITk6Gm5sbHn74YeTm5mp8TCZKIiLSi5ZoUUZHR2P27NkICwvDgAEDEBMTA3Nzc2zdulVl+c8//xzPPfccvL294eHhgc2bN0OhUCAxMVHjYzJREhGRXujaoiwtLVVaqqurVe6/pqYGJ0+eRFBQkLhOKpUiKCgIycnJGsVYWVmJW7duwd7eXuPzYqIkIqJW5ebmBhsbG3GJiopSWa6oqAh1dXVwdnZWWu/s7Iz8/HyNjvX666+jS5cuSsn2bjjqlYiI9ELX20Oys7NhbW0trjc1NdV7bADw9ttvY9euXUhKSoJcLte4HhMlERG1Kmtra6VE2RhHR0fIZDIUFBQorS8oKICLi4vauu+++y7efvtt/Pjjjxg8eLBW8bHrlYiI9OL2dUdtBvNot38TExP4+PgoDcSpH5jj7+/faL3/+7//w8qVK5GQkIAhQ4ZofV5sURIRkV60xMw84eHhCA0NxZAhQ+Dr64u1a9eioqICYWFhAICQkBC4urqK1znfeecdLF++HDt27IC7u7t4LdPS0hKWlpYaHZOJkoiI9KIlJkWfOnUqCgsLsXz5cuTn58Pb2xsJCQniAJ+srCxIpf90ln744YeoqanB448/rrSfiIgIREZGanRMJkoiItKLlprrdcGCBViwYIHKbUlJSUq/Z2Rk6HSMOzFREhGRXrTXx2xxMA8REZEabFESEZFeSP7+0aa8IWCiJCIivWivXa9MlEREpBd8cDMREZEaTJRERERqsOuViIhIDQm0bFFyMA8REXUkkr8XbcobAiZKIiLSj3ba98oJB4iIiNRgi5KIiPRDywalofS9MlESEZFe8PYQIiIiNdrpJUomSiIi0g+2KImIiNTgpOhERERqsOuViIhIDXa9EhERqdFeW5SccICIiEgNtiiJiEgv2PVKRESkRnvtemWiJCIiveDtIURERGq0165XDuYhIiK9qO961WbRxYYNG+Du7g65XA4/Pz+kpKQ0Wvb8+fOYPHky3N3dIZFIsHbtWq2Px0RJRET60QKZMjY2FuHh4YiIiMCpU6fg5eWF4OBgXL16VWX5yspK9OzZE2+//TZcXFx0Oi0mSiIi0ouWaFFGR0dj9uzZCAsLw4ABAxATEwNzc3Ns3bpVZfn77rsPq1evxpNPPglTU1OdzouJkoiI9KL+GqU2CwCUlpYqLdXV1Sr3X1NTg5MnTyIoKEhcJ5VKERQUhOTk5GY7LyZKIiJqVW5ubrCxsRGXqKgoleWKiopQV1cHZ2dnpfXOzs7Iz89vtvg46pWIiPRC8veiTXkAyM7OhrW1tbhe1y7S5sJESUREeqHr7SHW1tZKibIxjo6OkMlkKCgoUFpfUFCg80AdTbDrlYiI9KK5B/OYmJjAx8cHiYmJ4jqFQoHExET4+/vr+Wz+wRYlERHpRVlZmVYtyrKyMq2PER4ejtDQUAwZMgS+vr5Yu3YtKioqEBYWBgAICQmBq6ureJ2zpqYGf/zxh/jv3NxcnDlzBpaWlujdu7dGx2SiJCKiJjExMYGLiwvc3Ny0ruvi4gITExONy0+dOhWFhYVYvnw58vPz4e3tjYSEBHGAT1ZWFqTSfzpL8/LycM8994i/v/vuu3j33XcRGBiIpKQkjY4pEQRB0DjCDqi0tBQ2NjbY/FkizM0tWzscaueOnfiztUOgDqCmuhKb1sxGSUmJRtcGNXHz5k3U1NRoXc/ExARyuVwvMTQXtiiJiKjJ5HJ5m094uuJgHiIiIjWYKImIiNRgoiQiIlKDiZKIiEgNJkoiIiI1mCiJiIjUYKIkIiJSg/dR3kX9fAxVlRWtHAl1BDXVla0dAnUANdVVAP75fCP1ODPPXeTk5Og0LRMRUVuXnZ2Nrl27tnYYbR4T5V0oFArk5eXByspKq8l+O7rS0lK4ubk1eM4ckb7xvaY9QRBQVlaGLl26KM2LSqqx6/UupFIpv3E1gabPmSNqKr7XtGNjY9PaIRgMfpUgIiJSg4mSiIhIDSZKahampqaIiIiAqalpa4dC7Rzfa9TcOJiHiIhIDbYoiYiI1GCiJCIiUoOJkoiISA0mSiIiIjWYKImIiNRgoqQmKSgo0Ljs1atXmzESau/4XqPWwkRJTTJnzhxcuXLlruWOHDmCWbNmtUBE1F7xvUathYmSmuTUqVM4fPiw2jIZGRmYPHkyOnXq1EJRUXvE9xq1FiZK0tnVq1eRk5ODQ4cONVqmvLwcU6ZMQWFhIVxdXVswOmpP+F6j1sRESTpLSUkBACQmJqrcLggC5s6diwkTJgAAn8JCOuN7jVoTEyXp7NixY/D09MSlS5eQlZXVYPvSpUsxefJkODg4AAC/5ZPO+F6j1sRESTpLSUnBokWLAADfffed0rbPP/8ccrkckyZNQk5ODgDlb/mFhYUYO3YsLCws0K9fv0ZbCkRA095rH374Ie69914YGxsjMjKyxWKm9oOJknRSUlICY2NjjBkzBjKZDHFxceK2lJQUHDhwAEuXLgUA8cPrzm/5zz//PFxcXFBYWIjVq1fjiSeewPXr11v2JMggNPW91rlzZ0RGRmLy5MktGzi1G0atHQAZpv3792P06NGws7ODn58fDh8+jMzMTBgZGWHFihWIi4uDRCIBcPvDSy6Xw9HREcDtQRfx8fFIS0uDubk5xo8fD09PT+zZswdhYWGteVrUBjXlvQYAEydOBADs27evNcKndoAtStJJfHw8nnjiCQDAjBkzoFAo8O6772LOnDmIiYmBubm5WDYnJwddunQRf798+TIsLS2Vusc8PT1x/vz5ljsBMhhNea8R6QMTJWmktrYW27dvR0VFBdLT0yGTycQPpGnTpsHKygoff/wxlixZAjc3N6W6eXl5cHZ2Fn8vLy+HtbW1Uhlra2uUl5c3/4lQm6fP9xqRPrDrlTSSkpKCsLAw7NixA4WFhdixY4e4zdLSEk899RT8/PwQEBCgVE+hUKCqqgqpqamIj49HVVUVPDw8UFpaqlSutLQUlpaWLXIu1Lbp8702bdq0lg6f2iG2KEkjgwYNgo+PD06fPo1Fixahf//+Stvff/99ldcXpVIpNm7ciMGDB+P777/HyJEj0adPH5SXlyM3N1csd+7cOQwcOLDZz4PaPn2+14j0QSIIgtDaQVDHM2XKFNjY2GDdunVITExEaGgoLl++DHt7+9YOjdqZ2tpa1NbW4oUXXoCzszOWLl0KY2NjyGSy1g6NDARblNQqNm7ciLy8PDg4OCA8PByxsbFMktQsVq1aBTMzM2zevBn//e9/YWZmhk8//bS1wyIDwhYlERGRGmxREhERqcFESUREpAYTJRERkRpMlERERGowURIREanBRElERKQGEyUREZEaTJSkFXd3d0gkEqXF1NQU3bp1w9SpU3HkyJHWDlEUGRkJiUTS4GG927dvh0QiwYwZM1olLn1o7Nzak/r3lz4kJSVBIpFgxIgRetkfdSxMlKSToUOHIjQ0FKGhoRgzZgwUCgW++OILBAYGIjo6urXDazH1XxwyMjJaOxQiaiZ8egjpZNasWUotsps3b2Lu3Ln43//+h9deew2PPvoo+vbt23oBqvHYY4/h/vvvh42NTWuHQkQGgC1K0gu5XI4NGzbAwsICdXV12L17d2uH1CgbGxt4eHigc+fOrR0KERkAJkrSG0tLS/Tr1w8AlLoi77zWtG3bNvj7+8PGxqZBl2VeXh7Cw8PRv39/mJubw8rKCvfddx/Wr1+P2tpalcesqqpCZGQk+vTpA1NTU3Tu3BmhoaHIyspqNM67XaPMzc3Fq6++Ck9PT1hZWcHCwgJ9+/bFjBkzcPz4caV9ZGZmAgB69OihdN02KSlJaZ8tdW7q3HneJSUlCA8Ph7u7O+RyOfr06YN33nkHCoVCfA3mzp0LNzc3mJqaol+/fli3bl2j+66srMTbb7+Ne++9F1ZWVjA3N8fAgQOxdOlS3Lhxo9F6ycnJGDNmDGxtbWFpaYkhQ4Zg69atdz2XqqoqrFmzBvfffz9sbW0hl8vRr18/vPbaa7h27Zr2Lw6RGux6Jb2qfyCzqalpg20vvPACNm7ciICAAIwdOxZpaWliAj18+DAmTpyIGzduwN3dHQ899BCqq6uRkpKCF154Ad9++y2+++47GBsbi/urrKzEqFGj8PPPP8PCwgIPP/wwzMzMcODAAezduxdjx47VOv7ExEQ8/vjjKC4uhpOTE0aNGgUTExNkZGSIDxAOCAhA7969ERoaii+//BIVFRWYPHmy0oOnXVxcxH+3lXOrV1xcDH9/f1y7dg0PPPAAysrKcOTIESxatAg5OTl48cUXMWzYMBgbGyMgIACFhYU4fPgw/vOf/6CyshKvv/660v6uX7+OUaNG4cyZM7C2tsaDDz4IY2NjHDp0CP/973+xY8cO/PTTT3B3d1eqFxcXh2nTpqGurg6DBg2Cp6cnsrOzMWvWLJw/f77R+PPy8jB69GicPXsW9vb2uO+++2BlZYVTp05h9erViIuLQ1JSErp3767za0SkRCDSQvfu3QUAwrZt2xps++233wSpVCoAELZu3SquByAAEKytrYXk5OQG9a5cuSI4ODgIEolE2Lhxo1BXVyduKyoqEh588EEBgLBixQqleq+88ooAQPDw8BByc3PF9RUVFcKECRPE40ZERCjV27ZtmwBACA0NVVqflZUl2NjYCACERYsWCdXV1UrbCwoKhCNHjqh8PdLT01W9XC1+burUnzcAYdy4cUJFRYW47eTJk4KRkZEglUqFAQMGCPPmzRNu3bolbo+Pjxf/hnfWEwRBmDp1qgBA8PPzE4qKisT1ZWVlwpgxYwQAQkBAQIPXxcrKSgAgREdHK2378ccfBblcLsZ6J4VCIQwdOlQAIMycOVMoLS0Vt926dUt4+eWXBQDCyJEjleodPHhQACAEBgZq/HoR1WOiJK2oSpTFxcXC3r17hV69egkAhC5dugjl5eXi9voPvDfffFPlPl9//XUBgLBgwQKV23NycgRjY2OhU6dOgkKhEARBECorK8UP2v379zeoc+XKFfHDVtNE+eKLL4pJRFN3S5QtfW7q1J+3paWlUFBQ0GD7+PHjBQBCt27dhKqqqgbbPT09BQDCoUOHxHWZmZmCVCoVJBKJ8Ntvv6k8v/pYjx07Jq5ftWqVAEC4//77Vca6cOFClYly//79AgDB29tbKZHXq6urEwYNGiQAEM6ePSuuZ6KkpuA1StJJWFiYeD3O1tYWY8eOxV9//YVevXph3759sLCwaFDn8ccfV7mvvXv3AgCmTp2qcrurqyv69OmDwsJCXL58GQBw6tQplJWVwdHREaNHj25Qx8XFBQ8//LBW55SQkAAAmDNnjlb11Gkr53YnHx8fODk5NVjfp08fAMDIkSMhl8sb3Z6XlyeuO3z4MBQKBe655x4MHjy4QR1XV1cEBwcDAA4ePCiur7+GO336dJUxhoaGqlxf/3pOnjwZRkYNrxxJpVIMHz4cAMTryURNxWuUpJOhQ4eid+/eAAATExM4OTnh/vvvx+jRo1V+gAFocI2qXlpaGgDggQceuOtxCwsL0bdvX+Tk5KjdJ3B7gI026gfmeHh4aFVPnbZybnfq1q2byvX111gb225lZQXg9q1A9XJzc+8aT69evZTKAhDPsbF6ja2vfz2XLVuGZcuWNXpM4PbrSaQPTJSkk3/fR6kJMzMzlevrR1o+/vjjKluid3JwcNDqmK2tLZ6bVKq+I+lu21tT/es5bNgwMQE3ZuDAgS0REnUATJTU6tzc3HD58mW8/vrrGDJkiEZ1XF1dAUDtjDjazpbTrVs3XLx4EampqWJruanayrk1l/pY61t6qtRvqy9b/+/U1NRGz6Ox9W5ubgCACRMm4JVXXtEhYiLttd2vjtRhjBkzBgDwxRdfaFzHx8cHlpaWKCoqwvfff99ge0FBgcr16tRfD9y0aZPGdUxMTACg0Xsh28q5NZfhw4dDKpXizJkz+O233xpsv3Llinjtd+TIkeL6wMBAAMDnn3+ucr//+9//VK6vfz3j4uIgCEKTYifSFBMltbpXX30Vtra2iI6Oxpo1a1BTU9OgTHp6Oj777DPxdzMzM3HQzUsvvYQrV66I26qqqjB//nxUVVVpFUd4eDisrKzwzTffYOnSpbh165bS9qtXr+Lo0aNK67p27QoAjd7311bOrbl069YNU6ZMgSAImDt3rtLN/hUVFZgzZw5u3ryJgIAABAQEiNtmzpwJS0tLJCcn44MPPlDaZ1JSEmJiYlQeb8KECbjvvvuQkpKCsLAwldchb9y4gZiYmEa/vBBprbWH3ZJhUXcfZWOgYpj/vx06dEhwdHQUAAhOTk7Cgw8+KEyfPl149NFHxdtO/Pz8lOqUl5cLvr6+4i0P48aNE6ZMmSK4uLgIDg4OQkhIiFa3hwiCIBw4cEC8NcPZ2VmYOHGiMGXKFMHX11cwNjZuUGf9+vXi8SdNmiTMnDlTmDlzppCamtoq56aOuvMWBEGIiIhQu8/Q0FCVf/uioiLBy8tLACDY2NgIEydOFB5//HGhU6dOAgChR48eKm+f2blzpyCTyQQAgqenpzBt2jRh+PDhgkQiEV566aVG3ze5ubmCt7e3AECwsLAQAgIChCeffFKYNGmS4O3tLe7zzltceHsINQVblNQmDB8+HOfPn8eyZcvQtWtX/Prrr4iLi8OZM2fg7OyMiIiIBl2iFhYWOHjwIJYtWwZnZ2ccOHAAhw8fxqhRo3DixAmdRoY+/PDDOHfuHBYuXAhbW1skJCRg//79KC4uxjPPPIN58+YplZ8/fz6ioqLQvXt37Nu3D1u2bMGWLVuUWoFt5dyai4ODA44fP46oqCj06NED33//Pb777js4OjrijTfewMmTJ1WO4H3yySeRlJSE4OBgZGZmYs+ePSgrK0NMTIzaJ9B06dIFP//8M2JiYuDr64uLFy/iyy+/FFv78+bNw4EDB1Te4kKkC4kgsKOfiIioMWxREhERqcFESUREpAYTJRERkRpMlERERGowURIREanBRElERKQGEyUREZEaTJRERERqMFESERGpwURJRESkBhMlERGRGkyUREREavw/fb1qqv4V3jcAAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" } ], - "source": [ - "f=bf.diagnostics.plots.mc_confusion_matrix(\n", - " pred_models=pred_models,\n", - " true_models=df['model_indices'], \n", - " model_names=[r\"$\\mathcal{M}_0$\",r\"$\\mathcal{M}_1$\"],\n", - " normalize=\"true\"\n", - ")" - ] + "execution_count": 15 } ], "metadata": { From ad011711b398ceb6650a3a16d7abdc73ea94dfe2 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:44:44 -0400 Subject: [PATCH 33/46] remove backend print --- examples/SIR_Posterior_Estimation.ipynb | 35 +++++++------------------ 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/examples/SIR_Posterior_Estimation.ipynb b/examples/SIR_Posterior_Estimation.ipynb index c7dafa37f..7963d00e5 100644 --- a/examples/SIR_Posterior_Estimation.ipynb +++ b/examples/SIR_Posterior_Estimation.ipynb @@ -11,40 +11,24 @@ ] }, { - "cell_type": "code", - "execution_count": 1, - "id": "0383ba66", "metadata": {}, + "cell_type": "code", "outputs": [], + "execution_count": null, "source": [ "import os\n", "# Set to your favorite backend\n", "if \"KERAS_BACKEND\" not in os.environ:\n", " # set this to \"torch\", \"tensorflow\", or \"jax\"\n", - " os.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\n", - "else:\n", - " print(f\"Using '{os.environ['KERAS_BACKEND']}' backend\")" - ] + " os.environ[\"KERAS_BACKEND\"] = \"tensorflow\"" + ], + "id": "5fb5c0f856b6bcf4" }, { + "metadata": {}, "cell_type": "code", - "execution_count": 2, - "id": "684f2d7e19d40e09", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-11T19:54:02.700953Z", - "start_time": "2025-04-11T19:53:33.926075Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:2025-04-21 12:41:48,425:jax._src.xla_bridge:967: An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n" - ] - } - ], + "outputs": [], + "execution_count": null, "source": [ "import datetime\n", "\n", @@ -55,7 +39,8 @@ "import keras\n", "\n", "import bayesflow as bf" - ] + ], + "id": "4a9355783f1314a" }, { "cell_type": "markdown", From a742d9c66254895f2e7ee14a2599ec737c871fda Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:45:17 -0400 Subject: [PATCH 34/46] [skip ci] turn all single-quoted strings into double-quoted strings --- examples/From_ABC_to_BayesFlow.ipynb | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/From_ABC_to_BayesFlow.ipynb b/examples/From_ABC_to_BayesFlow.ipynb index b9757d9c4..35947a4cd 100644 --- a/examples/From_ABC_to_BayesFlow.ipynb +++ b/examples/From_ABC_to_BayesFlow.ipynb @@ -479,7 +479,7 @@ "\n", "# For BayesFlow devs: this ensures that the latest dev version can be found\n", "import sys\n", - "sys.path.append('../')\n", + "sys.path.append(\"../\")\n", "\n", "import bayesflow as bf" ] @@ -513,11 +513,11 @@ "source": [ "def prior_helper():\n", " \"\"\"The ABC prior returns a Parameter Object from pyabc which we convert to a dict.\"\"\"\n", - " return dict(rate=prior.rvs()['rate'])\n", + " return dict(rate=prior.rvs()[\"rate\"])\n", "\n", "def sim_helper(rate):\n", " \"\"\"The simulator returns a dict, we extract the output at the test times.\"\"\"\n", - " temp = sim({'rate': rate})\n", + " temp = sim({\"rate\": rate})\n", " xt_ind = np.searchsorted(temp[\"t\"], t_test_times) - 1\n", " obs = temp[\"X\"][:, 1][xt_ind]\n", " return dict(obs=obs)" @@ -568,8 +568,8 @@ ], "source": [ "adapter = bf.approximators.ContinuousApproximator.build_adapter(\n", - " inference_variables='rate',\n", - " inference_conditions='obs',\n", + " inference_variables=\"rate\",\n", + " inference_conditions=\"obs\",\n", " summary_variables=None\n", ")\n", "adapter" @@ -665,25 +665,25 @@ "output_type": "stream", "text": [ "Epoch 1/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 62ms/step - loss: 0.4428 - loss/inference_loss: 0.4428 - val_loss: 0.4605 - val_loss/inference_loss: 0.4605\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 62ms/step - loss: 0.4428 - loss/inference_loss: 0.4428 - val_loss: 0.4605 - val_loss/inference_loss: 0.4605\n", "Epoch 2/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 64ms/step - loss: 0.3700 - loss/inference_loss: 0.3700 - val_loss: 0.4467 - val_loss/inference_loss: 0.4467\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 64ms/step - loss: 0.3700 - loss/inference_loss: 0.3700 - val_loss: 0.4467 - val_loss/inference_loss: 0.4467\n", "Epoch 3/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 68ms/step - loss: 0.3458 - loss/inference_loss: 0.3458 - val_loss: 0.3627 - val_loss/inference_loss: 0.3627\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 68ms/step - loss: 0.3458 - loss/inference_loss: 0.3458 - val_loss: 0.3627 - val_loss/inference_loss: 0.3627\n", "Epoch 4/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 70ms/step - loss: 0.3771 - loss/inference_loss: 0.3771 - val_loss: 0.3637 - val_loss/inference_loss: 0.3637\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 70ms/step - loss: 0.3771 - loss/inference_loss: 0.3771 - val_loss: 0.3637 - val_loss/inference_loss: 0.3637\n", "Epoch 5/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 69ms/step - loss: 0.3729 - loss/inference_loss: 0.3729 - val_loss: 0.2138 - val_loss/inference_loss: 0.2138\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 69ms/step - loss: 0.3729 - loss/inference_loss: 0.3729 - val_loss: 0.2138 - val_loss/inference_loss: 0.2138\n", "Epoch 6/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 66ms/step - loss: 0.3567 - loss/inference_loss: 0.3567 - val_loss: 0.2888 - val_loss/inference_loss: 0.2888\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 66ms/step - loss: 0.3567 - loss/inference_loss: 0.3567 - val_loss: 0.2888 - val_loss/inference_loss: 0.2888\n", "Epoch 7/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 62ms/step - loss: 0.4077 - loss/inference_loss: 0.4077 - val_loss: 0.3235 - val_loss/inference_loss: 0.3235\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 62ms/step - loss: 0.4077 - loss/inference_loss: 0.4077 - val_loss: 0.3235 - val_loss/inference_loss: 0.3235\n", "Epoch 8/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 61ms/step - loss: 0.4124 - loss/inference_loss: 0.4124 - val_loss: 0.3256 - val_loss/inference_loss: 0.3256\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 61ms/step - loss: 0.4124 - loss/inference_loss: 0.4124 - val_loss: 0.3256 - val_loss/inference_loss: 0.3256\n", "Epoch 9/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 61ms/step - loss: 0.3960 - loss/inference_loss: 0.3960 - val_loss: 0.2767 - val_loss/inference_loss: 0.2767\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 61ms/step - loss: 0.3960 - loss/inference_loss: 0.3960 - val_loss: 0.2767 - val_loss/inference_loss: 0.2767\n", "Epoch 10/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 60ms/step - loss: 0.4217 - loss/inference_loss: 0.4217 - val_loss: 0.3482 - val_loss/inference_loss: 0.3482\n" + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 60ms/step - loss: 0.4217 - loss/inference_loss: 0.4217 - val_loss: 0.3482 - val_loss/inference_loss: 0.3482\n" ] } ], @@ -829,7 +829,7 @@ "obs = observations[\"X\"][:, 1][xt_ind]\n", "\n", "# Obtain 1000 posterior samples\n", - "samples = workflow.sample(conditions={'obs': [obs]}, num_samples=num_samples)" + "samples = workflow.sample(conditions={\"obs\": [obs]}, num_samples=num_samples)" ] }, { @@ -881,9 +881,9 @@ "source": [ "# abc gives us weighted samples, we resample them to get comparable samples\n", "df, w = abc_history.get_distribution()\n", - "abc_samples = weighted_statistics.resample(df['rate'].values, w, 1000)\n", + "abc_samples = weighted_statistics.resample(df[\"rate\"].values, w, 1000)\n", "\n", - "f = bf.diagnostics.plots.pairs_posterior({'rate': abc_samples}, targets=np.array([true_rate]))" + "f = bf.diagnostics.plots.pairs_posterior({\"rate\": abc_samples}, targets=np.array([true_rate]))" ] }, { From b450961cb60bc6aef370f2695e74eb1514da00b6 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Fri, 25 Apr 2025 22:45:17 -0400 Subject: [PATCH 35/46] turn all single-quoted strings into double-quoted strings amend to trigger workflow --- examples/From_ABC_to_BayesFlow.ipynb | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/From_ABC_to_BayesFlow.ipynb b/examples/From_ABC_to_BayesFlow.ipynb index b9757d9c4..35947a4cd 100644 --- a/examples/From_ABC_to_BayesFlow.ipynb +++ b/examples/From_ABC_to_BayesFlow.ipynb @@ -479,7 +479,7 @@ "\n", "# For BayesFlow devs: this ensures that the latest dev version can be found\n", "import sys\n", - "sys.path.append('../')\n", + "sys.path.append(\"../\")\n", "\n", "import bayesflow as bf" ] @@ -513,11 +513,11 @@ "source": [ "def prior_helper():\n", " \"\"\"The ABC prior returns a Parameter Object from pyabc which we convert to a dict.\"\"\"\n", - " return dict(rate=prior.rvs()['rate'])\n", + " return dict(rate=prior.rvs()[\"rate\"])\n", "\n", "def sim_helper(rate):\n", " \"\"\"The simulator returns a dict, we extract the output at the test times.\"\"\"\n", - " temp = sim({'rate': rate})\n", + " temp = sim({\"rate\": rate})\n", " xt_ind = np.searchsorted(temp[\"t\"], t_test_times) - 1\n", " obs = temp[\"X\"][:, 1][xt_ind]\n", " return dict(obs=obs)" @@ -568,8 +568,8 @@ ], "source": [ "adapter = bf.approximators.ContinuousApproximator.build_adapter(\n", - " inference_variables='rate',\n", - " inference_conditions='obs',\n", + " inference_variables=\"rate\",\n", + " inference_conditions=\"obs\",\n", " summary_variables=None\n", ")\n", "adapter" @@ -665,25 +665,25 @@ "output_type": "stream", "text": [ "Epoch 1/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 62ms/step - loss: 0.4428 - loss/inference_loss: 0.4428 - val_loss: 0.4605 - val_loss/inference_loss: 0.4605\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 62ms/step - loss: 0.4428 - loss/inference_loss: 0.4428 - val_loss: 0.4605 - val_loss/inference_loss: 0.4605\n", "Epoch 2/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 64ms/step - loss: 0.3700 - loss/inference_loss: 0.3700 - val_loss: 0.4467 - val_loss/inference_loss: 0.4467\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 64ms/step - loss: 0.3700 - loss/inference_loss: 0.3700 - val_loss: 0.4467 - val_loss/inference_loss: 0.4467\n", "Epoch 3/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 68ms/step - loss: 0.3458 - loss/inference_loss: 0.3458 - val_loss: 0.3627 - val_loss/inference_loss: 0.3627\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 68ms/step - loss: 0.3458 - loss/inference_loss: 0.3458 - val_loss: 0.3627 - val_loss/inference_loss: 0.3627\n", "Epoch 4/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 70ms/step - loss: 0.3771 - loss/inference_loss: 0.3771 - val_loss: 0.3637 - val_loss/inference_loss: 0.3637\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 70ms/step - loss: 0.3771 - loss/inference_loss: 0.3771 - val_loss: 0.3637 - val_loss/inference_loss: 0.3637\n", "Epoch 5/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 69ms/step - loss: 0.3729 - loss/inference_loss: 0.3729 - val_loss: 0.2138 - val_loss/inference_loss: 0.2138\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 69ms/step - loss: 0.3729 - loss/inference_loss: 0.3729 - val_loss: 0.2138 - val_loss/inference_loss: 0.2138\n", "Epoch 6/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 66ms/step - loss: 0.3567 - loss/inference_loss: 0.3567 - val_loss: 0.2888 - val_loss/inference_loss: 0.2888\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m7s\u001B[0m 66ms/step - loss: 0.3567 - loss/inference_loss: 0.3567 - val_loss: 0.2888 - val_loss/inference_loss: 0.2888\n", "Epoch 7/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 62ms/step - loss: 0.4077 - loss/inference_loss: 0.4077 - val_loss: 0.3235 - val_loss/inference_loss: 0.3235\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 62ms/step - loss: 0.4077 - loss/inference_loss: 0.4077 - val_loss: 0.3235 - val_loss/inference_loss: 0.3235\n", "Epoch 8/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 61ms/step - loss: 0.4124 - loss/inference_loss: 0.4124 - val_loss: 0.3256 - val_loss/inference_loss: 0.3256\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 61ms/step - loss: 0.4124 - loss/inference_loss: 0.4124 - val_loss: 0.3256 - val_loss/inference_loss: 0.3256\n", "Epoch 9/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 61ms/step - loss: 0.3960 - loss/inference_loss: 0.3960 - val_loss: 0.2767 - val_loss/inference_loss: 0.2767\n", + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 61ms/step - loss: 0.3960 - loss/inference_loss: 0.3960 - val_loss: 0.2767 - val_loss/inference_loss: 0.2767\n", "Epoch 10/10\n", - "\u001b[1m100/100\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 60ms/step - loss: 0.4217 - loss/inference_loss: 0.4217 - val_loss: 0.3482 - val_loss/inference_loss: 0.3482\n" + "\u001B[1m100/100\u001B[0m \u001B[32m━━━━━━━━━━━━━━━━━━━━\u001B[0m\u001B[37m\u001B[0m \u001B[1m6s\u001B[0m 60ms/step - loss: 0.4217 - loss/inference_loss: 0.4217 - val_loss: 0.3482 - val_loss/inference_loss: 0.3482\n" ] } ], @@ -829,7 +829,7 @@ "obs = observations[\"X\"][:, 1][xt_ind]\n", "\n", "# Obtain 1000 posterior samples\n", - "samples = workflow.sample(conditions={'obs': [obs]}, num_samples=num_samples)" + "samples = workflow.sample(conditions={\"obs\": [obs]}, num_samples=num_samples)" ] }, { @@ -881,9 +881,9 @@ "source": [ "# abc gives us weighted samples, we resample them to get comparable samples\n", "df, w = abc_history.get_distribution()\n", - "abc_samples = weighted_statistics.resample(df['rate'].values, w, 1000)\n", + "abc_samples = weighted_statistics.resample(df[\"rate\"].values, w, 1000)\n", "\n", - "f = bf.diagnostics.plots.pairs_posterior({'rate': abc_samples}, targets=np.array([true_rate]))" + "f = bf.diagnostics.plots.pairs_posterior({\"rate\": abc_samples}, targets=np.array([true_rate]))" ] }, { From 6fa75da583fd094bf3ec00b9d1526683f6b63310 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Sun, 27 Apr 2025 13:20:49 +0000 Subject: [PATCH 36/46] [no ci] website: list example notebooks only on Examples page * one place less to update * fix one link to a notebook --- docsrc/source/index.md | 10 +--------- examples/SIR_Posterior_Estimation.ipynb | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/docsrc/source/index.md b/docsrc/source/index.md index f89c5ff3f..f0cc6c159 100644 --- a/docsrc/source/index.md +++ b/docsrc/source/index.md @@ -45,15 +45,7 @@ history = workflow.fit_online(epochs=50, batch_size=32, num_batches_per_epoch=50 diagnostics = workflow.plot_default_diagnostics(test_data=300) ``` -For an in-depth exposition, check out our walkthrough notebooks below. - -1. [Linear regression starter example](_examples/Linear_Regression_Starter.ipynb) -2. [From ABC to BayesFlow](_examples/From_ABC_to_BayesFlow.ipynb) -3. [Two moons starter example](_examples/Two_Moons_Starter.ipynb) -4. [Rapid iteration with point estimators](_examples/Lotka_Volterra_point_estimation_and_expert_stats.ipynb) -5. [SIR model with custom summary network](_examples/SIR_Posterior_Estimation.ipynb) -6. [Bayesian experimental design](_examples/Bayesian_Experimental_Design.ipynb) -7. [Simple model comparison example](_examples/One_Sample_TTest.ipynb) +For an in-depth exposition, check out our walkthrough notebooks in the {doc}`Examples <../examples>` section. More tutorials are always welcome! Please consider making a pull request if you have a cool application that you want to contribute. diff --git a/examples/SIR_Posterior_Estimation.ipynb b/examples/SIR_Posterior_Estimation.ipynb index 7963d00e5..2e3f94bd9 100644 --- a/examples/SIR_Posterior_Estimation.ipynb +++ b/examples/SIR_Posterior_Estimation.ipynb @@ -84,7 +84,7 @@ "id": "39846c15b88eaf8e", "metadata": {}, "source": [ - "As described in our [very first notebook](linear_Regression_Starter.ipynb), a generative model consists of a prior (encoding suitable parameter ranges) and a simulator (generating data given simulations). Our underlying model distinguishes between susceptible, $S$, infected, $I$, and recovered, $R$, individuals with infection and recovery occurring at a constant transmission rate $\\lambda$ and constant recovery rate $\\mu$, respectively. The model dynamics are governed by the following system of ODEs:\n", + "As described in our [very first notebook](Linear_Regression_Starter.ipynb), a generative model consists of a prior (encoding suitable parameter ranges) and a simulator (generating data given simulations). Our underlying model distinguishes between susceptible, $S$, infected, $I$, and recovered, $R$, individuals with infection and recovery occurring at a constant transmission rate $\\lambda$ and constant recovery rate $\\mu$, respectively. The model dynamics are governed by the following system of ODEs:\n", "\n", "$$\n", "\\begin{align}\n", From 5b5363cfbdeef73f0dc7d9e2d8001144031444b4 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Sun, 27 Apr 2025 13:44:43 +0000 Subject: [PATCH 37/46] ci: update pip via python -m pip pip install -U pip setuptools wheel leads to an error: https://github.com/bayesflow-org/bayesflow/actions/runs/14692655483/job/41230057180?pr=449 --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ab3d03078..eee02895c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -43,7 +43,7 @@ jobs: - name: Install Dependencies run: | - pip install -U pip setuptools wheel + python -m pip install -U pip setuptools wheel pip install .[test] - name: Install JAX From a322ff176af75ba9ba6c763a009a42095075c10f Mon Sep 17 00:00:00 2001 From: Simon Kucharsky Date: Tue, 29 Apr 2025 20:18:18 +0200 Subject: [PATCH 38/46] Adapter keeps track of the transform jacobians (#419) * minimal working case (.scale) * concatenate * keep, drop, rename * scale, log, sqrt * standardize * constraint transforms * continuous approximator returns log_prob with volume correction * loop for inverse jacobian * inverse for elementwise * inverse for Transforms * raise error with numpy transform (for now) * do not fail if no transform is used * take care of log1p as well * fix filter transforms, boundary condition * add tests for adapter jacobians * document jacobian arg * jacobian -> log_det_jac * add test for inverse concatenation * fix standardize * correct nesting in map_transform --- bayesflow/adapters/adapter.py | 45 ++++++++++---- bayesflow/adapters/transforms/concatenate.py | 34 +++++++++++ bayesflow/adapters/transforms/constrain.py | 29 +++++++++ bayesflow/adapters/transforms/drop.py | 3 + .../transforms/elementwise_transform.py | 3 + .../adapters/transforms/filter_transform.py | 30 ++++++++- bayesflow/adapters/transforms/keep.py | 3 + bayesflow/adapters/transforms/log.py | 9 +++ .../adapters/transforms/map_transform.py | 45 ++++++++++---- .../adapters/transforms/numpy_transform.py | 3 + bayesflow/adapters/transforms/rename.py | 3 + bayesflow/adapters/transforms/scale.py | 7 +++ bayesflow/adapters/transforms/sqrt.py | 6 ++ bayesflow/adapters/transforms/standardize.py | 7 +++ bayesflow/adapters/transforms/transform.py | 5 ++ .../approximators/continuous_approximator.py | 7 ++- tests/test_adapters/conftest.py | 40 ++++++++++++ tests/test_adapters/test_adapters.py | 61 ++++++++++++++++++- 18 files changed, 312 insertions(+), 28 deletions(-) diff --git a/bayesflow/adapters/adapter.py b/bayesflow/adapters/adapter.py index 4db738eef..ab6800d8a 100644 --- a/bayesflow/adapters/adapter.py +++ b/bayesflow/adapters/adapter.py @@ -79,7 +79,9 @@ def get_config(self) -> dict: return serialize(config) - def forward(self, data: dict[str, any], *, stage: str = "inference", **kwargs) -> dict[str, np.ndarray]: + def forward( + self, data: dict[str, any], *, stage: str = "inference", log_det_jac: bool = False, **kwargs + ) -> dict[str, np.ndarray] | tuple[dict[str, np.ndarray], dict[str, np.ndarray]]: """Apply the transforms in the forward direction. Parameters @@ -88,22 +90,33 @@ def forward(self, data: dict[str, any], *, stage: str = "inference", **kwargs) - The data to be transformed. stage : str, one of ["training", "validation", "inference"] The stage the function is called in. + log_det_jac: bool, optional + Whether to return the log determinant of the Jacobian of the transforms. **kwargs : dict Additional keyword arguments passed to each transform. Returns ------- - dict - The transformed data. + dict | tuple[dict, dict] + The transformed data or tuple of transformed data and log determinant of the Jacobian. """ data = data.copy() + if not log_det_jac: + for transform in self.transforms: + data = transform(data, stage=stage, **kwargs) + return data + log_det_jac = {} for transform in self.transforms: - data = transform(data, stage=stage, **kwargs) + transformed_data = transform(data, stage=stage, **kwargs) + log_det_jac = transform.log_det_jac(data, log_det_jac, **kwargs) + data = transformed_data - return data + return data, log_det_jac - def inverse(self, data: dict[str, np.ndarray], *, stage: str = "inference", **kwargs) -> dict[str, any]: + def inverse( + self, data: dict[str, np.ndarray], *, stage: str = "inference", log_det_jac: bool = False, **kwargs + ) -> dict[str, np.ndarray] | tuple[dict[str, np.ndarray], dict[str, np.ndarray]]: """Apply the transforms in the inverse direction. Parameters @@ -112,24 +125,32 @@ def inverse(self, data: dict[str, np.ndarray], *, stage: str = "inference", **kw The data to be transformed. stage : str, one of ["training", "validation", "inference"] The stage the function is called in. + log_det_jac: bool, optional + Whether to return the log determinant of the Jacobian of the transforms. **kwargs : dict Additional keyword arguments passed to each transform. Returns ------- - dict - The transformed data. + dict | tuple[dict, dict] + The transformed data or tuple of transformed data and log determinant of the Jacobian. """ data = data.copy() + if not log_det_jac: + for transform in reversed(self.transforms): + data = transform(data, stage=stage, inverse=True, **kwargs) + return data + log_det_jac = {} for transform in reversed(self.transforms): data = transform(data, stage=stage, inverse=True, **kwargs) + log_det_jac = transform.log_det_jac(data, log_det_jac, inverse=True, **kwargs) - return data + return data, log_det_jac def __call__( self, data: Mapping[str, any], *, inverse: bool = False, stage="inference", **kwargs - ) -> dict[str, np.ndarray]: + ) -> dict[str, np.ndarray] | tuple[dict[str, np.ndarray], dict[str, np.ndarray]]: """Apply the transforms in the given direction. Parameters @@ -145,8 +166,8 @@ def __call__( Returns ------- - dict - The transformed data. + dict | tuple[dict, dict] + The transformed data or tuple of transformed data and log determinant of the Jacobian. """ if inverse: return self.inverse(data, stage=stage, **kwargs) diff --git a/bayesflow/adapters/transforms/concatenate.py b/bayesflow/adapters/transforms/concatenate.py index deb54fc3f..91ea9178b 100644 --- a/bayesflow/adapters/transforms/concatenate.py +++ b/bayesflow/adapters/transforms/concatenate.py @@ -115,3 +115,37 @@ def extra_repr(self) -> str: result += f", axis={self.axis}" return result + + def log_det_jac( + self, + data: dict[str, np.ndarray], + log_det_jac: dict[str, np.ndarray], + *, + strict: bool = False, + inverse: bool = False, + **kwargs, + ) -> dict[str, np.ndarray]: + # copy to avoid side effects + log_det_jac = log_det_jac.copy() + + if inverse: + if log_det_jac.get(self.into) is not None: + raise ValueError( + "Cannot obtain an inverse Jacobian of concatenation. " + "Transform your variables before you concatenate." + ) + + return log_det_jac + + required_keys = set(self.keys) + available_keys = set(log_det_jac.keys()) + common_keys = available_keys & required_keys + + if len(common_keys) == 0: + return log_det_jac + + parts = [log_det_jac.pop(key) for key in common_keys] + + log_det_jac[self.into] = sum(parts) + + return log_det_jac diff --git a/bayesflow/adapters/transforms/constrain.py b/bayesflow/adapters/transforms/constrain.py index 5f93135a1..a4ca0be25 100644 --- a/bayesflow/adapters/transforms/constrain.py +++ b/bayesflow/adapters/transforms/constrain.py @@ -87,6 +87,11 @@ def constrain(x): def unconstrain(x): return inverse_sigmoid((x - lower) / (upper - lower)) + + def ldj(x): + x = (x - lower) / (upper - lower) + return -np.log(x) - np.log1p(-x) - np.log(upper - lower) + case str() as name: raise ValueError(f"Unsupported method name for double bounded constraint: '{name}'.") case other: @@ -101,6 +106,11 @@ def constrain(x): def unconstrain(x): return inverse_softplus(x - lower) + + def ldj(x): + x = x - lower + return x - np.log(np.exp(x) - 1) + case "exp" | "log": def constrain(x): @@ -108,6 +118,10 @@ def constrain(x): def unconstrain(x): return np.log(x - lower) + + def ldj(x): + return -np.log(x - lower) + case str() as name: raise ValueError(f"Unsupported method name for single bounded constraint: '{name}'.") case other: @@ -122,6 +136,11 @@ def constrain(x): def unconstrain(x): return -inverse_softplus(-(x - upper)) + + def ldj(x): + x = -(x - upper) + return x - np.log(np.exp(x) - 1) + case "exp" | "log": def constrain(x): @@ -129,6 +148,9 @@ def constrain(x): def unconstrain(x): return -np.log(-x + upper) + + def ldj(x): + return -np.log(-x + upper) case str() as name: raise ValueError(f"Unsupported method name for single bounded constraint: '{name}'.") case other: @@ -142,6 +164,7 @@ def unconstrain(x): self.constrain = constrain self.unconstrain = unconstrain + self.ldj = ldj # do this last to avoid serialization issues match inclusive: @@ -178,3 +201,9 @@ def forward(self, data: np.ndarray, **kwargs) -> np.ndarray: def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: # inverse means network space -> data space, so constrain the data return self.constrain(data) + + def log_det_jac(self, data: np.ndarray, inverse: bool = False, **kwargs) -> np.ndarray: + ldj = self.ldj(data) + if inverse: + ldj = -ldj + return np.sum(ldj, axis=tuple(range(1, ldj.ndim))) diff --git a/bayesflow/adapters/transforms/drop.py b/bayesflow/adapters/transforms/drop.py index 51615d632..91dcd6a28 100644 --- a/bayesflow/adapters/transforms/drop.py +++ b/bayesflow/adapters/transforms/drop.py @@ -46,3 +46,6 @@ def inverse(self, data: dict[str, any], **kwargs) -> dict[str, any]: def extra_repr(self) -> str: return "[" + ", ".join(map(repr, self.keys)) + "]" + + def log_det_jac(self, data: dict[str, any], log_det_jac: dict[str, any], inverse: bool = False, **kwargs): + return self.inverse(data=log_det_jac) if inverse else self.forward(data=log_det_jac) diff --git a/bayesflow/adapters/transforms/elementwise_transform.py b/bayesflow/adapters/transforms/elementwise_transform.py index 3bde5a1da..7d603d517 100644 --- a/bayesflow/adapters/transforms/elementwise_transform.py +++ b/bayesflow/adapters/transforms/elementwise_transform.py @@ -25,3 +25,6 @@ def forward(self, data: np.ndarray, **kwargs) -> np.ndarray: def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: raise NotImplementedError + + def log_det_jac(self, data: np.ndarray, inverse: bool = False, **kwargs) -> np.ndarray | None: + return None diff --git a/bayesflow/adapters/transforms/filter_transform.py b/bayesflow/adapters/transforms/filter_transform.py index e1920e73c..7eccf370b 100644 --- a/bayesflow/adapters/transforms/filter_transform.py +++ b/bayesflow/adapters/transforms/filter_transform.py @@ -150,9 +150,35 @@ def _should_transform(self, key: str, value: np.ndarray, inverse: bool = False) return predicate(key, value, inverse=inverse) def _apply_transform(self, key: str, value: np.ndarray, inverse: bool = False, **kwargs) -> np.ndarray: + transform = self._get_transform(key) + + return transform(value, inverse=inverse, **kwargs) + + def _get_transform(self, key: str) -> ElementwiseTransform: if key not in self.transform_map: self.transform_map[key] = self.transform_constructor(**self.kwargs) - transform = self.transform_map[key] + return self.transform_map[key] - return transform(value, inverse=inverse, **kwargs) + def log_det_jac( + self, data: dict[str, np.ndarray], log_det_jac: dict[str, np.ndarray], *, strict: bool = True, **kwargs + ): + data = data.copy() + + if strict and self.include is not None: + missing_keys = set(self.include) - set(data.keys()) + if missing_keys: + raise KeyError(f"Missing keys from include list: {missing_keys!r}") + + for key, value in data.items(): + if self._should_transform(key, value, inverse=False): + transform = self._get_transform(key) + ldj = transform.log_det_jac(value, **kwargs) + if ldj is None: + continue + elif key in log_det_jac: + log_det_jac[key] += ldj + else: + log_det_jac[key] = ldj + + return log_det_jac diff --git a/bayesflow/adapters/transforms/keep.py b/bayesflow/adapters/transforms/keep.py index 62373071f..56f395166 100644 --- a/bayesflow/adapters/transforms/keep.py +++ b/bayesflow/adapters/transforms/keep.py @@ -57,3 +57,6 @@ def inverse(self, data: dict[str, any], **kwargs) -> dict[str, any]: def extra_repr(self) -> str: return "[" + ", ".join(map(repr, self.keys)) + "]" + + def log_det_jac(self, data: dict[str, any], log_det_jac: dict[str, any], inverse: bool = False, **kwargs): + return self.inverse(data=log_det_jac) if inverse else self.forward(data=log_det_jac) diff --git a/bayesflow/adapters/transforms/log.py b/bayesflow/adapters/transforms/log.py index 3184ab979..d5f559b4f 100644 --- a/bayesflow/adapters/transforms/log.py +++ b/bayesflow/adapters/transforms/log.py @@ -37,3 +37,12 @@ def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: def get_config(self) -> dict: return serialize({"p1": self.p1}) + + def log_det_jac(self, data: np.ndarray, inverse: bool = False, **kwargs) -> np.ndarray: + if self.p1: + ldj = -np.log1p(data) + else: + ldj = -np.log(data) + if inverse: + ldj = -ldj + return np.sum(ldj, axis=tuple(range(1, ldj.ndim))) diff --git a/bayesflow/adapters/transforms/map_transform.py b/bayesflow/adapters/transforms/map_transform.py index 7820ce611..5da8292af 100644 --- a/bayesflow/adapters/transforms/map_transform.py +++ b/bayesflow/adapters/transforms/map_transform.py @@ -41,12 +41,8 @@ def get_config(self) -> dict: def forward(self, data: dict[str, np.ndarray], *, strict: bool = True, **kwargs) -> dict[str, np.ndarray]: data = data.copy() - required_keys = set(self.transform_map.keys()) - available_keys = set(data.keys()) - missing_keys = required_keys - available_keys - - if strict and missing_keys: - raise KeyError(f"Missing keys: {missing_keys!r}") + if strict: + self._check_keys(data) for key, transform in self.transform_map.items(): if key in data: @@ -57,15 +53,40 @@ def forward(self, data: dict[str, np.ndarray], *, strict: bool = True, **kwargs) def inverse(self, data: dict[str, np.ndarray], *, strict: bool = False, **kwargs) -> dict[str, np.ndarray]: data = data.copy() - required_keys = set(self.transform_map.keys()) - available_keys = set(data.keys()) - missing_keys = required_keys - available_keys - - if strict and missing_keys: - raise KeyError(f"Missing keys: {missing_keys!r}") + if strict: + self._check_keys(data) for key, transform in self.transform_map.items(): if key in data: data[key] = transform.inverse(data[key], **kwargs) return data + + def log_det_jac( + self, data: dict[str, np.ndarray], log_det_jac: dict[str, np.ndarray], *, strict: bool = True, **kwargs + ) -> dict[str, np.ndarray]: + data = data.copy() + + if strict: + self._check_keys(data) + + for key, transform in self.transform_map.items(): + if key in data: + ldj = transform.log_det_jac(data[key], **kwargs) + + if ldj is None: + continue + elif key in log_det_jac: + log_det_jac[key] += ldj + else: + log_det_jac[key] = ldj + + return log_det_jac + + def _check_keys(self, data: dict[str, np.ndarray]): + required_keys = set(self.transform_map.keys()) + available_keys = set(data.keys()) + missing_keys = required_keys - available_keys + + if missing_keys: + raise KeyError(f"Missing keys: {missing_keys!r}") diff --git a/bayesflow/adapters/transforms/numpy_transform.py b/bayesflow/adapters/transforms/numpy_transform.py index aecf03bba..29d25dc67 100644 --- a/bayesflow/adapters/transforms/numpy_transform.py +++ b/bayesflow/adapters/transforms/numpy_transform.py @@ -72,3 +72,6 @@ def forward(self, data: dict[str, any], **kwargs) -> dict[str, any]: def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: return self._inverse(data) + + def log_det_jac(self, data, inverse=False, **kwargs): + raise NotImplementedError("log determinant of the Jacobian of the numpy transforms are not implemented yet") diff --git a/bayesflow/adapters/transforms/rename.py b/bayesflow/adapters/transforms/rename.py index 49cc52eba..746ef5a80 100644 --- a/bayesflow/adapters/transforms/rename.py +++ b/bayesflow/adapters/transforms/rename.py @@ -58,3 +58,6 @@ def inverse(self, data: dict[str, any], *, strict: bool = False, **kwargs) -> di def extra_repr(self) -> str: return f"{self.from_key!r} -> {self.to_key!r}" + + def log_det_jac(self, data: dict[str, any], log_det_jac: dict[str, any], inverse: bool = False, **kwargs): + return self.inverse(data=log_det_jac) if inverse else self.forward(data=log_det_jac, strict=False) diff --git a/bayesflow/adapters/transforms/scale.py b/bayesflow/adapters/transforms/scale.py index 8d9dce1be..96b2ff927 100644 --- a/bayesflow/adapters/transforms/scale.py +++ b/bayesflow/adapters/transforms/scale.py @@ -18,3 +18,10 @@ def forward(self, data: np.ndarray, **kwargs) -> np.ndarray: def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: return data / self.scale + + def log_det_jac(self, data: np.ndarray, inverse: bool = False, **kwargs) -> np.ndarray: + ldj = np.log(np.abs(self.scale)) + ldj = np.full(data.shape, ldj) + if inverse: + ldj = -ldj + return np.sum(ldj, axis=tuple(range(1, ldj.ndim))) diff --git a/bayesflow/adapters/transforms/sqrt.py b/bayesflow/adapters/transforms/sqrt.py index 617f892bc..4ef1370dc 100644 --- a/bayesflow/adapters/transforms/sqrt.py +++ b/bayesflow/adapters/transforms/sqrt.py @@ -22,3 +22,9 @@ def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: def get_config(self) -> dict: return {} + + def log_det_jac(self, data: np.ndarray, inverse: bool = False, **kwargs) -> np.ndarray: + ldj = -0.5 * np.log(data) - np.log(2) + if inverse: + ldj = -ldj + return np.sum(ldj, axis=tuple(range(1, ldj.ndim))) diff --git a/bayesflow/adapters/transforms/standardize.py b/bayesflow/adapters/transforms/standardize.py index 52899917c..9699819b9 100644 --- a/bayesflow/adapters/transforms/standardize.py +++ b/bayesflow/adapters/transforms/standardize.py @@ -120,3 +120,10 @@ def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: std = np.broadcast_to(self.std, data.shape) return data * std + mean + + def log_det_jac(self, data, inverse: bool = False, **kwargs) -> np.ndarray: + std = np.broadcast_to(self.std, data.shape) + ldj = np.log(np.abs(std)) + if inverse: + ldj = -ldj + return np.sum(ldj, axis=tuple(range(1, ldj.ndim))) diff --git a/bayesflow/adapters/transforms/transform.py b/bayesflow/adapters/transforms/transform.py index 4642c1165..ed3058e15 100644 --- a/bayesflow/adapters/transforms/transform.py +++ b/bayesflow/adapters/transforms/transform.py @@ -35,3 +35,8 @@ def inverse(self, data: dict[str, np.ndarray], **kwargs) -> dict[str, np.ndarray def extra_repr(self) -> str: return "" + + def log_det_jac( + self, data: dict[str, np.ndarray], log_det_jac: dict[str, np.ndarray], inverse: bool = False, **kwargs + ) -> dict[str, np.ndarray]: + return log_det_jac diff --git a/bayesflow/approximators/continuous_approximator.py b/bayesflow/approximators/continuous_approximator.py index dcb661ca0..bf4e263a0 100644 --- a/bayesflow/approximators/continuous_approximator.py +++ b/bayesflow/approximators/continuous_approximator.py @@ -417,11 +417,16 @@ def log_prob(self, data: Mapping[str, np.ndarray], **kwargs) -> np.ndarray | dic np.ndarray Log-probabilities of the distribution `p(inference_variables | inference_conditions, h(summary_conditions))` """ - data = self.adapter(data, strict=False, stage="inference", **kwargs) + data, log_det_jac = self.adapter(data, strict=False, stage="inference", log_det_jac=True, **kwargs) data = keras.tree.map_structure(keras.ops.convert_to_tensor, data) log_prob = self._log_prob(**data, **kwargs) log_prob = keras.tree.map_structure(keras.ops.convert_to_numpy, log_prob) + # change of variables formula + log_det_jac = log_det_jac.get("inference_variables") + if log_det_jac is not None: + log_prob = log_prob + log_det_jac + return log_prob def _log_prob( diff --git a/tests/test_adapters/conftest.py b/tests/test_adapters/conftest.py index 873279f09..d69cd4be4 100644 --- a/tests/test_adapters/conftest.py +++ b/tests/test_adapters/conftest.py @@ -49,6 +49,8 @@ def random_data(): "z1": np.random.standard_normal(size=(32, 2)), "p1": np.random.lognormal(size=(32, 2)), "p2": np.random.lognormal(size=(32, 2)), + "p3": np.random.lognormal(size=(32, 2)), + "n1": 1 - np.random.lognormal(size=(32, 2)), "s1": np.random.standard_normal(size=(32, 3, 2)), "s2": np.random.standard_normal(size=(32, 3, 2)), "t1": np.zeros((3, 2)), @@ -56,5 +58,43 @@ def random_data(): "d1": np.random.standard_normal(size=(32, 2)), "d2": np.random.standard_normal(size=(32, 2)), "o1": np.random.randint(0, 9, size=(32, 2)), + "u1": np.random.uniform(low=-1, high=2, size=(32, 1)), "key_to_split": np.random.standard_normal(size=(32, 10)), } + + +@pytest.fixture() +def adapter_log_det_jac(): + from bayesflow.adapters import Adapter + + adapter = ( + Adapter() + .scale("x1", by=2) + .log("p1", p1=True) + .sqrt("p2") + .constrain("p3", lower=0) + .constrain("n1", upper=1) + .constrain("u1", lower=-1, upper=2) + .concatenate(["p1", "p2", "p3"], into="p") + .rename("u1", "u") + ) + + return adapter + + +@pytest.fixture() +def adapter_log_det_jac_inverse(): + from bayesflow.adapters import Adapter + + adapter = ( + Adapter() + .standardize("x1", mean=1, std=2) + .log("p1") + .sqrt("p2") + .constrain("p3", lower=0, method="log") + .constrain("n1", upper=1, method="log") + .constrain("u1", lower=-1, upper=2) + .scale(["p1", "p2", "p3"], by=3.5) + ) + + return adapter diff --git a/tests/test_adapters/test_adapters.py b/tests/test_adapters/test_adapters.py index d6215170e..1784befb7 100644 --- a/tests/test_adapters/test_adapters.py +++ b/tests/test_adapters/test_adapters.py @@ -13,7 +13,7 @@ def test_cycle_consistency(adapter, random_data): deprocessed = adapter(processed, inverse=True) for key, value in random_data.items(): - if key in ["d1", "d2"]: + if key in ["d1", "d2", "p3", "n1", "u1"]: # dropped continue assert key in deprocessed @@ -230,3 +230,62 @@ def test_to_dict_transform(): # category should have 5 one-hot categories, even though it was only passed 4 assert processed["category"].shape[-1] == 5 + + +def test_log_det_jac(adapter_log_det_jac, random_data): + d, log_det_jac = adapter_log_det_jac(random_data, log_det_jac=True) + + assert np.allclose(log_det_jac["x1"], np.log(2)) + + p1 = -np.log1p(random_data["p1"]) + p2 = -0.5 * np.log(random_data["p2"]) - np.log(2) + p3 = random_data["p3"] - np.log(np.exp(random_data["p3"]) - 1) + p = np.sum(p1, axis=-1) + np.sum(p2, axis=-1) + np.sum(p3, axis=-1) + + assert np.allclose(log_det_jac["p"], p) + + n1 = -(random_data["n1"] - 1) + n1 = n1 - np.log(np.exp(n1) - 1) + n1 = np.sum(n1, axis=-1) + + assert np.allclose(log_det_jac["n1"], n1) + + u1 = random_data["u1"] + u1 = (u1 + 1) / 3 + u1 = -np.log(u1) - np.log1p(-u1) - np.log(3) + + assert np.allclose(log_det_jac["u"], u1[:, 0]) + + +def test_log_det_jac_inverse(adapter_log_det_jac_inverse, random_data): + d, forward_log_det_jac = adapter_log_det_jac_inverse(random_data, log_det_jac=True) + d, inverse_log_det_jac = adapter_log_det_jac_inverse(d, inverse=True, log_det_jac=True) + + for key in forward_log_det_jac.keys(): + assert np.allclose(forward_log_det_jac[key], -inverse_log_det_jac[key]) + + +def test_log_det_jac_exceptions(random_data): + # Test cannot compute inverse log_det_jac + # e.g., when we apply a concat and then a transform that + # we cannot "unconcatenate" the log_det_jac + # (because the log_det_jac are summed, not concatenated) + adapter = bf.Adapter().concatenate(["p1", "p2", "p3"], into="p").sqrt("p") + transformed_data, log_det_jac = adapter(random_data, log_det_jac=True) + + # test that inverse raises error + with pytest.raises(ValueError): + adapter(transformed_data, inverse=True, log_det_jac=True) + + # test resolvable order: first transform, then concatenate + adapter = bf.Adapter().sqrt(["p1", "p2", "p3"]).concatenate(["p1", "p2", "p3"], into="p") + + transformed_data, forward_log_det_jac = adapter(random_data, log_det_jac=True) + data, inverse_log_det_jac = adapter(transformed_data, inverse=True, log_det_jac=True) + inverse_log_det_jac = sum(inverse_log_det_jac.values()) + + # forward is the same regardless + assert np.allclose(forward_log_det_jac["p"], log_det_jac["p"]) + + # inverse works when concatenation is used after transforms + assert np.allclose(forward_log_det_jac["p"], -inverse_log_det_jac) From 5595eabaa08f2e6bb036106e17164e8af9b47e8e Mon Sep 17 00:00:00 2001 From: Valentin Pratz <112951103+vpratz@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:22:15 +0200 Subject: [PATCH 39/46] Deprecate old serialization (`(de)serialize_value_or_type`) and add developer docs (#450) * document and expose serialization module * add functools.wraps call to allow_kwargs decorator, as before it was breaking the autodoc functionality * restructure and update developer docs * move content to separate pages * update section on serialization * ci: update pip via python -m pip pip install -U pip setuptools wheel leads to an error: https://github.com/bayesflow-org/bayesflow/actions/runs/14692655483/job/41230057180?pr=449 * serializable: increase depth in sys._getframe The functools.wrap decorator adds a frame object to the call stack * deprecate (de)serialize_value_or_type - add deprecation warning, remove functionality - replace all occurences with the corresponding new functions --- bayesflow/networks/point_inference_network.py | 14 +- bayesflow/scores/scoring_rule.py | 19 +-- bayesflow/utils/__init__.py | 3 +- bayesflow/utils/decorators.py | 1 + bayesflow/utils/serialization.py | 152 ++++++++++-------- docsrc/source/development/index.md | 90 ++--------- docsrc/source/development/introduction.md | 12 ++ docsrc/source/development/pitfalls.md | 13 ++ docsrc/source/development/serialization.md | 26 +++ docsrc/source/development/stages.md | 8 + 10 files changed, 169 insertions(+), 169 deletions(-) create mode 100644 docsrc/source/development/introduction.md create mode 100644 docsrc/source/development/pitfalls.md create mode 100644 docsrc/source/development/serialization.md create mode 100644 docsrc/source/development/stages.md diff --git a/bayesflow/networks/point_inference_network.py b/bayesflow/networks/point_inference_network.py index 3b1699e5a..63094a2a8 100644 --- a/bayesflow/networks/point_inference_network.py +++ b/bayesflow/networks/point_inference_network.py @@ -1,11 +1,7 @@ import keras -from keras.saving import ( - deserialize_keras_object as deserialize, - serialize_keras_object as serialize, - register_keras_serializable as serializable, -) -from bayesflow.utils import model_kwargs, find_network, serialize_value_or_type, deserialize_value_or_type +from bayesflow.utils import model_kwargs, find_network +from bayesflow.utils.serialization import deserialize, serializable, serialize from bayesflow.types import Shape, Tensor from bayesflow.scores import ScoringRule, ParametricDistributionScore from bayesflow.utils.decorators import allow_batch_size @@ -30,10 +26,10 @@ def __init__( self.subnet = find_network(subnet, **kwargs.get("subnet_kwargs", {})) self.config = { + "subnet": serialize(subnet), + "scores": serialize(scores), **kwargs, } - self.config = serialize_value_or_type(self.config, "subnet", subnet) - self.config["scores"] = serialize(self.scores) def build(self, xz_shape: Shape, conditions_shape: Shape = None) -> None: """Builds all network components based on shapes of conditions and targets. @@ -119,7 +115,7 @@ def get_config(self): def from_config(cls, config): config = config.copy() config["scores"] = deserialize(config["scores"]) - config = deserialize_value_or_type(config, "subnet") + config["subnet"] = deserialize(config["subnet"]) return cls(**config) def call( diff --git a/bayesflow/scores/scoring_rule.py b/bayesflow/scores/scoring_rule.py index a1a3f5717..0144de458 100644 --- a/bayesflow/scores/scoring_rule.py +++ b/bayesflow/scores/scoring_rule.py @@ -1,10 +1,10 @@ import math import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.types import Shape, Tensor -from bayesflow.utils import find_network, serialize_value_or_type, deserialize_value_or_type +from bayesflow.utils import find_network +from bayesflow.utils.serialization import deserialize, serializable, serialize @serializable(package="bayesflow.scores") @@ -51,23 +51,16 @@ def __init__( self.config = {"subnets_kwargs": self.subnets_kwargs} def get_config(self): - self.config["subnets"] = { - key: serialize_value_or_type({}, "subnet", subnet) for key, subnet in self.subnets.items() - } - self.config["links"] = {key: serialize_value_or_type({}, "link", link) for key, link in self.links.items()} + self.config["subnets"] = {key: serialize(subnet) for key, subnet in self.subnets.items()} + self.config["links"] = {key: serialize(link) for key, link in self.links.items()} return self.config @classmethod def from_config(cls, config): config = config.copy() - config["subnets"] = { - key: deserialize_value_or_type(subnet_dict, "subnet")["subnet"] - for key, subnet_dict in config["subnets"].items() - } - config["links"] = { - key: deserialize_value_or_type(link_dict, "link")["link"] for key, link_dict in config["links"].items() - } + config["subnets"] = {key: deserialize(subnet_dict) for key, subnet_dict in config["subnets"].items()} + config["links"] = {key: deserialize(link_dict) for key, link_dict in config["links"].items()} return cls(**config) diff --git a/bayesflow/utils/__init__.py b/bayesflow/utils/__init__.py index 737c533ce..47ab771ff 100644 --- a/bayesflow/utils/__init__.py +++ b/bayesflow/utils/__init__.py @@ -6,6 +6,7 @@ keras_utils, logging, numpy_utils, + serialization, ) from .callbacks import detailed_loss_callback @@ -104,4 +105,4 @@ from ._docs import _add_imports_to_all -_add_imports_to_all(include_modules=["keras_utils", "logging", "numpy_utils"]) +_add_imports_to_all(include_modules=["keras_utils", "logging", "numpy_utils", "serialization"]) diff --git a/bayesflow/utils/decorators.py b/bayesflow/utils/decorators.py index 7fd32edc9..1283fe66a 100644 --- a/bayesflow/utils/decorators.py +++ b/bayesflow/utils/decorators.py @@ -17,6 +17,7 @@ def allow_args(fn: Decorator) -> Decorator: def wrapper(f: Fn) -> Fn: ... @overload def wrapper(*fargs: any, **fkwargs: any) -> Fn: ... + @wraps(fn) def wrapper(*fargs: any, **fkwargs: any) -> Fn: if len(fargs) == 1 and not fkwargs and callable(fargs[0]): # called without arguments diff --git a/bayesflow/utils/serialization.py b/bayesflow/utils/serialization.py index 500264f05..bb55aee41 100644 --- a/bayesflow/utils/serialization.py +++ b/bayesflow/utils/serialization.py @@ -5,6 +5,7 @@ import keras import numpy as np import sys +from warnings import warn # this import needs to be exactly like this to work with monkey patching from keras.saving import deserialize_keras_object @@ -19,109 +20,102 @@ def serialize_value_or_type(config, name, obj): - """Serialize an object that can be either a value or a type - and add it to a copy of the supplied dictionary. + """This function is deprecated.""" + warn( + "This method is deprecated. It was replaced by bayesflow.utils.serialization.serialize.", + DeprecationWarning, + stacklevel=2, + ) - Parameters - ---------- - config : dict - Dictionary to add the serialized object to. This function does not - modify the dictionary in place, but returns a modified copy. - name : str - Name of the obj that should be stored. Required for later deserialization. - obj : object or type - The object to serialize. If `obj` is of type `type`, we use - `keras.saving.get_registered_name` to obtain the registered type name. - If it is not a type, we try to serialize it as a Keras object. - Returns - ------- - updated_config : dict - Updated dictionary with a new key `"_bayesflow__type"` or - `"_bayesflow__val"`. The prefix is used to avoid name collisions, - the suffix indicates how the stored value has to be deserialized. - - Notes - ----- - We allow strings or `type` parameters at several places to instantiate objects - of a given type (e.g., `subnet` in `CouplingFlow`). As `type` objects cannot - be serialized, we have to distinguish the two cases for serialization and - deserialization. This function is a helper function to standardize and - simplify this. - """ - updated_config = config.copy() - if isinstance(obj, type): - updated_config[f"{PREFIX}{name}_type"] = keras.saving.get_registered_name(obj) - else: - updated_config[f"{PREFIX}{name}_val"] = keras.saving.serialize_keras_object(obj) - return updated_config +def deserialize_value_or_type(config, name): + """This function is deprecated.""" + warn( + "This method is deprecated. It was replaced by bayesflow.utils.serialization.deserialize.", + DeprecationWarning, + stacklevel=2, + ) -def deserialize_value_or_type(config, name): - """Deserialize an object that can be either a value or a type and add - it to the supplied dictionary. +def deserialize(config: dict, custom_objects=None, safe_mode=True, **kwargs): + """Deserialize an object serialized with :py:func:`serialize`. + + Wrapper function around `keras.saving.deserialize_keras_object` to enable deserialization of + classes. Parameters ---------- - config : dict - Dictionary containing the object to deserialize. If a type was - serialized, it should contain the key `"_bayesflow__type"`. - If an object was serialized, it should contain the key - `"_bayesflow__val"`. In a copy of this dictionary, - the item will be replaced with the key `name`. - name : str - Name of the object to deserialize. + config : dict + Python dict describing the object. + custom_objects : dict, optional + Python dict containing a mapping between custom object names and the corresponding + classes or functions. Forwarded to `keras.saving.deserialize_keras_object`. + safe_mode : bool, optional + Boolean, whether to disallow unsafe lambda deserialization. When safe_mode=False, + loading an object has the potential to trigger arbitrary code execution. This argument + is only applicable to the Keras v3 model format. Defaults to True. + Forwarded to `keras.saving.deserialize_keras_object`. Returns ------- - updated_config : dict - Updated dictionary with a new key `name`, with a value that is either - a type or an object. + obj : + The object described by the config dictionary. + + Raises + ------ + ValueError + If a type in the config can not be deserialized. See Also -------- - serialize_value_or_type + serialize """ - updated_config = config.copy() - if f"{PREFIX}{name}_type" in config: - updated_config[name] = keras.saving.get_registered_object(config[f"{PREFIX}{name}_type"]) - del updated_config[f"{PREFIX}{name}_type"] - elif f"{PREFIX}{name}_val" in config: - updated_config[name] = keras.saving.deserialize_keras_object(config[f"{PREFIX}{name}_val"]) - del updated_config[f"{PREFIX}{name}_val"] - return updated_config - - -def deserialize(obj, custom_objects=None, safe_mode=True, **kwargs): with monkey_patch(deserialize_keras_object, deserialize) as original_deserialize: - if isinstance(obj, str) and obj.startswith(_type_prefix): + if isinstance(config, str) and config.startswith(_type_prefix): # we marked this as a type during serialization - obj = obj[len(_type_prefix) :] + config = config[len(_type_prefix) :] tp = keras.saving.get_registered_object( # TODO: can we pass module objects without overwriting numpy's dict with builtins? - obj, + config, custom_objects=custom_objects, module_objects=np.__dict__ | builtins.__dict__, ) if tp is None: raise ValueError( - f"Could not deserialize type {obj!r}. Make sure it is registered with " + f"Could not deserialize type {config!r}. Make sure it is registered with " f"`keras.saving.register_keras_serializable` or pass it in `custom_objects`." ) return tp - if inspect.isclass(obj): + if inspect.isclass(config): # add this base case since keras does not cover it - return obj + return config - obj = original_deserialize(obj, custom_objects=custom_objects, safe_mode=safe_mode, **kwargs) + obj = original_deserialize(config, custom_objects=custom_objects, safe_mode=safe_mode, **kwargs) return obj @allow_args -def serializable(cls, package=None, name=None): +def serializable(cls, package: str | None = None, name: str | None = None): + """Register class as Keras serialize. + + Wrapper function around `keras.saving.register_keras_serializable` to automatically + set the `package` and `name` arguments. + + Parameters + ---------- + cls : type + The class to register. + package : str, optional + `package` argument forwarded to `keras.saving.register_keras_serializable`. + If None is provided, the package is automatically inferred using the __name__ + attribute of the module the class resides in. + name : str, optional + `name` argument forwarded to `keras.saving.register_keras_serializable`. + If None is provided, the classe's __name__ attribute is used. + """ if package is None: - frame = sys._getframe(1) + frame = sys._getframe(2) g = frame.f_globals package = g.get("__name__", "bayesflow") @@ -133,6 +127,26 @@ def serializable(cls, package=None, name=None): def serialize(obj): + """Serialize an object using Keras. + + Wrapper function around `keras.saving.serialize_keras_object`, which adds the + ability to serialize classes. + + Parameters + ---------- + object : Keras serializable object, or class + The object to serialize + + Returns + ------- + config : dict + A python dict that represents the object. The python dict can be deserialized via + :py:func:`deserialize`. + + See Also + -------- + deserialize + """ if isinstance(obj, (tuple, list, dict)): return keras.tree.map_structure(serialize, obj) elif inspect.isclass(obj): diff --git a/docsrc/source/development/index.md b/docsrc/source/development/index.md index c62971532..adbadf21f 100644 --- a/docsrc/source/development/index.md +++ b/docsrc/source/development/index.md @@ -1,87 +1,23 @@ -# Patterns & Caveats +# Developer Documentation -**Note**: This document is part of BayesFlow's developer documentation, and +**Attention:** You are looking BayesFlow's developer documentation, which is aimed at people who want to extend or improve BayesFlow. For user documentation, -please refer to the examples and the public API documentation. +please refer to the {doc}`../examples` and the {doc}`../api/bayesflow`. -## Introduction - -From version 2 on, BayesFlow is built on [Keras](https://keras.io/) v3, which -allows writing machine learning pipelines that run in JAX, TensorFlow and PyTorch. -By using functionality provided by Keras, and extending it with backend-specific -code where necessary, we aim to build BayesFlow in a backend-agnostic fashion as -well. - -As Keras is built upon three different backend, each with different functionality -and design decisions, it has its own quirks and compromises. This documents -outlines some of them, along with the design decisions and programming patterns -we use to counter them. - -This document is work in progress, so if you read through the code base and +This section is work in progress, so if you read through the code base and encounter something that looks odd, but shows up in multiple places, please open an issue so that we can add it here. Also, if you introduce a new pattern that others will have to use in the future as well, please document it here, along with some background information on why it is necessary and how to use it in practice. -## Privileged `training` argument in the `call()` method cannot be passed via `kwargs` - -For layers that have different behavior at training and inference time (e.g., -dropout or batch normalization layers), a boolean `training` argument can be -exposed, see [this section of the Keras documentation](https://keras.io/guides/making_new_layers_and_models_via_subclassing/#privileged-training-argument-in-the-call-method). -If we want to pass this manually, we have to do so explicitly and not as part -of a set of keyword arguments via `**kwargs`. - -@Lars: Maybe you can add more details on what is going on behind the scenes. - -## Serialization - -Serialization deals with the problem of storing objects to disk, and loading -them at a later point in time. This is straight-forward for data structures like -numpy arrays, but for classes with custom behavior, like approximators or neural -network layers, it is somewhat more complex. - -Please refer to the Keras guide [Save, serialize, and export models](https://keras.io/guides/serialization_and_saving/) -for an introduction, and [Customizing Saving and Serialization](https://keras.io/guides/customizing_saving_and_serialization/) -for advanced concepts. - -The basic idea is: by storing the arguments of the constructor of a class -(i.e., the arguments of the `__init__` function), we can later construct an -object identical to the one we have stored, except for the weights. -As the structure is identical, we can then map the stored weights to the newly -constructed object. The caveat is that all arguments have to be either basic -Python objects (like int, float, string, bool, ...) or themselves serializable. -If they are not, we have to manually specify how to serialize them, and how to -load them later on. - -### Registering classes as serializable - -TODO - -### Serialization of custom types - -In BayesFlow, we often encounter situations where we do not want to pass a -specific object (e.g., an MPL of a certain size), but we want to pass its type -(MLP) and the arguments to construct it. With the type and the arguments, we can -then construct multiple instances of the network in different places, for example -as the network inside a coupling block. - -Unfortunately, `type` is not Keras serializable, so we have to serialize those -arguments manually. To complicate matters further, we also allow passing a string -instead of a type, which is then used to select the correct type. - -To make it more concrete, we look at the `CouplingFlow` class, which takes the -argument `subnet` that provide the type of the subnet. It is either a -string (e.g., `"mlp"`) or a class (e.g., `bayesflow.networks.MLP`). In the first -case, we can just store the value and load it, in the latter case, we first have -to convert the type to a string that we can later convert back into a type. - -We provide two helper functions that can deal with both cases: -`bayesflow.utils.serialize_value_or_type(config, name, obj)` and -`bayesflow.utils.deserialize_value_or_type(config, name)`. -In `get_config`, we use the first to store the object, whereas we use the -latter in `from_config` to load it again. +```{toctree} +:maxdepth: 1 +:titlesonly: +:numbered: -As we need all arguments to `__init__` in `get_config`, it can make sense to -build a `config` dictionary in `__init__` already, which can then be stored when -`get_config` is called. Take a look at `CouplingFlow` for an example of that. +introduction +pitfalls +stages +serialization +``` diff --git a/docsrc/source/development/introduction.md b/docsrc/source/development/introduction.md new file mode 100644 index 000000000..a60830c2a --- /dev/null +++ b/docsrc/source/development/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +From version 2 on, BayesFlow is built on [Keras3](https://keras.io/), which +allows writing machine learning pipelines that run in JAX, TensorFlow and PyTorch. +By using functionality provided by Keras, and extending it with backend-specific +code where necessary, we aim to build BayesFlow in a backend-agnostic fashion as +well. + +As Keras is built upon three different backends, each with different functionality +and design decisions, it comes with its own quirks and compromises. The following documents +outline some of them, along with the design decisions and programming patterns +we use to counter them. diff --git a/docsrc/source/development/pitfalls.md b/docsrc/source/development/pitfalls.md new file mode 100644 index 000000000..69d183ec1 --- /dev/null +++ b/docsrc/source/development/pitfalls.md @@ -0,0 +1,13 @@ +# Potential Pitfalls + +This document covers things we have learned during development that might cause problems or hard to find bugs. + +## Privileged `training` argument in the `call()` method cannot be passed via `kwargs` + +For layers that have different behavior at training and inference time (e.g., +dropout or batch normalization layers), a boolean `training` argument can be +exposed, see [this section of the Keras documentation](https://keras.io/guides/making_new_layers_and_models_via_subclassing/#privileged-training-argument-in-the-call-method). +If we want to pass this manually, we have to do so explicitly and not as part +of a set of keyword arguments via `**kwargs`. + +@Lars: Maybe you can add more details on what is going on behind the scenes. diff --git a/docsrc/source/development/serialization.md b/docsrc/source/development/serialization.md new file mode 100644 index 000000000..15c812686 --- /dev/null +++ b/docsrc/source/development/serialization.md @@ -0,0 +1,26 @@ +# Serialization: Enable Model Saving & Loading + +Serialization deals with the problem of storing objects to disk, and loading them at a later point in time. +This is straight-forward for data structures like numpy arrays, but for classes with custom behavior it is somewhat more complex. + +Please refer to the Keras guide [Save, serialize, and export models](https://keras.io/guides/serialization_and_saving/) for an introduction, and [Customizing Saving and Serialization](https://keras.io/guides/customizing_saving_and_serialization/) for advanced concepts. + +The basic idea is: by storing the arguments of the constructor of a class (i.e., the arguments of the `__init__` function), we can later construct an object similar to the one we have stored, except for the weights and other stateful content. +As the structure is identical, we can then map the stored weights to the newly constructed object. +The caveat is that all arguments have to be either basic Python objects (like int, float, string, bool, ...) or themselves serializable. +If they are not, we have to manually specify how to serialize them, and how to load them later on. +One important example is that types are not serializable. +As we want/need to pass them in some places, we have to resort to some custom behavior, that is described below. + +## Serialization Utilities + +BayesFlows serialization utilities can be found in the {py:mod}`~bayesflow.utils.serialization` module. +We mainly provide three convenience functions: + +- The {py:func}`~bayesflow.utils.serialization.serializable` decorator wraps the `keras.saving.register_keras_serializable` function to provide automatic `package` and `name` arguments. +- The {py:func}`~bayesflow.utils.serialization.serialize` function, which adds support for serializing classes. +- Its counterpart {py:func}`~bayesflow.utils.serialization.deserialize`, adds support to deserialize classes. + +## Usage + +To use the adapted serialization functions, you have to use them in the `get_config` and `from_config` method. Please refer to existing classes in the library for usage examples. diff --git a/docsrc/source/development/stages.md b/docsrc/source/development/stages.md new file mode 100644 index 000000000..e9aa4ad8f --- /dev/null +++ b/docsrc/source/development/stages.md @@ -0,0 +1,8 @@ +# Stages + +To keep track of the phase each functionality is called in, we provide a `stage` parameter. +There are three stages: + +- `training`: The stage to train approximator (and related stateful objects, like the adapter) +- `validation`: Identical setting to `training`, but calls in this stage should _not_ change the approximator +- `inference`: Calls in this change should not change the approximator. In addition, the input structure might be different compared to the training phase. For example for sampling, we only provide `summary_conditions` and `inference_conditions`, but not the `inference_variables`, which we want to infer. From 62675c33f51f8c6f531046be3e767ad07b1890fb Mon Sep 17 00:00:00 2001 From: The-Gia Leo Nguyen Date: Fri, 2 May 2025 09:14:15 +0200 Subject: [PATCH 40/46] Implement Feature for Issue #379: MMD Hypothesis Test (#384) - add `bootstrap_comparison` and `summary_space_comparison` to enable comparisons of two domains in the data space or the summary space via bootstrapping - add `.summaries()` function for easy access to summaries to `ContinuousApproximator` and `ModelComparisonApproximator` - add tests for the added functionality --------- Co-authored-by: Valentin Pratz --- .../approximators/continuous_approximator.py | 33 ++ .../model_comparison_approximator.py | 33 ++ bayesflow/diagnostics/__init__.py | 9 +- bayesflow/diagnostics/metrics/__init__.py | 1 + .../metrics/model_misspecification.py | 155 ++++++++++ tests/test_approximators/conftest.py | 31 ++ tests/test_approximators/test_summaries.py | 23 ++ tests/test_diagnostics/conftest.py | 14 + .../test_diagnostics_metrics.py | 290 +++++++++++++++++- tests/utils/__init__.py | 1 + tests/utils/networks.py | 8 + 11 files changed, 595 insertions(+), 3 deletions(-) create mode 100644 bayesflow/diagnostics/metrics/model_misspecification.py create mode 100644 tests/test_approximators/test_summaries.py create mode 100644 tests/utils/networks.py diff --git a/bayesflow/approximators/continuous_approximator.py b/bayesflow/approximators/continuous_approximator.py index bf4e263a0..834521d4b 100644 --- a/bayesflow/approximators/continuous_approximator.py +++ b/bayesflow/approximators/continuous_approximator.py @@ -400,6 +400,39 @@ def _sample( **filter_kwargs(kwargs, self.inference_network.sample), ) + def summaries(self, data: Mapping[str, np.ndarray], **kwargs): + """ + Computes the summaries of given data. + + The `data` dictionary is preprocessed using the `adapter` and passed through the summary network. + + Parameters + ---------- + data : Mapping[str, np.ndarray] + Dictionary of data as NumPy arrays. + **kwargs : dict + Additional keyword arguments for the adapter and the summary network. + + Returns + ------- + summaries : np.ndarray + Log-probabilities of the distribution `p(inference_variables | inference_conditions, h(summary_conditions))` + + Raises + ------ + ValueError + If the approximator does not have a summary network, or the adapter does not produce the output required + by the summary network. + """ + if self.summary_network is None: + raise ValueError("A summary network is required to compute summeries.") + data_adapted = self.adapter(data, strict=False, stage="inference", **kwargs) + if "summary_variables" not in data_adapted or data_adapted["summary_variables"] is None: + raise ValueError("Summary variables are required to compute summaries.") + summary_variables = keras.ops.convert_to_tensor(data_adapted["summary_variables"]) + summaries = self.summary_network(summary_variables, **filter_kwargs(kwargs, self.summary_network.call)) + return summaries + def log_prob(self, data: Mapping[str, np.ndarray], **kwargs) -> np.ndarray | dict[str, np.ndarray]: """ Computes the log-probability of given data under the model. The `data` dictionary is preprocessed using the diff --git a/bayesflow/approximators/model_comparison_approximator.py b/bayesflow/approximators/model_comparison_approximator.py index 1b9d198ff..028e8837a 100644 --- a/bayesflow/approximators/model_comparison_approximator.py +++ b/bayesflow/approximators/model_comparison_approximator.py @@ -345,3 +345,36 @@ def _predict(self, classifier_conditions: Tensor = None, summary_variables: Tens output = self.logits_projector(output) return output + + def summaries(self, data: Mapping[str, np.ndarray], **kwargs): + """ + Computes the summaries of given data. + + The `data` dictionary is preprocessed using the `adapter` and passed through the summary network. + + Parameters + ---------- + data : Mapping[str, np.ndarray] + Dictionary of data as NumPy arrays. + **kwargs : dict + Additional keyword arguments for the adapter and the summary network. + + Returns + ------- + summaries : np.ndarray + Log-probabilities of the distribution `p(inference_variables | inference_conditions, h(summary_conditions))` + + Raises + ------ + ValueError + If the approximator does not have a summary network, or the adapter does not produce the output required + by the summary network. + """ + if self.summary_network is None: + raise ValueError("A summary network is required to compute summaries.") + data_adapted = self.adapter(data, strict=False, stage="inference", **kwargs) + if "summary_variables" not in data_adapted or data_adapted["summary_variables"] is None: + raise ValueError("Summary variables are required to compute summaries.") + summary_variables = keras.ops.convert_to_tensor(data_adapted["summary_variables"]) + summaries = self.summary_network(summary_variables, **filter_kwargs(kwargs, self.summary_network.call)) + return summaries diff --git a/bayesflow/diagnostics/__init__.py b/bayesflow/diagnostics/__init__.py index 1e13e11f2..87823c754 100644 --- a/bayesflow/diagnostics/__init__.py +++ b/bayesflow/diagnostics/__init__.py @@ -1,8 +1,13 @@ -""" +r""" A collection of plotting utilities and metrics for evaluating trained :py:class:`~bayesflow.workflows.Workflow`\ s. """ -from .metrics import root_mean_squared_error, calibration_error, posterior_contraction +from .metrics import ( + bootstrap_comparison, + calibration_error, + posterior_contraction, + summary_space_comparison, +) from .plots import ( calibration_ecdf, diff --git a/bayesflow/diagnostics/metrics/__init__.py b/bayesflow/diagnostics/metrics/__init__.py index ceeca4cc4..3e3496cda 100644 --- a/bayesflow/diagnostics/metrics/__init__.py +++ b/bayesflow/diagnostics/metrics/__init__.py @@ -3,3 +3,4 @@ from .root_mean_squared_error import root_mean_squared_error from .expected_calibration_error import expected_calibration_error from .classifier_two_sample_test import classifier_two_sample_test +from .model_misspecification import bootstrap_comparison, summary_space_comparison diff --git a/bayesflow/diagnostics/metrics/model_misspecification.py b/bayesflow/diagnostics/metrics/model_misspecification.py new file mode 100644 index 000000000..c698d4eb2 --- /dev/null +++ b/bayesflow/diagnostics/metrics/model_misspecification.py @@ -0,0 +1,155 @@ +""" +This module provides functions for computing distances between observation samples and reference samples with distance +distributions within the reference samples for hypothesis testing. +""" + +from collections.abc import Mapping, Callable + +import numpy as np +from keras.ops import convert_to_numpy, convert_to_tensor + +from bayesflow.approximators import ContinuousApproximator +from bayesflow.metrics.functional import maximum_mean_discrepancy +from bayesflow.types import Tensor + + +def bootstrap_comparison( + observed_samples: np.ndarray, + reference_samples: np.ndarray, + comparison_fn: Callable[[Tensor, Tensor], Tensor], + num_null_samples: int = 100, +) -> tuple[float, np.ndarray]: + """Computes the distance between observed and reference samples and generates a distribution of null sample + distances by bootstrapping for hypothesis testing. + + Parameters + ---------- + observed_samples : np.ndarray) + Observed samples, shape (num_observed, ...). + reference_samples : np.ndarray + Reference samples, shape (num_reference, ...). + comparison_fn : Callable[[Tensor, Tensor], Tensor] + Function to compute the distance metric. + num_null_samples : int + Number of null samples to generate for hypothesis testing. Default is 100. + + Returns + ------- + distance_observed : float + The distance value between observed and reference samples. + distance_null : np.ndarray + A distribution of distance values under the null hypothesis. + + Raises + ------ + ValueError + - If the number of number of observed samples exceeds the number of reference samples + - If the shapes of observed and reference samples do not match on dimensions besides the first one. + """ + num_observed: int = observed_samples.shape[0] + num_reference: int = reference_samples.shape[0] + + if num_observed > num_reference: + raise ValueError( + f"Number of observed samples ({num_observed}) cannot exceed" + f"the number of reference samples ({num_reference}) for bootstrapping." + ) + if observed_samples.shape[1:] != reference_samples.shape[1:]: + raise ValueError( + f"Expected observed and reference samples to have the same shape, " + f"but got {observed_samples.shape[1:]} != {reference_samples.shape[1:]}." + ) + + observed_samples_tensor: Tensor = convert_to_tensor(observed_samples, dtype="float32") + reference_samples_tensor: Tensor = convert_to_tensor(reference_samples, dtype="float32") + + distance_null_samples: np.ndarray = np.zeros(num_null_samples, dtype=np.float64) + for i in range(num_null_samples): + bootstrap_idx: np.ndarray = np.random.randint(0, num_reference, size=num_observed) + bootstrap_samples: np.ndarray = reference_samples[bootstrap_idx] + bootstrap_samples_tensor: Tensor = convert_to_tensor(bootstrap_samples, dtype="float32") + distance_null_samples[i] = convert_to_numpy(comparison_fn(bootstrap_samples_tensor, reference_samples_tensor)) + + distance_observed_tensor: Tensor = comparison_fn( + observed_samples_tensor, + reference_samples_tensor, + ) + + distance_observed: float = float(convert_to_numpy(distance_observed_tensor)) + + return distance_observed, distance_null_samples + + +def summary_space_comparison( + observed_data: Mapping[str, np.ndarray], + reference_data: Mapping[str, np.ndarray], + approximator: ContinuousApproximator, + num_null_samples: int = 100, + comparison_fn: Callable = maximum_mean_discrepancy, + **kwargs, +) -> tuple[float, np.ndarray]: + """Computes the distance between observed and reference data in the summary space and + generates a distribution of distance values under the null hypothesis to assess model misspecification. + + By default, the Maximum Mean Discrepancy (MMD) is used as a distance function. + + [1] M. Schmitt, P.-C. Bürkner, U. Köthe, and S. T. Radev, "Detecting model misspecification in amortized Bayesian + inference with neural networks," arXiv e-prints, Dec. 2021, Art. no. arXiv:2112.08866. + URL: https://arxiv.org/abs/2112.08866 + + Parameters + ---------- + observed_data : dict[str, np.ndarray] + Dictionary of observed data as NumPy arrays, which will be preprocessed by the approximators adapter and passed + through its summary network. + reference_data : dict[str, np.ndarray] + Dictionary of reference data as NumPy arrays, which will be preprocessed by the approximators adapter and passed + through its summary network. + approximator : ContinuousApproximator + An instance of :py:class:`~bayesflow.approximators.ContinuousApproximator` used to compute summary statistics + from the data. + num_null_samples : int, optional + Number of null samples to generate for hypothesis testing. Default is 100. + comparison_fn : Callable, optional + Distance function to compare the data in the summary space. + **kwargs : dict + Additional keyword arguments for the adapter and sampling process. + + Returns + ------- + distance_observed : float + The MMD value between observed and reference summaries. + distance_null : np.ndarray + A distribution of MMD values under the null hypothesis. + + Raises + ------ + ValueError + If approximator is not an instance of ContinuousApproximator or does not have a summary network. + """ + + if not isinstance(approximator, ContinuousApproximator): + raise ValueError("The approximator must be an instance of ContinuousApproximator.") + + if not hasattr(approximator, "summary_network") or approximator.summary_network is None: + comparison_fn_name = ( + "bayesflow.metrics.functional.maximum_mean_discrepancy" + if comparison_fn is maximum_mean_discrepancy + else comparison_fn.__name__ + ) + raise ValueError( + "The approximator must have a summary network. If you have manually crafted summary " + "statistics, or want to compare raw data and not summary statistics, please use the " + f"`bootstrap_comparison` function with `comparison_fn={comparison_fn_name}` on the respective arrays." + ) + observed_summaries = convert_to_numpy(approximator.summaries(observed_data)) + reference_summaries = convert_to_numpy(approximator.summaries(reference_data)) + + distance_observed, distance_null = bootstrap_comparison( + observed_samples=observed_summaries, + reference_samples=reference_summaries, + comparison_fn=comparison_fn, + num_null_samples=num_null_samples, + ) + + return distance_observed, distance_null diff --git a/tests/test_approximators/conftest.py b/tests/test_approximators/conftest.py index 125371a52..227e70ff1 100644 --- a/tests/test_approximators/conftest.py +++ b/tests/test_approximators/conftest.py @@ -163,3 +163,34 @@ def validation_dataset(batch_size, adapter, simulator): num_batches = 2 data = simulator.sample((num_batches * batch_size,)) return OfflineDataset(data=data, adapter=adapter, batch_size=batch_size, workers=4, max_queue_size=num_batches) + + +@pytest.fixture() +def mean_std_summary_network(): + from tests.utils import MeanStdSummaryNetwork + + return MeanStdSummaryNetwork() + + +@pytest.fixture(params=["continuous_approximator", "point_approximator", "model_comparison_approximator"]) +def approximator_with_summaries(request): + from bayesflow.adapters import Adapter + + adapter = Adapter() + match request.param: + case "continuous_approximator": + from bayesflow.approximators import ContinuousApproximator + + return ContinuousApproximator(adapter=adapter, inference_network=None, summary_network=None) + case "point_approximator": + from bayesflow.approximators import PointApproximator + + return PointApproximator(adapter=adapter, inference_network=None, summary_network=None) + case "model_comparison_approximator": + from bayesflow.approximators import ModelComparisonApproximator + + return ModelComparisonApproximator( + num_models=2, classifier_network=None, adapter=adapter, summary_network=None + ) + case _: + raise ValueError("Invalid param for approximator class.") diff --git a/tests/test_approximators/test_summaries.py b/tests/test_approximators/test_summaries.py new file mode 100644 index 000000000..7962ddaab --- /dev/null +++ b/tests/test_approximators/test_summaries.py @@ -0,0 +1,23 @@ +import pytest +from tests.utils import assert_allclose +import keras + + +def test_valid_summaries(approximator_with_summaries, mean_std_summary_network, monkeypatch): + monkeypatch.setattr(approximator_with_summaries, "summary_network", mean_std_summary_network) + summaries = approximator_with_summaries.summaries({"summary_variables": keras.ops.ones((2, 3))}) + assert_allclose(summaries, keras.ops.stack([keras.ops.ones((2,)), keras.ops.zeros((2,))], axis=-1)) + + +def test_no_summary_network(approximator_with_summaries, monkeypatch): + monkeypatch.setattr(approximator_with_summaries, "summary_network", None) + + with pytest.raises(ValueError): + approximator_with_summaries.summaries({"summary_variables": keras.ops.ones((2, 3))}) + + +def test_no_summary_variables(approximator_with_summaries, mean_std_summary_network, monkeypatch): + monkeypatch.setattr(approximator_with_summaries, "summary_network", mean_std_summary_network) + + with pytest.raises(ValueError): + approximator_with_summaries.summaries({}) diff --git a/tests/test_diagnostics/conftest.py b/tests/test_diagnostics/conftest.py index 8e77d6729..dc859d2d4 100644 --- a/tests/test_diagnostics/conftest.py +++ b/tests/test_diagnostics/conftest.py @@ -78,3 +78,17 @@ def history(): } return h + + +@pytest.fixture() +def adapter(): + from bayesflow.adapters import Adapter + + return Adapter.create_default("parameters").rename("observables", "summary_variables") + + +@pytest.fixture() +def summary_network(): + from tests.utils import MeanStdSummaryNetwork + + return MeanStdSummaryNetwork() diff --git a/tests/test_diagnostics/test_diagnostics_metrics.py b/tests/test_diagnostics/test_diagnostics_metrics.py index 4fb0945b3..3a2c711bc 100644 --- a/tests/test_diagnostics/test_diagnostics_metrics.py +++ b/tests/test_diagnostics/test_diagnostics_metrics.py @@ -1,6 +1,9 @@ -import bayesflow as bf +import numpy as np +import keras import pytest +import bayesflow as bf + def num_variables(x: dict): return sum(arr.shape[-1] for arr in x.values()) @@ -79,3 +82,288 @@ def test_expected_calibration_error(pred_models, true_models, model_names): with pytest.raises(Exception): out = bf.diagnostics.metrics.expected_calibration_error(pred_models, true_models.transpose) + + +def test_bootstrap_comparison_shapes(): + """Test the bootstrap_comparison output shapes.""" + observed_samples = np.random.rand(10, 5) + reference_samples = np.random.rand(100, 5) + num_null_samples = 50 + + distance_observed, distance_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_samples, + reference_samples, + lambda x, y: keras.ops.abs(keras.ops.mean(x) - keras.ops.mean(y)), + num_null_samples, + ) + + assert isinstance(distance_observed, float) + assert isinstance(distance_null, np.ndarray) + assert distance_null.shape == (num_null_samples,) + + +def test_bootstrap_comparison_same_distribution(): + """Test bootstrap_comparison on same distributions.""" + observed_samples = np.random.normal(loc=0.5, scale=0.1, size=(10, 5)) + reference_samples = observed_samples.copy() + num_null_samples = 5 + + distance_observed, distance_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_samples, + reference_samples, + lambda x, y: keras.ops.abs(keras.ops.mean(x) - keras.ops.mean(y)), + num_null_samples, + ) + + assert distance_observed <= np.quantile(distance_null, 0.99) + + +def test_bootstrap_comparison_different_distributions(): + """Test bootstrap_comparison on different distributions.""" + observed_samples = np.random.normal(loc=-5, scale=0.1, size=(10, 5)) + reference_samples = np.random.normal(loc=5, scale=0.1, size=(100, 5)) + num_null_samples = 50 + + distance_observed, distance_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_samples, + reference_samples, + lambda x, y: keras.ops.abs(keras.ops.mean(x) - keras.ops.mean(y)), + num_null_samples, + ) + + assert distance_observed >= np.quantile(distance_null, 0.68) + + +def test_bootstrap_comparison_mismatched_shapes(): + """Test bootstrap_comparison raises ValueError for mismatched shapes.""" + observed_samples = np.random.rand(10, 5) + reference_samples = np.random.rand(20, 4) + num_null_samples = 10 + + with pytest.raises(ValueError): + bf.diagnostics.metrics.bootstrap_comparison( + observed_samples, + reference_samples, + lambda x, y: keras.ops.abs(keras.ops.mean(x) - keras.ops.mean(y)), + num_null_samples, + ) + + +def test_bootstrap_comparison_num_observed_exceeds_num_reference(): + """Test bootstrap_comparison raises ValueError when number of observed samples exceeds the number of reference + samples.""" + observed_samples = np.random.rand(100, 5) + reference_samples = np.random.rand(20, 5) + num_null_samples = 50 + + with pytest.raises(ValueError): + bf.diagnostics.metrics.bootstrap_comparison( + observed_samples, + reference_samples, + lambda x, y: keras.ops.abs(keras.ops.mean(x) - keras.ops.mean(y)), + num_null_samples, + ) + + +def test_mmd_comparison_from_summaries_shapes(): + """Test the mmd_comparison_from_summaries output shapes.""" + observed_summaries = np.random.rand(10, 5) + reference_summaries = np.random.rand(100, 5) + num_null_samples = 50 + + mmd_observed, mmd_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_summaries, + reference_summaries, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + num_null_samples=num_null_samples, + ) + + assert isinstance(mmd_observed, float) + assert isinstance(mmd_null, np.ndarray) + assert mmd_null.shape == (num_null_samples,) + + +def test_mmd_comparison_from_summaries_positive(): + """Test MMD output values of mmd_comparison_from_summaries are positive.""" + observed_summaries = np.random.rand(10, 5) + reference_summaries = np.random.rand(100, 5) + num_null_samples = 50 + + mmd_observed, mmd_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_summaries, + reference_summaries, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + num_null_samples=num_null_samples, + ) + + assert mmd_observed >= 0 + assert np.all(mmd_null >= 0) + + +def test_mmd_comparison_from_summaries_same_distribution(): + """Test mmd_comparison_from_summaries on same distributions.""" + observed_summaries = np.random.rand(10, 5) + reference_summaries = observed_summaries.copy() + num_null_samples = 5 + + mmd_observed, mmd_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_summaries, + reference_summaries, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + num_null_samples=num_null_samples, + ) + + assert mmd_observed <= np.quantile(mmd_null, 0.99) + + +def test_mmd_comparison_from_summaries_different_distributions(): + """Test mmd_comparison_from_summaries on different distributions.""" + observed_summaries = np.random.rand(10, 5) + reference_summaries = np.random.normal(loc=0.5, scale=0.1, size=(100, 5)) + num_null_samples = 50 + + mmd_observed, mmd_null = bf.diagnostics.metrics.bootstrap_comparison( + observed_summaries, + reference_summaries, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + num_null_samples=num_null_samples, + ) + + assert mmd_observed >= np.quantile(mmd_null, 0.68) + + +def test_mmd_comparison_shapes(summary_network, adapter): + """Test the mmd_comparison output shapes.""" + observed_data = dict(observables=np.random.rand(10, 5)) + reference_data = dict(observables=np.random.rand(100, 5)) + num_null_samples = 50 + + mock_approximator = bf.approximators.ContinuousApproximator( + adapter=adapter, + inference_network=None, + summary_network=summary_network, + ) + + mmd_observed, mmd_null = bf.diagnostics.metrics.summary_space_comparison( + observed_data=observed_data, + reference_data=reference_data, + approximator=mock_approximator, + num_null_samples=num_null_samples, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + ) + + assert isinstance(mmd_observed, float) + assert isinstance(mmd_null, np.ndarray) + assert mmd_null.shape == (num_null_samples,) + + +def test_mmd_comparison_positive(summary_network, adapter): + """Test MMD output values of mmd_comparison are positive.""" + observed_data = dict(observables=np.random.rand(10, 5)) + reference_data = dict(observables=np.random.rand(100, 5)) + num_null_samples = 50 + + mock_approximator = bf.approximators.ContinuousApproximator( + adapter=adapter, + inference_network=None, + summary_network=summary_network, + ) + + mmd_observed, mmd_null = bf.diagnostics.metrics.summary_space_comparison( + observed_data=observed_data, + reference_data=reference_data, + approximator=mock_approximator, + num_null_samples=num_null_samples, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + ) + + assert mmd_observed >= 0 + assert np.all(mmd_null >= 0) + + +def test_mmd_comparison_same_distribution(summary_network, adapter): + """Test mmd_comparison on same distributions.""" + observed_data = dict(observables=np.random.rand(10, 5)) + reference_data = observed_data + num_null_samples = 5 + + mock_approximator = bf.approximators.ContinuousApproximator( + adapter=adapter, + inference_network=None, + summary_network=summary_network, + ) + + mmd_observed, mmd_null = bf.diagnostics.metrics.summary_space_comparison( + observed_data=observed_data, + reference_data=reference_data, + approximator=mock_approximator, + num_null_samples=num_null_samples, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + ) + + assert mmd_observed <= np.quantile(mmd_null, 0.99) + + +def test_mmd_comparison_different_distributions(summary_network, adapter): + """Test mmd_comparison on different distributions.""" + observed_data = dict(observables=np.random.rand(10, 5)) + reference_data = dict(observables=np.random.normal(loc=0.5, scale=0.1, size=(100, 5))) + num_null_samples = 50 + + mock_approximator = bf.approximators.ContinuousApproximator( + adapter=adapter, + inference_network=None, + summary_network=summary_network, + ) + + mmd_observed, mmd_null = bf.diagnostics.metrics.summary_space_comparison( + observed_data=observed_data, + reference_data=reference_data, + approximator=mock_approximator, + num_null_samples=num_null_samples, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + ) + + assert mmd_observed >= np.quantile(mmd_null, 0.68) + + +def test_mmd_comparison_no_summary_network(adapter): + observed_data = dict(observables=np.random.rand(10, 5)) + reference_data = dict(observables=np.random.rand(100, 5)) + num_null_samples = 50 + + mock_approximator = bf.approximators.ContinuousApproximator( + adapter=adapter, + inference_network=None, + summary_network=None, + ) + + with pytest.raises(ValueError): + bf.diagnostics.metrics.summary_space_comparison( + observed_data=observed_data, + reference_data=reference_data, + approximator=mock_approximator, + num_null_samples=num_null_samples, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + ) + + +def test_mmd_comparison_approximator_incorrect_instance(): + """Test mmd_comparison raises ValueError for incorrect approximator instance.""" + observed_data = dict(observables=np.random.rand(10, 5)) + reference_data = dict(observables=np.random.rand(100, 5)) + num_null_samples = 50 + + class IncorrectApproximator: + pass + + mock_approximator = IncorrectApproximator() + + with pytest.raises(ValueError): + bf.diagnostics.metrics.summary_space_comparison( + observed_data=observed_data, + reference_data=reference_data, + approximator=mock_approximator, + num_null_samples=num_null_samples, + comparison_fn=bf.metrics.functional.maximum_mean_discrepancy, + ) diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index f36b02bbd..9c2affc22 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -2,4 +2,5 @@ from .callbacks import * from .check_combinations import * from .jupyter import * +from .networks import * from .ops import * diff --git a/tests/utils/networks.py b/tests/utils/networks.py new file mode 100644 index 000000000..cf35e1463 --- /dev/null +++ b/tests/utils/networks.py @@ -0,0 +1,8 @@ +from bayesflow.networks import SummaryNetwork +import keras + + +class MeanStdSummaryNetwork(SummaryNetwork): + def call(self, x): + summary_outputs = keras.ops.stack([keras.ops.mean(x, axis=-1), keras.ops.std(x, axis=-1)], axis=-1) + return summary_outputs From 3a69644a1e95d17268fb11180f90d47fd415479c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Olischl=C3=A4ger?= <106988117+han-ol@users.noreply.github.com> Date: Sat, 3 May 2025 21:54:52 +0200 Subject: [PATCH 41/46] Add projectors to DeepSet (#453) * v2.0.2 (#447) * [no ci] notebook tests: increase timeout, fix platform/backend dependent code Torch is very slow, so I had to increase the timeout accordingly. * Enable use of summary networks with functional API again (#434) * summary networks: add tests for using functional API * fix build functions for use with functional API * [no ci] docs: add GitHub and Discourse links, reorder navbar * [no ci] docs: acknowledge scikit-learn website * [no ci] docs: capitalize navigation headings * More tests (#437) * fix docs of coupling flow * add additional tests * Automatically run slow tests when main is involved. (#438) In addition, this PR limits the slow test to Windows and Python 3.10. The choices are somewhat arbitrary, my thought was to test the setup not covered as much through use by the devs. * Update dispatch * Update dispatching distributions * Improve workflow tests with multiple summary nets / approximators * Fix zombie find_distribution import * Add readme entry [no ci] * Update README: NumFOCUS affiliation, awesome-abi list (#445) * fix is_symbolic_tensor * remove multiple batch sizes, remove multiple python version tests, remove update-workflows branch from workflow style tests, add __init__ and conftest to test_point_approximators (#443) * implement compile_from_config and get_compile_config (#442) * implement compile_from_config and get_compile_config * add optimizer build to compile_from_config * Fix Optimal Transport for Compiled Contexts (#446) * remove the is_symbolic_tensor check because this would otherwise skip the whole function for compiled contexts * skip pyabc test * fix sinkhorn and log_sinkhorn message formatting for jax by making the warning message worse * update dispatch tests for more coverage * Update issue templates (#448) * Hotfix Version 2.0.1 (#431) * fix optimal transport config (#429) * run linter * [skip-ci] bump version to 2.0.1 * Update issue templates * Robustify kwargs passing inference networks, add class variables * fix convergence method to debug for non-log sinkhorn * Bump optimal transport default to False * use logging.info for backend selection instead of logging.debug * fix model comparison approximator * improve docs and type hints * improve One-Sample T-Test Notebook: - use torch as default backend - reduce range of N so users of jax won't be stuck with a slow notebook - use BayesFlow built-in MLP instead of keras.Sequential solution - general code cleanup * remove backend print * [skip ci] turn all single-quoted strings into double-quoted strings * turn all single-quoted strings into double-quoted strings amend to trigger workflow --------- Co-authored-by: Valentin Pratz Co-authored-by: Valentin Pratz <112951103+vpratz@users.noreply.github.com> Co-authored-by: stefanradev93 Co-authored-by: Marvin Schmitt <35921281+marvinschmitt@users.noreply.github.com> * drafting feature * Initialize projectors for invariant and equivariant DeepSet layers * implement requested changes and improve activation --------- Co-authored-by: Lars Co-authored-by: Valentin Pratz Co-authored-by: Valentin Pratz <112951103+vpratz@users.noreply.github.com> Co-authored-by: stefanradev93 Co-authored-by: Marvin Schmitt <35921281+marvinschmitt@users.noreply.github.com> --- bayesflow/networks/deep_set/deep_set.py | 4 ++-- bayesflow/networks/deep_set/equivariant_layer.py | 6 +++++- bayesflow/networks/deep_set/invariant_layer.py | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bayesflow/networks/deep_set/deep_set.py b/bayesflow/networks/deep_set/deep_set.py index 633a1508b..5fd9cc0b0 100644 --- a/bayesflow/networks/deep_set/deep_set.py +++ b/bayesflow/networks/deep_set/deep_set.py @@ -30,7 +30,7 @@ def __init__( mlp_widths_invariant_inner: Sequence[int] = (64, 64), mlp_widths_invariant_outer: Sequence[int] = (64, 64), mlp_widths_invariant_last: Sequence[int] = (64, 64), - activation: str = "gelu", + activation: str = "silu", kernel_initializer: str = "he_normal", dropout: int | float | None = 0.05, spectral_normalization: bool = False, @@ -72,7 +72,7 @@ def __init__( mlp_widths_invariant_last : Sequence[int], optional Widths of the MLP layers in the final invariant transformation. Default is (64, 64). activation : str, optional - Activation function used throughout the network, such as "gelu". Default is "gelu". + Activation function used throughout the network, such as "gelu". Default is "silu". kernel_initializer : str, optional Initialization strategy for kernel weights, such as "he_normal". Default is "he_normal". dropout : int, float, or None, optional diff --git a/bayesflow/networks/deep_set/equivariant_layer.py b/bayesflow/networks/deep_set/equivariant_layer.py index 81bd62f58..0e6587d26 100644 --- a/bayesflow/networks/deep_set/equivariant_layer.py +++ b/bayesflow/networks/deep_set/equivariant_layer.py @@ -94,6 +94,7 @@ def __init__( kernel_initializer=kernel_initializer, spectral_normalization=spectral_normalization, ) + self.out_fc_projector = keras.layers.Dense(mlp_widths_equivariant[-1], kernel_initializer=kernel_initializer) self.layer_norm = layers.LayerNormalization() if layer_norm else None @@ -137,7 +138,10 @@ def call(self, input_set: Tensor, training: bool = False, **kwargs) -> Tensor: output_set = ops.concatenate([input_set, invariant_summary], axis=-1) # Pass through final equivariant transform + residual - output_set = input_set + self.equivariant_fc(output_set, training=training) + out_fc = self.equivariant_fc(output_set, training=training) + out_projected = self.out_fc_projector(out_fc) + output_set = input_set + out_projected + if self.layer_norm is not None: output_set = self.layer_norm(output_set, training=training) diff --git a/bayesflow/networks/deep_set/invariant_layer.py b/bayesflow/networks/deep_set/invariant_layer.py index d1b6a26f9..fbf74fb2a 100644 --- a/bayesflow/networks/deep_set/invariant_layer.py +++ b/bayesflow/networks/deep_set/invariant_layer.py @@ -74,6 +74,7 @@ def __init__( kernel_initializer=kernel_initializer, spectral_normalization=spectral_normalization, ) + self.inner_projector = keras.layers.Dense(mlp_widths_inner[-1], kernel_initializer=kernel_initializer) self.outer_fc = MLP( mlp_widths_outer, @@ -82,6 +83,7 @@ def __init__( kernel_initializer=kernel_initializer, spectral_normalization=spectral_normalization, ) + self.outer_projector = keras.layers.Dense(mlp_widths_outer[-1], kernel_initializer=kernel_initializer) # Pooling function as keras layer for sum decomposition: inner( pooling( inner(set) ) ) if pooling_kwargs is None: @@ -106,8 +108,10 @@ def call(self, input_set: Tensor, training: bool = False, **kwargs) -> Tensor: """ set_summary = self.inner_fc(input_set, training=training) + set_summary = self.inner_projector(set_summary) set_summary = self.pooling_layer(set_summary, training=training) set_summary = self.outer_fc(set_summary, training=training) + set_summary = self.outer_projector(set_summary) return set_summary @sanitize_input_shape From ec938ed40b61ea8d95f76e55e27236893f0b5109 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Sun, 4 May 2025 18:50:57 +0000 Subject: [PATCH 42/46] [no ci] bf 1.1 to 2.0 notebook: minor additions/edits --- examples/From_BayesFlow_1.1_to_2.0.ipynb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/From_BayesFlow_1.1_to_2.0.ipynb b/examples/From_BayesFlow_1.1_to_2.0.ipynb index fa0e13876..b8090fa79 100644 --- a/examples/From_BayesFlow_1.1_to_2.0.ipynb +++ b/examples/From_BayesFlow_1.1_to_2.0.ipynb @@ -15,9 +15,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Long-time users of BayesFlow will notice that with the update to version 2.0 many things have changed. This short guide aims to clarify some of those changes. Users familiar with the previous Quickstart guide will notice that this notebook follows a similar structure, but assumes that users are already familiar with BayesFlow. We omit many of the the mathematical explanations in favor of demonstrating the differences in workflow. For a more detailed explanation of the BayesFlow framework, users should read, for example, the Starter Notebook on Bayesian Linear Regression.\n", + "Long-time users of BayesFlow will notice that with the update to version 2.0 many things have changed. This short guide aims to clarify some of those changes. Users familiar with the previous Quickstart guide will notice that this notebook follows a similar structure, but assumes that users are already familiar with BayesFlow. We omit many of the mathematical explanations in favor of demonstrating the differences in workflow. For a more detailed explanation of the BayesFlow framework, users should read, for example, the Starter Notebook on Bayesian Linear Regression.\n", "\n", - "Additionally to avoid confusion, similarly named objects from _BayesFlow v1.1_ will have `1.1` after their name, whereas those from _BayesFlow v2.0_ will not. Finally, a short table with a summary of the function call changes is provided at the end of the guide. " + "Additionally, to avoid confusion, similarly named objects from _BayesFlow v1.1_ will have `1.1` after their name, whereas those from _BayesFlow v2.0_ will not. Finally, a short table with a summary of the function call changes is provided at the end of the guide. " ] }, { @@ -26,7 +26,7 @@ "source": [ "## Keras Framework\n", "\n", - "BayesFlow 2.0 looks quite different from BayesFlow 1.1 because the library was refactored to replace the old backend, TensorFlow, with the new [Keras](https://keras.io) API. Users can now choose their preferred backend among the machine learning frameworks `TensorFlow`, `JAX` and `PyTorch`." + "BayesFlow 2.0 looks quite different from BayesFlow 1.1 because the library was refactored to replace the old backend, TensorFlow, with the new [Keras3](https://keras.io) API. Users can now choose their preferred backend among the machine learning frameworks [TensorFlow](https://github.com/tensorflow/tensorflow), [JAX](https://github.com/google/jax) and [PyTorch](https://github.com/pytorch/pytorch)." ] }, { @@ -52,7 +52,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In general, BayesFlow 2.0 relies much more on dictionaries since parameters are now named by convention. Many objects now expect a dictionary, and parameters and data are returned as dictionaries as well. " + "In general, BayesFlow 2.0 relies much more on dictionaries since parameters and data are now named by convention. Many objects now expect a dictionary, and parameters and data are returned as dictionaries as well. " ] }, { @@ -67,7 +67,7 @@ "\n", "Previously users would define a prior function and pass it to a `Prior1.1` object to sample prior values. The likelihood would also be specified via a function and passed to a `Simulator1.1` wrapper to produce observations for given parameter values. These were then combined in the `GenerativeModel1.1`. \n", "\n", - "In 2.0 we no longer make use of the `Prior1.1`, `Simulator1.1` or `GenerativeModel1.1` objects. Instead, the `Simulator` class comprises the whole functionality, taking the role of the `GenerativeModel1.1`. It directly produces joint samples from prior and likelihood, without creating separate `Prior1.1` and `Simulator1.1` objects first. The `bf.simulator.make_simulator` offers a convenient wrapper to create the appropriate simulator for different settings." + "In 2.0 we no longer make use of the `Prior1.1`, `Simulator1.1` or `GenerativeModel1.1` objects. Instead, the `Simulator` class comprises the whole functionality, taking the role of the `GenerativeModel1.1`. It directly produces joint samples from prior and likelihood, without creating separate `Prior1.1` and `Simulator1.1` objects first. The `bf.make_simulator` offers a convenient wrapper to create the appropriate simulator for different settings." ] }, { @@ -217,7 +217,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For the inference network there are now several implemented architectures for users to choose from. They are `FlowMatching`, `ConsistencyModel`, and `CouplingFlow`. For this demonstration we use `FlowMatching`, for explanations on the different models please refer to the other examples and the API documentation. " + "The previous version only featured the `InvertibleNetwork1.1` class as inference network. As the generative modeling landscape has evolved since then, there are now several implemented architectures for users to choose from. `CouplingFlow` features an architecture similar to `InvertibleNetwork1.1`. `FlowMatching` is a continuous flow architecture, which can express more complex distributions, at the cost of higher inference time. For this demonstration we use `FlowMatching`. For explanations on the different models please refer to the other examples and the API documentation. " ] }, { @@ -377,10 +377,11 @@ "| BayesFlow v1.1 | BayesFlow v2.0 usage |\n", "| :--------| :---------| \n", "| `Prior`, `Simulator` | Defunct and no longer standalone objects but incorporated into `bf.simulators.Simulator` | \n", - "|`GenerativeModel` | Defunct with it's functionality having been taken over by `bf.simulators.make_simulator` | \n", + "| `GenerativeModel` | Defunct with it's functionality having been taken over by `bf.make_simulator` | \n", "| `training.configurator` | Functionality taken over by `bf.adapters.Adapter` | \n", "|`Trainer` | Functionality taken over by `fit` method of `bf.approximators.Approximator` | \n", - "| `AmortizedPosterior`, `AmortizedLikelihood` | Functionality taken over by `ContinuousApproximator` | " + "| `AmortizedPosterior`, `AmortizedLikelihood` | Functionality taken over by `ContinuousApproximator` |\n", + "| `InvertibleNetwork` | Functionality taken over by `CouplingFlow`, but also other networks that are subclasses of `InferenceNetwork`, e.g. `FlowMatching` |" ] } ], From ddd4ea8e3cd53ec38f0cb65b77a16489bdeeaca6 Mon Sep 17 00:00:00 2001 From: Valentin Pratz Date: Mon, 5 May 2025 07:50:51 +0000 Subject: [PATCH 43/46] [no ci] add minor details to FAQ --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5d2dc61f0..7c8d08d2f 100644 --- a/README.md +++ b/README.md @@ -245,8 +245,9 @@ Depending on your needs, you might not want to upgrade yet if one of the followi with the new version. Loading models from version 1.x in version 2.0+ is not supported. - You require a feature that was not ported to BayesFlow 2.0+ yet. To our knowledge, this applies to: - * Two-level/Hierarchical models: `TwoLevelGenerativeModel`, `TwoLevelPrior`. - * Sensitivity analysis: functionality from the `bayesflow.sensitivity` module. + * Two-level/Hierarchical models (planned for version 2.1): `TwoLevelGenerativeModel`, `TwoLevelPrior`. + * Sensitivity analysis (partially discontinued): functionality from the `bayesflow.sensitivity` module. This is still + possible, but we do no longer offer a special module for it. We plan to add a tutorial on this, see [#455](https://github.com/bayesflow-org/bayesflow/issues/455). * MCMC (discontinued): The `bayesflow.mcmc` module. We are considering other options to enable the use of BayesFlow in an MCMC setting. * Networks: `EvidentialNetwork`. From e8d2f2cdd0e3ed34248044ffb6f44888da1744a3 Mon Sep 17 00:00:00 2001 From: Valentin Pratz <112951103+vpratz@users.noreply.github.com> Date: Mon, 5 May 2025 15:24:10 +0200 Subject: [PATCH 44/46] serialization: apply new scheme for package (breaking change) (#457) * serialization: apply new scheme for `package` (breaking) - introduces new policy for consistent naming for serilization (see #451 for a discussion): standard is the path of a module a class resides in, trucated at depth to. So for all class in bayesflow.networks, we set package="bayesflow.networks", even if the live in the bayesflow.networks.mlp submodule. - The `serializable` decorator checks this and errors if this is not followed. The check can be disabled for certain cases (e.g., classes in the experimental module, that might eventually live somewhere else). - After this commit, previously saved models will not be loadable. As we introduced a bug regarding this anyway (#451), we will accept this and should inform users about it. - usage of direct calls to `keras.saving.register_keras_serializable` were replaced with our custom decorator. * update serilization policy in dev docs * README: add not regarding breaking changes until 2.1 release * standardize use of serializable decorator * [no ci] change (de)serialize to new pipeline in transform * serialization check: exempt classes not in bayesflow module This should ensure that users that try to use our decorator with external classes do not encounter the error. Possible edge case: they also name their module "bayesflow". --------- Co-authored-by: LarsKue --- README.md | 6 +++ bayesflow/adapters/adapter.py | 2 +- bayesflow/adapters/transforms/as_set.py | 2 +- .../adapters/transforms/as_time_series.py | 2 +- bayesflow/adapters/transforms/broadcast.py | 2 +- bayesflow/adapters/transforms/concatenate.py | 2 +- bayesflow/adapters/transforms/constrain.py | 2 +- .../adapters/transforms/convert_dtype.py | 2 +- bayesflow/adapters/transforms/drop.py | 2 +- .../transforms/elementwise_transform.py | 2 +- bayesflow/adapters/transforms/expand_dims.py | 2 +- .../adapters/transforms/filter_transform.py | 2 +- bayesflow/adapters/transforms/keep.py | 2 +- bayesflow/adapters/transforms/log.py | 2 +- .../adapters/transforms/map_transform.py | 2 +- .../adapters/transforms/numpy_transform.py | 2 +- bayesflow/adapters/transforms/one_hot.py | 2 +- bayesflow/adapters/transforms/rename.py | 2 +- bayesflow/adapters/transforms/scale.py | 2 +- .../serializable_custom_transform.py | 7 ++-- bayesflow/adapters/transforms/shift.py | 2 +- bayesflow/adapters/transforms/split.py | 2 +- bayesflow/adapters/transforms/sqrt.py | 2 +- bayesflow/adapters/transforms/standardize.py | 2 +- bayesflow/adapters/transforms/to_array.py | 2 +- bayesflow/adapters/transforms/to_dict.py | 2 +- bayesflow/adapters/transforms/transform.py | 2 +- .../approximators/continuous_approximator.py | 2 +- .../model_comparison_approximator.py | 2 +- bayesflow/approximators/point_approximator.py | 2 +- bayesflow/distributions/diagonal_normal.py | 2 +- bayesflow/distributions/diagonal_student_t.py | 2 +- bayesflow/distributions/distribution.py | 2 +- bayesflow/distributions/mixture.py | 2 +- bayesflow/experimental/cif/cif.py | 5 ++- .../experimental/cif/conditional_gaussian.py | 5 ++- .../continuous_time_consistency_model.py | 3 +- .../free_form_flow/free_form_flow.py | 3 +- bayesflow/experimental/resnet/dense_resnet.py | 3 +- bayesflow/experimental/resnet/double_conv.py | 3 +- .../experimental/resnet/double_linear.py | 3 +- bayesflow/experimental/resnet/resnet.py | 3 +- bayesflow/links/ordered.py | 4 +- bayesflow/links/ordered_quantiles.py | 4 +- bayesflow/links/positive_definite.py | 5 +-- bayesflow/metrics/maximum_mean_discrepancy.py | 2 +- bayesflow/metrics/root_mean_squard_error.py | 2 +- .../consistency_models/consistency_model.py | 2 +- bayesflow/networks/coupling_flow/actnorm.py | 2 +- .../networks/coupling_flow/coupling_flow.py | 2 +- .../coupling_flow/couplings/dual_coupling.py | 2 +- .../couplings/single_coupling.py | 2 +- .../permutations/fixed_permutation.py | 2 +- .../coupling_flow/permutations/orthogonal.py | 2 +- .../coupling_flow/permutations/random.py | 2 +- .../coupling_flow/permutations/swap.py | 2 +- .../transforms/affine_transform.py | 2 +- .../transforms/spline_transform.py | 2 +- bayesflow/networks/deep_set/deep_set.py | 2 +- .../networks/deep_set/equivariant_layer.py | 2 +- .../networks/deep_set/invariant_layer.py | 2 +- .../networks/embeddings/fourier_embedding.py | 2 +- .../embeddings/recurrent_embedding.py | 2 +- bayesflow/networks/embeddings/time2vec.py | 2 +- .../networks/flow_matching/flow_matching.py | 2 +- bayesflow/networks/mlp/mlp.py | 2 +- bayesflow/networks/point_inference_network.py | 2 +- bayesflow/networks/residual/residual.py | 2 +- .../time_series_network/skip_recurrent.py | 2 +- .../time_series_network.py | 2 +- .../transformers/fusion_transformer.py | 2 +- bayesflow/networks/transformers/isab.py | 2 +- bayesflow/networks/transformers/mab.py | 2 +- bayesflow/networks/transformers/pma.py | 2 +- bayesflow/networks/transformers/sab.py | 2 +- .../networks/transformers/set_transformer.py | 2 +- .../transformers/time_series_transformer.py | 2 +- bayesflow/scores/mean_score.py | 5 +-- bayesflow/scores/median_score.py | 5 +-- bayesflow/scores/multivariate_normal_score.py | 4 +- bayesflow/scores/normed_difference_score.py | 4 +- .../scores/parametric_distribution_score.py | 5 +-- bayesflow/scores/quantile_score.py | 4 +- bayesflow/scores/scoring_rule.py | 2 +- bayesflow/utils/serialization.py | 39 ++++++++++++++----- bayesflow/wrappers/mamba/mamba.py | 2 +- bayesflow/wrappers/mamba/mamba_block.py | 2 +- docsrc/source/development/serialization.md | 11 +++++- .../test_utils/test_serialize_deserialize.py | 4 +- tests/test_workflows/conftest.py | 2 +- 90 files changed, 155 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index 7c8d08d2f..9b5ef3d47 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,12 @@ It provides users and researchers with: BayesFlow (version 2+) is designed to be a flexible and efficient tool that enables rapid statistical inference fueled by continuous progress in generative AI and Bayesian inference. +> [!IMPORTANT] +> As the 2.0 version introduced many new features, we still have to make breaking changes from time to time. +> This especially concerns **saving and loading** of models. We aim to stabilize this from the 2.1 release onwards. +> Until then, consider pinning your BayesFlow 2.0 installation to an exact version, or re-training after an update +> for less costly models. + ## Important Note for Existing Users You are currently looking at BayesFlow 2.0+, which is a complete rewrite of the library. diff --git a/bayesflow/adapters/adapter.py b/bayesflow/adapters/adapter.py index ab6800d8a..a17a59d81 100644 --- a/bayesflow/adapters/adapter.py +++ b/bayesflow/adapters/adapter.py @@ -29,7 +29,7 @@ from .transforms.filter_transform import Predicate -@serializable +@serializable("bayesflow.adapters") class Adapter(MutableSequence[Transform]): """ Defines an adapter to apply various transforms to data. diff --git a/bayesflow/adapters/transforms/as_set.py b/bayesflow/adapters/transforms/as_set.py index f4d5bdfc5..903536bc4 100644 --- a/bayesflow/adapters/transforms/as_set.py +++ b/bayesflow/adapters/transforms/as_set.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class AsSet(ElementwiseTransform): """The `.as_set(["x", "y"])` transform indicates that both `x` and `y` are treated as sets. diff --git a/bayesflow/adapters/transforms/as_time_series.py b/bayesflow/adapters/transforms/as_time_series.py index 3f4d2a2c5..d7791352c 100644 --- a/bayesflow/adapters/transforms/as_time_series.py +++ b/bayesflow/adapters/transforms/as_time_series.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class AsTimeSeries(ElementwiseTransform): """The `.as_time_series` transform can be used to indicate that variables shall be treated as time series. diff --git a/bayesflow/adapters/transforms/broadcast.py b/bayesflow/adapters/transforms/broadcast.py index 8667ec0c7..646e1f72e 100644 --- a/bayesflow/adapters/transforms/broadcast.py +++ b/bayesflow/adapters/transforms/broadcast.py @@ -6,7 +6,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class Broadcast(Transform): """ Broadcasts arrays or scalars to the shape of a given other array. diff --git a/bayesflow/adapters/transforms/concatenate.py b/bayesflow/adapters/transforms/concatenate.py index 91ea9178b..ac3700616 100644 --- a/bayesflow/adapters/transforms/concatenate.py +++ b/bayesflow/adapters/transforms/concatenate.py @@ -7,7 +7,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class Concatenate(Transform): """Concatenate multiple arrays into a new key. Used to specify how data variables should be treated by the network. diff --git a/bayesflow/adapters/transforms/constrain.py b/bayesflow/adapters/transforms/constrain.py index a4ca0be25..d01211dfc 100644 --- a/bayesflow/adapters/transforms/constrain.py +++ b/bayesflow/adapters/transforms/constrain.py @@ -11,7 +11,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class Constrain(ElementwiseTransform): """ Constrains neural network predictions of a data variable to specified bounds. diff --git a/bayesflow/adapters/transforms/convert_dtype.py b/bayesflow/adapters/transforms/convert_dtype.py index e68815269..8cd21b4cc 100644 --- a/bayesflow/adapters/transforms/convert_dtype.py +++ b/bayesflow/adapters/transforms/convert_dtype.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class ConvertDType(ElementwiseTransform): """ Default transform used to convert all floats from float64 to float32 to be in line with keras framework. diff --git a/bayesflow/adapters/transforms/drop.py b/bayesflow/adapters/transforms/drop.py index 91dcd6a28..5073027e6 100644 --- a/bayesflow/adapters/transforms/drop.py +++ b/bayesflow/adapters/transforms/drop.py @@ -5,7 +5,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class Drop(Transform): """ Transform to drop variables from further calculation. diff --git a/bayesflow/adapters/transforms/elementwise_transform.py b/bayesflow/adapters/transforms/elementwise_transform.py index 7d603d517..020301749 100644 --- a/bayesflow/adapters/transforms/elementwise_transform.py +++ b/bayesflow/adapters/transforms/elementwise_transform.py @@ -3,7 +3,7 @@ from bayesflow.utils.serialization import serializable, deserialize -@serializable +@serializable("bayesflow.adapters") class ElementwiseTransform: """Base class on which other transforms are based""" diff --git a/bayesflow/adapters/transforms/expand_dims.py b/bayesflow/adapters/transforms/expand_dims.py index e44d133b8..0f4151d37 100644 --- a/bayesflow/adapters/transforms/expand_dims.py +++ b/bayesflow/adapters/transforms/expand_dims.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class ExpandDims(ElementwiseTransform): """ Expand the shape of an array. diff --git a/bayesflow/adapters/transforms/filter_transform.py b/bayesflow/adapters/transforms/filter_transform.py index 7eccf370b..4dc2c8008 100644 --- a/bayesflow/adapters/transforms/filter_transform.py +++ b/bayesflow/adapters/transforms/filter_transform.py @@ -14,7 +14,7 @@ def __call__(self, key: str, value: np.ndarray, inverse: bool) -> bool: raise NotImplementedError -@serializable +@serializable("bayesflow.adapters") class FilterTransform(Transform): """ Implements a transform that applies a different transform on a subset of the data. diff --git a/bayesflow/adapters/transforms/keep.py b/bayesflow/adapters/transforms/keep.py index 56f395166..c69d01ca3 100644 --- a/bayesflow/adapters/transforms/keep.py +++ b/bayesflow/adapters/transforms/keep.py @@ -5,7 +5,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class Keep(Transform): """ Name the data parameters that should be kept for futher calculation. diff --git a/bayesflow/adapters/transforms/log.py b/bayesflow/adapters/transforms/log.py index d5f559b4f..a42c43ef0 100644 --- a/bayesflow/adapters/transforms/log.py +++ b/bayesflow/adapters/transforms/log.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class Log(ElementwiseTransform): """Log transforms a variable. diff --git a/bayesflow/adapters/transforms/map_transform.py b/bayesflow/adapters/transforms/map_transform.py index 5da8292af..15c5c945d 100644 --- a/bayesflow/adapters/transforms/map_transform.py +++ b/bayesflow/adapters/transforms/map_transform.py @@ -6,7 +6,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class MapTransform(Transform): """ Implements a transform that applies a set of elementwise transforms diff --git a/bayesflow/adapters/transforms/numpy_transform.py b/bayesflow/adapters/transforms/numpy_transform.py index 29d25dc67..a19216dd2 100644 --- a/bayesflow/adapters/transforms/numpy_transform.py +++ b/bayesflow/adapters/transforms/numpy_transform.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class NumpyTransform(ElementwiseTransform): """ A class to apply element-wise transformations using plain NumPy functions. diff --git a/bayesflow/adapters/transforms/one_hot.py b/bayesflow/adapters/transforms/one_hot.py index e097a28f9..bbed8bf5d 100644 --- a/bayesflow/adapters/transforms/one_hot.py +++ b/bayesflow/adapters/transforms/one_hot.py @@ -6,7 +6,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class OneHot(ElementwiseTransform): """ Changes data to be one-hot encoded. diff --git a/bayesflow/adapters/transforms/rename.py b/bayesflow/adapters/transforms/rename.py index 746ef5a80..bec3388b0 100644 --- a/bayesflow/adapters/transforms/rename.py +++ b/bayesflow/adapters/transforms/rename.py @@ -3,7 +3,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class Rename(Transform): """ Transform to rename keys in data dictionary. Useful to rename variables to match those required by diff --git a/bayesflow/adapters/transforms/scale.py b/bayesflow/adapters/transforms/scale.py index 96b2ff927..d7c1aa2a7 100644 --- a/bayesflow/adapters/transforms/scale.py +++ b/bayesflow/adapters/transforms/scale.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class Scale(ElementwiseTransform): def __init__(self, scale: np.typing.ArrayLike): self.scale = np.array(scale) diff --git a/bayesflow/adapters/transforms/serializable_custom_transform.py b/bayesflow/adapters/transforms/serializable_custom_transform.py index 75d588afd..248fc0ec5 100644 --- a/bayesflow/adapters/transforms/serializable_custom_transform.py +++ b/bayesflow/adapters/transforms/serializable_custom_transform.py @@ -1,18 +1,17 @@ from collections.abc import Callable import numpy as np from keras.saving import ( - deserialize_keras_object as deserialize, - register_keras_serializable as serializable, - serialize_keras_object as serialize, get_registered_name, get_registered_object, ) + +from bayesflow.utils.serialization import deserialize, serializable, serialize from .elementwise_transform import ElementwiseTransform from ...utils import filter_kwargs import inspect -@serializable(package="bayesflow.adapters") +@serializable("bayesflow.adapters") class SerializableCustomTransform(ElementwiseTransform): """ Transforms a parameter using a pair of registered serializable forward and inverse functions. diff --git a/bayesflow/adapters/transforms/shift.py b/bayesflow/adapters/transforms/shift.py index b7c9659d2..5923b4e49 100644 --- a/bayesflow/adapters/transforms/shift.py +++ b/bayesflow/adapters/transforms/shift.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class Shift(ElementwiseTransform): def __init__(self, shift: np.typing.ArrayLike): self.shift = np.array(shift) diff --git a/bayesflow/adapters/transforms/split.py b/bayesflow/adapters/transforms/split.py index 919db4e08..4c0ae9f65 100644 --- a/bayesflow/adapters/transforms/split.py +++ b/bayesflow/adapters/transforms/split.py @@ -6,7 +6,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class Split(Transform): """This is the effective inverse of the :py:class:`~Concatenate` Transform. diff --git a/bayesflow/adapters/transforms/sqrt.py b/bayesflow/adapters/transforms/sqrt.py index 4ef1370dc..bcfe49136 100644 --- a/bayesflow/adapters/transforms/sqrt.py +++ b/bayesflow/adapters/transforms/sqrt.py @@ -5,7 +5,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class Sqrt(ElementwiseTransform): """Square-root transform a variable. diff --git a/bayesflow/adapters/transforms/standardize.py b/bayesflow/adapters/transforms/standardize.py index 9699819b9..a1c3c5a3d 100644 --- a/bayesflow/adapters/transforms/standardize.py +++ b/bayesflow/adapters/transforms/standardize.py @@ -7,7 +7,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class Standardize(ElementwiseTransform): """ Transform that when applied standardizes data using typical z-score standardization diff --git a/bayesflow/adapters/transforms/to_array.py b/bayesflow/adapters/transforms/to_array.py index 9d5381ca0..fe1b82f2d 100644 --- a/bayesflow/adapters/transforms/to_array.py +++ b/bayesflow/adapters/transforms/to_array.py @@ -7,7 +7,7 @@ from .elementwise_transform import ElementwiseTransform -@serializable +@serializable("bayesflow.adapters") class ToArray(ElementwiseTransform): """ Checks provided data for any non-arrays and converts them to numpy arrays. diff --git a/bayesflow/adapters/transforms/to_dict.py b/bayesflow/adapters/transforms/to_dict.py index 6babb2a40..cfc4ec00d 100644 --- a/bayesflow/adapters/transforms/to_dict.py +++ b/bayesflow/adapters/transforms/to_dict.py @@ -6,7 +6,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.adapters") class ToDict(Transform): """Convert non-dict batches (e.g., pandas.DataFrame) to dict batches""" diff --git a/bayesflow/adapters/transforms/transform.py b/bayesflow/adapters/transforms/transform.py index ed3058e15..0bc6331bc 100644 --- a/bayesflow/adapters/transforms/transform.py +++ b/bayesflow/adapters/transforms/transform.py @@ -3,7 +3,7 @@ from bayesflow.utils.serialization import serializable, deserialize -@serializable +@serializable("bayesflow.adapters") class Transform: """ Base class on which other transforms are based diff --git a/bayesflow/approximators/continuous_approximator.py b/bayesflow/approximators/continuous_approximator.py index 834521d4b..3e43a8917 100644 --- a/bayesflow/approximators/continuous_approximator.py +++ b/bayesflow/approximators/continuous_approximator.py @@ -13,7 +13,7 @@ from .approximator import Approximator -@serializable +@serializable("bayesflow.approximators") class ContinuousApproximator(Approximator): """ Defines a workflow for performing fast posterior or likelihood inference. diff --git a/bayesflow/approximators/model_comparison_approximator.py b/bayesflow/approximators/model_comparison_approximator.py index 028e8837a..86af4ee33 100644 --- a/bayesflow/approximators/model_comparison_approximator.py +++ b/bayesflow/approximators/model_comparison_approximator.py @@ -14,7 +14,7 @@ from .approximator import Approximator -@serializable +@serializable("bayesflow.approximators") class ModelComparisonApproximator(Approximator): """ Defines an approximator for model (simulator) comparison, where the (discrete) posterior model probabilities are diff --git a/bayesflow/approximators/point_approximator.py b/bayesflow/approximators/point_approximator.py index 1e407e2a6..b3d90781c 100644 --- a/bayesflow/approximators/point_approximator.py +++ b/bayesflow/approximators/point_approximator.py @@ -11,7 +11,7 @@ from .continuous_approximator import ContinuousApproximator -@serializable +@serializable("bayesflow.approximators") class PointApproximator(ContinuousApproximator): """ A workflow for fast amortized point estimation of a conditional distribution. diff --git a/bayesflow/distributions/diagonal_normal.py b/bayesflow/distributions/diagonal_normal.py index 98a127b1c..f8d93b945 100644 --- a/bayesflow/distributions/diagonal_normal.py +++ b/bayesflow/distributions/diagonal_normal.py @@ -12,7 +12,7 @@ from .distribution import Distribution -@serializable +@serializable("bayesflow.distributions") class DiagonalNormal(Distribution): """Implements a backend-agnostic diagonal Gaussian distribution.""" diff --git a/bayesflow/distributions/diagonal_student_t.py b/bayesflow/distributions/diagonal_student_t.py index cd32a67fb..98e3fb7eb 100644 --- a/bayesflow/distributions/diagonal_student_t.py +++ b/bayesflow/distributions/diagonal_student_t.py @@ -13,7 +13,7 @@ from .distribution import Distribution -@serializable +@serializable("bayesflow.distributions") class DiagonalStudentT(Distribution): """Implements a backend-agnostic diagonal Student-t distribution.""" diff --git a/bayesflow/distributions/distribution.py b/bayesflow/distributions/distribution.py index 1d3a83962..3689f0d9f 100644 --- a/bayesflow/distributions/distribution.py +++ b/bayesflow/distributions/distribution.py @@ -5,7 +5,7 @@ from bayesflow.utils.serialization import serializable, deserialize -@serializable +@serializable("bayesflow.distributions") class Distribution(keras.Layer): def __init__(self, **kwargs): super().__init__(**layer_kwargs(kwargs)) diff --git a/bayesflow/distributions/mixture.py b/bayesflow/distributions/mixture.py index d7f6bd758..a7bf2ea27 100644 --- a/bayesflow/distributions/mixture.py +++ b/bayesflow/distributions/mixture.py @@ -11,7 +11,7 @@ from bayesflow.distributions import Distribution -@serializable +@serializable("bayesflow.distributions") class Mixture(Distribution): """Utility class for a backend-agnostic mixture distributions.""" diff --git a/bayesflow/experimental/cif/cif.py b/bayesflow/experimental/cif/cif.py index 8742501b3..bd776f93e 100644 --- a/bayesflow/experimental/cif/cif.py +++ b/bayesflow/experimental/cif/cif.py @@ -1,7 +1,7 @@ import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.types import Shape, Tensor +from bayesflow.utils.serialization import serializable from bayesflow.networks.inference_network import InferenceNetwork from bayesflow.networks.coupling_flow import CouplingFlow @@ -9,7 +9,8 @@ from .conditional_gaussian import ConditionalGaussian -@serializable(package="bayesflow.networks") +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class CIF(InferenceNetwork): """Implements a continuously indexed flow (CIF) with a `CouplingFlow` bijection and `ConditionalGaussian` distributions p and q. Improves on diff --git a/bayesflow/experimental/cif/conditional_gaussian.py b/bayesflow/experimental/cif/conditional_gaussian.py index d11f3fb65..ebba47a2e 100644 --- a/bayesflow/experimental/cif/conditional_gaussian.py +++ b/bayesflow/experimental/cif/conditional_gaussian.py @@ -1,13 +1,14 @@ import keras -from keras.saving import register_keras_serializable import numpy as np from bayesflow.networks.mlp import MLP from bayesflow.types import Shape, Tensor from bayesflow.utils import layer_kwargs +from bayesflow.utils.serialization import serializable -@register_keras_serializable(package="bayesflow.networks.cif") +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class ConditionalGaussian(keras.Layer): """Implements a conditional gaussian distribution with neural networks for the means and standard deviations respectively. Bulit in reference to [1]. diff --git a/bayesflow/experimental/continuous_time_consistency_model.py b/bayesflow/experimental/continuous_time_consistency_model.py index 54417cd07..b1c751454 100644 --- a/bayesflow/experimental/continuous_time_consistency_model.py +++ b/bayesflow/experimental/continuous_time_consistency_model.py @@ -22,7 +22,8 @@ from bayesflow.networks.embeddings import FourierEmbedding -@serializable +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class ContinuousTimeConsistencyModel(InferenceNetwork): """Implements an sCM (simple, stable, and scalable Consistency Model) with continous-time Consistency Training (CT) as described in [1]. diff --git a/bayesflow/experimental/free_form_flow/free_form_flow.py b/bayesflow/experimental/free_form_flow/free_form_flow.py index 61937d56f..12bb97b93 100644 --- a/bayesflow/experimental/free_form_flow/free_form_flow.py +++ b/bayesflow/experimental/free_form_flow/free_form_flow.py @@ -19,7 +19,8 @@ from bayesflow.networks import InferenceNetwork -@serializable +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class FreeFormFlow(InferenceNetwork): """Implements a dimensionality-preserving Free-form Flow. Incorporates ideas from [1-2]. diff --git a/bayesflow/experimental/resnet/dense_resnet.py b/bayesflow/experimental/resnet/dense_resnet.py index fa380969f..93ff59d3f 100644 --- a/bayesflow/experimental/resnet/dense_resnet.py +++ b/bayesflow/experimental/resnet/dense_resnet.py @@ -8,7 +8,8 @@ from .double_linear import DoubleLinear -@serializable +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class DenseResNet(keras.Sequential): """ Implements the fully-connected analogue of the ResNet architecture. diff --git a/bayesflow/experimental/resnet/double_conv.py b/bayesflow/experimental/resnet/double_conv.py index c70e37323..a2b6bbc88 100644 --- a/bayesflow/experimental/resnet/double_conv.py +++ b/bayesflow/experimental/resnet/double_conv.py @@ -5,7 +5,8 @@ from bayesflow.utils.serialization import deserialize, serializable, serialize -@serializable +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class DoubleConv(keras.Sequential): def __init__( self, diff --git a/bayesflow/experimental/resnet/double_linear.py b/bayesflow/experimental/resnet/double_linear.py index e2138c8b0..aae72fa39 100644 --- a/bayesflow/experimental/resnet/double_linear.py +++ b/bayesflow/experimental/resnet/double_linear.py @@ -5,7 +5,8 @@ from bayesflow.utils.serialization import deserialize, serializable, serialize -@serializable +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class DoubleLinear(keras.Sequential): def __init__( self, diff --git a/bayesflow/experimental/resnet/resnet.py b/bayesflow/experimental/resnet/resnet.py index 07f1f2cda..862e0ac98 100644 --- a/bayesflow/experimental/resnet/resnet.py +++ b/bayesflow/experimental/resnet/resnet.py @@ -8,7 +8,8 @@ from .double_conv import DoubleConv -@serializable +# disable module check, use potential module after moving from experimental +@serializable("bayesflow.networks", disable_module_check=True) class ResNet(keras.Sequential): """ Implements the ResNet architecture. diff --git a/bayesflow/links/ordered.py b/bayesflow/links/ordered.py index 77545b6f8..25caf5350 100644 --- a/bayesflow/links/ordered.py +++ b/bayesflow/links/ordered.py @@ -1,11 +1,11 @@ import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.utils import layer_kwargs from bayesflow.utils.decorators import sanitize_input_shape +from bayesflow.utils.serialization import serializable -@serializable(package="links.ordered") +@serializable("bayesflow.links") class Ordered(keras.Layer): """Activation function to link to a tensor which is monotonously increasing along a specified axis.""" diff --git a/bayesflow/links/ordered_quantiles.py b/bayesflow/links/ordered_quantiles.py index d4f4caba2..81b2c0cc7 100644 --- a/bayesflow/links/ordered_quantiles.py +++ b/bayesflow/links/ordered_quantiles.py @@ -1,14 +1,14 @@ import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.utils import layer_kwargs, logging +from bayesflow.utils.serialization import serializable from collections.abc import Sequence from .ordered import Ordered -@serializable(package="links.ordered_quantiles") +@serializable("bayesflow.links") class OrderedQuantiles(Ordered): """Activation function to link to monotonously increasing quantile estimates.""" diff --git a/bayesflow/links/positive_definite.py b/bayesflow/links/positive_definite.py index 28c937f86..909ac2792 100644 --- a/bayesflow/links/positive_definite.py +++ b/bayesflow/links/positive_definite.py @@ -1,12 +1,11 @@ import keras -from keras.saving import register_keras_serializable as serializable - from bayesflow.types import Tensor from bayesflow.utils import layer_kwargs, fill_triangular_matrix +from bayesflow.utils.serialization import serializable -@serializable(package="bayesflow.links") +@serializable("bayesflow.links") class PositiveDefinite(keras.Layer): """Activation function to link from flat elements of a lower triangular matrix to a positive definite matrix.""" diff --git a/bayesflow/metrics/maximum_mean_discrepancy.py b/bayesflow/metrics/maximum_mean_discrepancy.py index 37af44fd4..de4ee32f1 100644 --- a/bayesflow/metrics/maximum_mean_discrepancy.py +++ b/bayesflow/metrics/maximum_mean_discrepancy.py @@ -6,7 +6,7 @@ from .functional import maximum_mean_discrepancy -@serializable +@serializable("bayesflow.metrics") class MaximumMeanDiscrepancy(keras.Metric): def __init__( self, diff --git a/bayesflow/metrics/root_mean_squard_error.py b/bayesflow/metrics/root_mean_squard_error.py index 97de62e6a..8827095e9 100644 --- a/bayesflow/metrics/root_mean_squard_error.py +++ b/bayesflow/metrics/root_mean_squard_error.py @@ -5,7 +5,7 @@ from .functional import root_mean_squared_error -@serializable +@serializable("bayesflow.metrics") class RootMeanSquaredError(keras.metrics.MeanMetricWrapper): def __init__(self, name="root_mean_squared_error", dtype=None, **kwargs): fn = partial(root_mean_squared_error, **kwargs) diff --git a/bayesflow/networks/consistency_models/consistency_model.py b/bayesflow/networks/consistency_models/consistency_model.py index b8d4c56ed..8d36c1736 100644 --- a/bayesflow/networks/consistency_models/consistency_model.py +++ b/bayesflow/networks/consistency_models/consistency_model.py @@ -12,7 +12,7 @@ from ..inference_network import InferenceNetwork -@serializable +@serializable("bayesflow.networks") class ConsistencyModel(InferenceNetwork): """Implements a Consistency Model with Consistency Training (CT) a described in [1-2]. The adaptations to CT described in [2] were taken into account in our implementation for ABI [3]. diff --git a/bayesflow/networks/coupling_flow/actnorm.py b/bayesflow/networks/coupling_flow/actnorm.py index 5221caea1..81cdc425d 100644 --- a/bayesflow/networks/coupling_flow/actnorm.py +++ b/bayesflow/networks/coupling_flow/actnorm.py @@ -6,7 +6,7 @@ from .invertible_layer import InvertibleLayer -@serializable +@serializable("bayesflow.networks") class ActNorm(InvertibleLayer): """Implements an Activation Normalization (ActNorm) Layer. Activation Normalization is learned invertible normalization, using a scale (s) and a bias (b) vector:: diff --git a/bayesflow/networks/coupling_flow/coupling_flow.py b/bayesflow/networks/coupling_flow/coupling_flow.py index 203962b0f..28954d7d2 100644 --- a/bayesflow/networks/coupling_flow/coupling_flow.py +++ b/bayesflow/networks/coupling_flow/coupling_flow.py @@ -13,7 +13,7 @@ from ..inference_network import InferenceNetwork -@serializable +@serializable("bayesflow.networks") class CouplingFlow(InferenceNetwork): """Implements a coupling flow as a sequence of dual couplings with permutations and activation normalization. Incorporates ideas from [1-5]. diff --git a/bayesflow/networks/coupling_flow/couplings/dual_coupling.py b/bayesflow/networks/coupling_flow/couplings/dual_coupling.py index 67db6e269..462bc02d6 100644 --- a/bayesflow/networks/coupling_flow/couplings/dual_coupling.py +++ b/bayesflow/networks/coupling_flow/couplings/dual_coupling.py @@ -9,7 +9,7 @@ from ..invertible_layer import InvertibleLayer -@serializable +@serializable("bayesflow.networks") class DualCoupling(InvertibleLayer): def __init__(self, subnet: str | type = "mlp", transform: str = "affine", **kwargs): super().__init__(**kwargs) diff --git a/bayesflow/networks/coupling_flow/couplings/single_coupling.py b/bayesflow/networks/coupling_flow/couplings/single_coupling.py index d2703cfa7..7bd6aaf3b 100644 --- a/bayesflow/networks/coupling_flow/couplings/single_coupling.py +++ b/bayesflow/networks/coupling_flow/couplings/single_coupling.py @@ -8,7 +8,7 @@ from ..transforms import find_transform -@serializable +@serializable("bayesflow.networks") class SingleCoupling(InvertibleLayer): """ Implements a single coupling layer as a composition of a subnet and a transform. diff --git a/bayesflow/networks/coupling_flow/permutations/fixed_permutation.py b/bayesflow/networks/coupling_flow/permutations/fixed_permutation.py index d93f27ae5..68591a172 100644 --- a/bayesflow/networks/coupling_flow/permutations/fixed_permutation.py +++ b/bayesflow/networks/coupling_flow/permutations/fixed_permutation.py @@ -6,7 +6,7 @@ from ..invertible_layer import InvertibleLayer -@serializable +@serializable("bayesflow.networks") class FixedPermutation(InvertibleLayer): """ Interface class for permutations with no learnable parameters. Child classes should diff --git a/bayesflow/networks/coupling_flow/permutations/orthogonal.py b/bayesflow/networks/coupling_flow/permutations/orthogonal.py index a28fe7965..54bfbf901 100644 --- a/bayesflow/networks/coupling_flow/permutations/orthogonal.py +++ b/bayesflow/networks/coupling_flow/permutations/orthogonal.py @@ -6,7 +6,7 @@ from ..invertible_layer import InvertibleLayer -@serializable +@serializable("bayesflow.networks") class OrthogonalPermutation(InvertibleLayer): """Implements a learnable orthogonal transformation according to [1]. Can be used as an alternative to a fixed ``Permutation`` layer. diff --git a/bayesflow/networks/coupling_flow/permutations/random.py b/bayesflow/networks/coupling_flow/permutations/random.py index 82d7f39ff..522d48c63 100644 --- a/bayesflow/networks/coupling_flow/permutations/random.py +++ b/bayesflow/networks/coupling_flow/permutations/random.py @@ -6,7 +6,7 @@ from .fixed_permutation import FixedPermutation -@serializable +@serializable("bayesflow.networks") class RandomPermutation(FixedPermutation): # noinspection PyMethodOverriding def build(self, xz_shape: Shape, **kwargs) -> None: diff --git a/bayesflow/networks/coupling_flow/permutations/swap.py b/bayesflow/networks/coupling_flow/permutations/swap.py index c5f707a1a..bb7f641b9 100644 --- a/bayesflow/networks/coupling_flow/permutations/swap.py +++ b/bayesflow/networks/coupling_flow/permutations/swap.py @@ -6,7 +6,7 @@ from .fixed_permutation import FixedPermutation -@serializable +@serializable("bayesflow.networks") class Swap(FixedPermutation): def build(self, xz_shape: Shape, **kwargs) -> None: shift = xz_shape[-1] // 2 diff --git a/bayesflow/networks/coupling_flow/transforms/affine_transform.py b/bayesflow/networks/coupling_flow/transforms/affine_transform.py index 9e8c4a9e1..1d66b0bfb 100644 --- a/bayesflow/networks/coupling_flow/transforms/affine_transform.py +++ b/bayesflow/networks/coupling_flow/transforms/affine_transform.py @@ -7,7 +7,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.networks") class AffineTransform(Transform): def __init__(self, clamp: bool = True, **kwargs): super().__init__(**kwargs) diff --git a/bayesflow/networks/coupling_flow/transforms/spline_transform.py b/bayesflow/networks/coupling_flow/transforms/spline_transform.py index d5b0cf4b3..28b9c4415 100644 --- a/bayesflow/networks/coupling_flow/transforms/spline_transform.py +++ b/bayesflow/networks/coupling_flow/transforms/spline_transform.py @@ -10,7 +10,7 @@ from .transform import Transform -@serializable +@serializable("bayesflow.networks") class SplineTransform(Transform): def __init__( self, diff --git a/bayesflow/networks/deep_set/deep_set.py b/bayesflow/networks/deep_set/deep_set.py index 5fd9cc0b0..9c9d0ad23 100644 --- a/bayesflow/networks/deep_set/deep_set.py +++ b/bayesflow/networks/deep_set/deep_set.py @@ -11,7 +11,7 @@ from ..summary_network import SummaryNetwork -@serializable +@serializable("bayesflow.networks") class DeepSet(SummaryNetwork): """Implements a deep set encoder introduced in [1] for learning permutation-invariant representations of set-based data, as generated by exchangeable models. diff --git a/bayesflow/networks/deep_set/equivariant_layer.py b/bayesflow/networks/deep_set/equivariant_layer.py index 0e6587d26..7e35ad9bb 100644 --- a/bayesflow/networks/deep_set/equivariant_layer.py +++ b/bayesflow/networks/deep_set/equivariant_layer.py @@ -13,7 +13,7 @@ from .invariant_layer import InvariantLayer -@serializable +@serializable("bayesflow.networks") class EquivariantLayer(keras.Layer): """Implements an equivariant module performing an equivariant transform. diff --git a/bayesflow/networks/deep_set/invariant_layer.py b/bayesflow/networks/deep_set/invariant_layer.py index fbf74fb2a..2f29c6b8d 100644 --- a/bayesflow/networks/deep_set/invariant_layer.py +++ b/bayesflow/networks/deep_set/invariant_layer.py @@ -11,7 +11,7 @@ from ..mlp import MLP -@serializable +@serializable("bayesflow.networks") class InvariantLayer(keras.Layer): """Implements an invariant module performing a permutation-invariant transform. diff --git a/bayesflow/networks/embeddings/fourier_embedding.py b/bayesflow/networks/embeddings/fourier_embedding.py index 65b5938d7..21924ee60 100644 --- a/bayesflow/networks/embeddings/fourier_embedding.py +++ b/bayesflow/networks/embeddings/fourier_embedding.py @@ -7,7 +7,7 @@ from bayesflow.utils.serialization import serializable -@serializable +@serializable("bayesflow.networks") class FourierEmbedding(keras.Layer): """Implements a Fourier projection with normally distributed frequencies.""" diff --git a/bayesflow/networks/embeddings/recurrent_embedding.py b/bayesflow/networks/embeddings/recurrent_embedding.py index df7c00f32..3fa82868d 100644 --- a/bayesflow/networks/embeddings/recurrent_embedding.py +++ b/bayesflow/networks/embeddings/recurrent_embedding.py @@ -6,7 +6,7 @@ from bayesflow.utils.serialization import serializable -@serializable +@serializable("bayesflow.networks") class RecurrentEmbedding(keras.Layer): """Implements a recurrent network for flexibly embedding time vectors.""" diff --git a/bayesflow/networks/embeddings/time2vec.py b/bayesflow/networks/embeddings/time2vec.py index 4c9c3a87f..b52ca77d8 100644 --- a/bayesflow/networks/embeddings/time2vec.py +++ b/bayesflow/networks/embeddings/time2vec.py @@ -5,7 +5,7 @@ from bayesflow.utils.serialization import serializable -@serializable +@serializable("bayesflow.networks") class Time2Vec(keras.Layer): """ Implements the Time2Vec learnbale embedding from [1]. diff --git a/bayesflow/networks/flow_matching/flow_matching.py b/bayesflow/networks/flow_matching/flow_matching.py index 3c0190467..797d4c62d 100644 --- a/bayesflow/networks/flow_matching/flow_matching.py +++ b/bayesflow/networks/flow_matching/flow_matching.py @@ -19,7 +19,7 @@ from ..inference_network import InferenceNetwork -@serializable +@serializable("bayesflow.networks") class FlowMatching(InferenceNetwork): """Implements Optimal Transport Flow Matching, originally introduced as Rectified Flow, with ideas incorporated from [1-3]. diff --git a/bayesflow/networks/mlp/mlp.py b/bayesflow/networks/mlp/mlp.py index 1ac11fe1a..11dcdca2b 100644 --- a/bayesflow/networks/mlp/mlp.py +++ b/bayesflow/networks/mlp/mlp.py @@ -9,7 +9,7 @@ from ..residual import Residual -@serializable +@serializable("bayesflow.networks") class MLP(keras.Sequential): """ Implements a simple configurable MLP with optional residual connections and dropout. diff --git a/bayesflow/networks/point_inference_network.py b/bayesflow/networks/point_inference_network.py index 63094a2a8..402632355 100644 --- a/bayesflow/networks/point_inference_network.py +++ b/bayesflow/networks/point_inference_network.py @@ -7,7 +7,7 @@ from bayesflow.utils.decorators import allow_batch_size -@serializable(package="networks.point_inference_network") +@serializable("bayesflow.networks") class PointInferenceNetwork(keras.Layer): """Implements point estimation for user specified scoring rules by a shared feed forward architecture with separate heads for each scoring rule. diff --git a/bayesflow/networks/residual/residual.py b/bayesflow/networks/residual/residual.py index f2ca54b51..edf32782c 100644 --- a/bayesflow/networks/residual/residual.py +++ b/bayesflow/networks/residual/residual.py @@ -7,7 +7,7 @@ from bayesflow.utils.serialization import deserialize, serializable, serialize -@serializable +@serializable("bayesflow.networks") class Residual(keras.Sequential): def __init__(self, *layers: keras.Layer, **kwargs): if len(layers) == 1 and isinstance(layers[0], Sequence): diff --git a/bayesflow/networks/time_series_network/skip_recurrent.py b/bayesflow/networks/time_series_network/skip_recurrent.py index 9b2c06c0d..23dee5156 100644 --- a/bayesflow/networks/time_series_network/skip_recurrent.py +++ b/bayesflow/networks/time_series_network/skip_recurrent.py @@ -6,7 +6,7 @@ from bayesflow.utils.serialization import serializable -@serializable +@serializable("bayesflow.networks") class SkipRecurrentNet(keras.Layer): """ Implements a Skip recurrent layer as described in [1], allowing a more flexible recurrent backbone diff --git a/bayesflow/networks/time_series_network/time_series_network.py b/bayesflow/networks/time_series_network/time_series_network.py index 354806f6c..7a96a099d 100644 --- a/bayesflow/networks/time_series_network/time_series_network.py +++ b/bayesflow/networks/time_series_network/time_series_network.py @@ -7,7 +7,7 @@ from ..summary_network import SummaryNetwork -@serializable +@serializable("bayesflow.networks") class TimeSeriesNetwork(SummaryNetwork): """ Implements a LSTNet Architecture as described in [1] diff --git a/bayesflow/networks/transformers/fusion_transformer.py b/bayesflow/networks/transformers/fusion_transformer.py index 1821c25d2..f416957fb 100644 --- a/bayesflow/networks/transformers/fusion_transformer.py +++ b/bayesflow/networks/transformers/fusion_transformer.py @@ -10,7 +10,7 @@ from .mab import MultiHeadAttentionBlock -@serializable +@serializable("bayesflow.networks") class FusionTransformer(SummaryNetwork): """Implements a more flexible version of the TimeSeriesTransformer that applies a series of self-attention layers followed by cross-attention between the representation and a learnable template summarized via a recurrent net.""" diff --git a/bayesflow/networks/transformers/isab.py b/bayesflow/networks/transformers/isab.py index ae1242469..03f15a561 100644 --- a/bayesflow/networks/transformers/isab.py +++ b/bayesflow/networks/transformers/isab.py @@ -7,7 +7,7 @@ from .mab import MultiHeadAttentionBlock -@serializable +@serializable("bayesflow.networks") class InducedSetAttentionBlock(keras.Layer): """Implements the ISAB block from [1] which represents learnable self-attention specifically designed to deal with large sets via a learnable set of "inducing points". diff --git a/bayesflow/networks/transformers/mab.py b/bayesflow/networks/transformers/mab.py index 8f0e3f881..5bd7c9dff 100644 --- a/bayesflow/networks/transformers/mab.py +++ b/bayesflow/networks/transformers/mab.py @@ -8,7 +8,7 @@ from bayesflow.utils.serialization import serializable -@serializable +@serializable("bayesflow.networks") class MultiHeadAttentionBlock(keras.Layer): """Implements the MAB block from [1] which represents learnable cross-attention. diff --git a/bayesflow/networks/transformers/pma.py b/bayesflow/networks/transformers/pma.py index 956c85b48..bdcb2f983 100644 --- a/bayesflow/networks/transformers/pma.py +++ b/bayesflow/networks/transformers/pma.py @@ -10,7 +10,7 @@ from .mab import MultiHeadAttentionBlock -@serializable +@serializable("bayesflow.networks") class PoolingByMultiHeadAttention(keras.Layer): """Implements the pooling with multi-head attention (PMA) block from [1] which represents a permutation-invariant encoder for set-based inputs. diff --git a/bayesflow/networks/transformers/sab.py b/bayesflow/networks/transformers/sab.py index a447d92a2..276383dfd 100644 --- a/bayesflow/networks/transformers/sab.py +++ b/bayesflow/networks/transformers/sab.py @@ -7,7 +7,7 @@ from .mab import MultiHeadAttentionBlock -@serializable +@serializable("bayesflow.networks") class SetAttentionBlock(MultiHeadAttentionBlock): """Implements the SAB block from [1] which represents learnable self-attention. diff --git a/bayesflow/networks/transformers/set_transformer.py b/bayesflow/networks/transformers/set_transformer.py index 6c0ab0efc..256d9e54d 100644 --- a/bayesflow/networks/transformers/set_transformer.py +++ b/bayesflow/networks/transformers/set_transformer.py @@ -11,7 +11,7 @@ from .pma import PoolingByMultiHeadAttention -@serializable +@serializable("bayesflow.networks") class SetTransformer(SummaryNetwork): """Implements the set transformer architecture from [1] which ultimately represents a learnable permutation-invariant function. Designed to naturally model interactions in diff --git a/bayesflow/networks/transformers/time_series_transformer.py b/bayesflow/networks/transformers/time_series_transformer.py index 16feca444..007ae8b74 100644 --- a/bayesflow/networks/transformers/time_series_transformer.py +++ b/bayesflow/networks/transformers/time_series_transformer.py @@ -10,7 +10,7 @@ from .mab import MultiHeadAttentionBlock -@serializable +@serializable("bayesflow.networks") class TimeSeriesTransformer(SummaryNetwork): def __init__( self, diff --git a/bayesflow/scores/mean_score.py b/bayesflow/scores/mean_score.py index 553a7c3af..0c7f200b2 100644 --- a/bayesflow/scores/mean_score.py +++ b/bayesflow/scores/mean_score.py @@ -1,9 +1,8 @@ -from keras.saving import register_keras_serializable as serializable - +from bayesflow.utils.serialization import serializable from .normed_difference_score import NormedDifferenceScore -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class MeanScore(NormedDifferenceScore): r""":math:`S(\hat \theta, \theta) = | \hat \theta - \theta |^2` diff --git a/bayesflow/scores/median_score.py b/bayesflow/scores/median_score.py index 10c8809c3..385c47436 100644 --- a/bayesflow/scores/median_score.py +++ b/bayesflow/scores/median_score.py @@ -1,9 +1,8 @@ -from keras.saving import register_keras_serializable as serializable - +from bayesflow.utils.serialization import serializable from .normed_difference_score import NormedDifferenceScore -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class MedianScore(NormedDifferenceScore): r""":math:`S(\hat \theta, \theta) = | \hat \theta - \theta |` diff --git a/bayesflow/scores/multivariate_normal_score.py b/bayesflow/scores/multivariate_normal_score.py index 84cfd4910..7c745919c 100644 --- a/bayesflow/scores/multivariate_normal_score.py +++ b/bayesflow/scores/multivariate_normal_score.py @@ -1,15 +1,15 @@ import math import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.types import Shape, Tensor from bayesflow.links import PositiveDefinite +from bayesflow.utils.serialization import serializable from .parametric_distribution_score import ParametricDistributionScore -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class MultivariateNormalScore(ParametricDistributionScore): r""":math:`S(\hat p_{\mu, \Sigma}, \theta; k) = -\log( \mathcal N (\theta; \mu, \Sigma))` diff --git a/bayesflow/scores/normed_difference_score.py b/bayesflow/scores/normed_difference_score.py index eb2795927..d33bc128f 100644 --- a/bayesflow/scores/normed_difference_score.py +++ b/bayesflow/scores/normed_difference_score.py @@ -1,13 +1,13 @@ import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.types import Shape, Tensor from bayesflow.utils import weighted_mean +from bayesflow.utils.serialization import serializable from .scoring_rule import ScoringRule -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class NormedDifferenceScore(ScoringRule): r""":math:`S(\hat \theta, \theta; k) = | \hat \theta - \theta |^k` diff --git a/bayesflow/scores/parametric_distribution_score.py b/bayesflow/scores/parametric_distribution_score.py index 3ead3271f..91df32d48 100644 --- a/bayesflow/scores/parametric_distribution_score.py +++ b/bayesflow/scores/parametric_distribution_score.py @@ -1,12 +1,11 @@ -from keras.saving import register_keras_serializable as serializable - from bayesflow.types import Tensor from bayesflow.utils import weighted_mean +from bayesflow.utils.serialization import serializable from .scoring_rule import ScoringRule -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class ParametricDistributionScore(ScoringRule): r""":math:`S(\hat p_\phi, \theta; k) = -\log(\hat p_\phi(\theta))` diff --git a/bayesflow/scores/quantile_score.py b/bayesflow/scores/quantile_score.py index b05a35fc5..7ba021340 100644 --- a/bayesflow/scores/quantile_score.py +++ b/bayesflow/scores/quantile_score.py @@ -1,16 +1,16 @@ from typing import Sequence import keras -from keras.saving import register_keras_serializable as serializable from bayesflow.types import Shape, Tensor from bayesflow.utils import logging, weighted_mean +from bayesflow.utils.serialization import serializable from bayesflow.links import OrderedQuantiles from .scoring_rule import ScoringRule -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class QuantileScore(ScoringRule): r""":math:`S(\hat \theta_i, \theta; \tau_i) = (\hat \theta_i - \theta)(\mathbf{1}_{\hat \theta - \theta > 0} - \tau_i)` diff --git a/bayesflow/scores/scoring_rule.py b/bayesflow/scores/scoring_rule.py index 0144de458..6dee0afec 100644 --- a/bayesflow/scores/scoring_rule.py +++ b/bayesflow/scores/scoring_rule.py @@ -7,7 +7,7 @@ from bayesflow.utils.serialization import deserialize, serializable, serialize -@serializable(package="bayesflow.scores") +@serializable("bayesflow.scores") class ScoringRule: """Base class for scoring rules. diff --git a/bayesflow/utils/serialization.py b/bayesflow/utils/serialization.py index bb55aee41..5be0e0e1d 100644 --- a/bayesflow/utils/serialization.py +++ b/bayesflow/utils/serialization.py @@ -96,28 +96,49 @@ def deserialize(config: dict, custom_objects=None, safe_mode=True, **kwargs): @allow_args -def serializable(cls, package: str | None = None, name: str | None = None): - """Register class as Keras serialize. +def serializable(cls, package: str, name: str | None = None, disable_module_check: bool = False): + """Register class as Keras serializable. - Wrapper function around `keras.saving.register_keras_serializable` to automatically - set the `package` and `name` arguments. + Wrapper function around `keras.saving.register_keras_serializable` to automatically check consistency + of the supplied `package` argument with the module a class resides in. The `package` name should generally + be the module the class resides in, truncated at depth two. Valid examples would be "bayesflow.networks" + or "bayesflow.adapters". The check can be disabled if necessary by setting `disable_module_check` to True. + This should only be done in exceptional cases, and accompanied by a comment why it is necessary for a given + class. Parameters ---------- cls : type The class to register. - package : str, optional + package : str `package` argument forwarded to `keras.saving.register_keras_serializable`. - If None is provided, the package is automatically inferred using the __name__ - attribute of the module the class resides in. + Should generally correspond to the module of the class, truncated at depth two (e.g., "bayesflow.networks"). name : str, optional `name` argument forwarded to `keras.saving.register_keras_serializable`. If None is provided, the classe's __name__ attribute is used. + disable_module_check : bool, optional + Disable check that the provided `package` is consistent with the location of the class within the library. + + Raises + ------ + ValueError + If the supplied `package` does not correspond to the module of the class, truncated at depth two, and + `disable_module_check` is False. No error is thrown when a class is not part of the bayesflow module. """ - if package is None: + if not disable_module_check: frame = sys._getframe(2) g = frame.f_globals - package = g.get("__name__", "bayesflow") + module_name = g.get("__name__", "") + # only apply this check if the class is inside the bayesflow module + is_bayesflow = module_name.split(".")[0] == "bayesflow" + auto_package = ".".join(module_name.split(".")[:2]) + if is_bayesflow and package != auto_package: + raise ValueError( + "'package' should be the first two levels of the module the class resides in (e.g., bayesflow.networks)" + f'. In this case it should be \'package="{auto_package}"\' (was "{package}"). If this is not possible' + " (e.g., because a class was moved to a different module, and serializability should be preserved)," + " please set 'disable_module_check=True' and add a comment why it is necessary for this class." + ) if name is None: name = copy(cls.__name__) diff --git a/bayesflow/wrappers/mamba/mamba.py b/bayesflow/wrappers/mamba/mamba.py index d06508790..b328ede98 100644 --- a/bayesflow/wrappers/mamba/mamba.py +++ b/bayesflow/wrappers/mamba/mamba.py @@ -9,7 +9,7 @@ from .mamba_block import MambaBlock -@serializable +@serializable("bayesflow.wrappers") class Mamba(SummaryNetwork): """ Wraps a sequence of Mamba modules using the simple Mamba module from: diff --git a/bayesflow/wrappers/mamba/mamba_block.py b/bayesflow/wrappers/mamba/mamba_block.py index b8ba36d2e..bd15ecc29 100644 --- a/bayesflow/wrappers/mamba/mamba_block.py +++ b/bayesflow/wrappers/mamba/mamba_block.py @@ -6,7 +6,7 @@ from bayesflow.utils.serialization import serializable -@serializable +@serializable("bayesflow.wrappers") class MambaBlock(keras.Layer): """ Wraps the original Mamba module from, with added functionality for bidirectional processing: diff --git a/docsrc/source/development/serialization.md b/docsrc/source/development/serialization.md index 15c812686..ec8988454 100644 --- a/docsrc/source/development/serialization.md +++ b/docsrc/source/development/serialization.md @@ -17,10 +17,19 @@ As we want/need to pass them in some places, we have to resort to some custom be BayesFlows serialization utilities can be found in the {py:mod}`~bayesflow.utils.serialization` module. We mainly provide three convenience functions: -- The {py:func}`~bayesflow.utils.serialization.serializable` decorator wraps the `keras.saving.register_keras_serializable` function to provide automatic `package` and `name` arguments. +- The {py:func}`~bayesflow.utils.serialization.serializable` decorator wraps the `keras.saving.register_keras_serializable` function to ensure consistent naming of the `package` argument within the library. - The {py:func}`~bayesflow.utils.serialization.serialize` function, which adds support for serializing classes. - Its counterpart {py:func}`~bayesflow.utils.serialization.deserialize`, adds support to deserialize classes. ## Usage To use the adapted serialization functions, you have to use them in the `get_config` and `from_config` method. Please refer to existing classes in the library for usage examples. + +### The `serializable` Decorator + +To make serialization as little confusing as possible, as well as providing stability even when moving classes around, we provide the `package` argument explicitly for each class. +The naming should respect the following naming scheme: Take the module the class resides in (for example, `bayesflow.adapters.transforms.standardize`), and truncate the path to depth two (`bayesflow.adapters`). +In cases where this convention cannot be followed, set `disable_module_check` to `True`, and describe why a different name was necessary. +Changing `package` breaks backwards-compatibility for serialization, so it should be avoided whenever possible. +If you move a class to a different module (without changing the class itself), keep the `package` and set `disable_module_check` to `True`. +This may later be adapted in a release that breaks backward compatiblity anyways. diff --git a/tests/test_utils/test_serialize_deserialize.py b/tests/test_utils/test_serialize_deserialize.py index 6c3bc3983..a9888ecc5 100644 --- a/tests/test_utils/test_serialize_deserialize.py +++ b/tests/test_utils/test_serialize_deserialize.py @@ -3,7 +3,7 @@ from bayesflow.utils.serialization import deserialize, serializable, serialize -@serializable +@serializable("test", disable_module_check=True) class Foo: @classmethod def from_config(cls, config, custom_objects=None): @@ -13,7 +13,7 @@ def get_config(self): return {} -@serializable +@serializable("test", disable_module_check=True) class Bar: @classmethod def from_config(cls, config, custom_objects=None): diff --git a/tests/test_workflows/conftest.py b/tests/test_workflows/conftest.py index c98e543e9..e66b6efe4 100644 --- a/tests/test_workflows/conftest.py +++ b/tests/test_workflows/conftest.py @@ -40,7 +40,7 @@ def summary_network(request): elif request.param == "custom": from bayesflow.networks import SummaryNetwork - @serializable + @serializable("test", disable_module_check=True) class Custom(SummaryNetwork): def __init__(self, **kwargs): super().__init__(**kwargs) From 335e54674af7b3f561ea6f980ea05dd6de1676ec Mon Sep 17 00:00:00 2001 From: LarsKue Date: Mon, 5 May 2025 10:43:14 -0400 Subject: [PATCH 45/46] improve adding __version__ attribute --- bayesflow/__init__.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bayesflow/__init__.py b/bayesflow/__init__.py index 5a28ffe2e..008afc89b 100644 --- a/bayesflow/__init__.py +++ b/bayesflow/__init__.py @@ -50,17 +50,11 @@ def setup(): "in contexts where you need gradients (e.g. custom training loops)." ) + # dynamically add __version__ attribute + from importlib.metadata import version -# dynamically add version dunder variable -try: - from importlib.metadata import version, PackageNotFoundError + globals()["__version__"] = version("bayesflow") - __version__ = version(__name__) -except PackageNotFoundError: - __version__ = "2.0.0" -finally: - del version - del PackageNotFoundError # call and clean up namespace setup() From 52bdb581300aaf5053baca3dccd567e415be29a1 Mon Sep 17 00:00:00 2001 From: LarsKue Date: Mon, 5 May 2025 10:43:22 -0400 Subject: [PATCH 46/46] bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a42b1df97..07a5e924b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bayesflow" -version = "2.0.2" +version = "2.0.3" authors = [{ name = "The BayesFlow Team" }] classifiers = [ "Development Status :: 5 - Production/Stable",