From a443b4bb17528bb398e3d052e8a5054c83b2764c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 21 May 2023 17:10:40 +0100 Subject: [PATCH 001/107] build: version bumped to 2.9.0-dev. --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 892994aa..70ba39fa 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.8.0" +__version__ = "2.9.0-dev" From 21def4aad1b768be1ead3b50557f30efb7c0138b Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 21 May 2023 17:11:14 +0100 Subject: [PATCH 002/107] build: python 3.7 removed from setup.py --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 37a56f51..f503358a 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup(name='pyerrors', author_email='fabian.joswig@ed.ac.uk', license="MIT", packages=find_packages(), - python_requires='>=3.7.0', + python_requires='>=3.8.0', install_requires=['numpy>=1.21', 'autograd>=1.5', 'numdifftools>=0.9.41', 'matplotlib>=3.5', 'scipy>=1.7', 'iminuit>=2.17', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.9', 'pandas>=1.1'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis']}, classifiers=[ @@ -32,7 +32,6 @@ setup(name='pyerrors', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', From 86c060e79f2fa84a9a7d419aab8df10015098445 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 21 May 2023 17:11:38 +0100 Subject: [PATCH 003/107] ci: python 3.7 removed from ci. --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 50316b09..320e3543 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11"] include: - os: macos-latest python-version: 3.9 From bbe74b438c3830cd48975c32adba6718ea8b5370 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 21 May 2023 17:12:32 +0100 Subject: [PATCH 004/107] docs: python badge changed to 3.8+ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c1888a3f..ddb08471 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![pytest](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml/badge.svg)](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml) [![](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![arXiv](https://img.shields.io/badge/arXiv-2209.14371-b31b1b.svg)](https://arxiv.org/abs/2209.14371) [![DOI](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2023.108750-blue)](https://doi.org/10.1016/j.cpc.2023.108750) +[![pytest](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml/badge.svg)](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml) [![](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![arXiv](https://img.shields.io/badge/arXiv-2209.14371-b31b1b.svg)](https://arxiv.org/abs/2209.14371) [![DOI](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2023.108750-blue)](https://doi.org/10.1016/j.cpc.2023.108750) # pyerrors `pyerrors` is a python framework for error computation and propagation of Markov chain Monte Carlo data from lattice field theory and statistical mechanics simulations. From e97cc519a9da6ce5cd4edd15fc13b8ebdf466758 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Mon, 22 May 2023 13:37:46 +0200 Subject: [PATCH 005/107] taking care of cols with only None values (#184) --- pyerrors/input/pandas.py | 30 +++++++++++++++++------------- tests/pandas_test.py | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/pyerrors/input/pandas.py b/pyerrors/input/pandas.py index 911c14d5..13482983 100644 --- a/pyerrors/input/pandas.py +++ b/pyerrors/input/pandas.py @@ -171,26 +171,30 @@ def _deserialize_df(df, auto_gamma=False): if isinstance(df[column][0], bytes): if df[column][0].startswith(b"\x1f\x8b\x08\x00"): df[column] = df[column].transform(lambda x: gzip.decompress(x).decode('utf-8')) - df = df.replace({r'^$': None}, regex=True) - i = 0 - while df[column][i] is None: - i += 1 - if isinstance(df[column][i], str): - if '"program":' in df[column][i][:20]: - df[column] = df[column].transform(lambda x: import_json_string(x, verbose=False) if x is not None else None) - if auto_gamma is True: - if isinstance(df[column][i], list): - df[column].apply(lambda x: [o.gm() if o is not None else x for o in x]) - else: - df[column].apply(lambda x: x.gm() if x is not None else x) + + if not all([e is None for e in df[column]]): + df[column] = df[column].replace({r'^$': None}, regex=True) + i = 0 + while df[column][i] is None: + i += 1 + if isinstance(df[column][i], str): + if '"program":' in df[column][i][:20]: + df[column] = df[column].transform(lambda x: import_json_string(x, verbose=False) if x is not None else None) + if auto_gamma is True: + if isinstance(df[column][i], list): + df[column].apply(lambda x: [o.gm() if o is not None else x for o in x]) + else: + df[column].apply(lambda x: x.gm() if x is not None else x) return df def _need_to_serialize(col): serialize = False i = 0 - while col[i] is None: + while i < len(col) and col[i] is None: i += 1 + if i == len(col): + return serialize if isinstance(col[i], (Obs, Corr)): serialize = True elif isinstance(col[i], list): diff --git a/tests/pandas_test.py b/tests/pandas_test.py index fdff4f10..3a02b97e 100644 --- a/tests/pandas_test.py +++ b/tests/pandas_test.py @@ -173,6 +173,22 @@ def test_null_second_line_df_sql_export_import(tmp_path): assert np.all(reconstructed_df.loc[2:] == my_df.loc[2:]) +def test_null_col_df_gzsql_export_import(tmp_path): + my_dict = {"int": 1, + "float": -0.01, + "Noneval": None, + "Obs1": pe.pseudo_Obs(87, 21, "test_ensemble"), + "Obs2": pe.pseudo_Obs(-87, 21, "test_ensemble2")} + my_df = pd.DataFrame([my_dict] * 4) + pe.input.pandas.to_sql(my_df, 'test', (tmp_path / 'test.db').as_posix(), gz=True) + reconstructed_df = pe.input.pandas.read_sql('SELECT * FROM test', (tmp_path / 'test.db').as_posix(), auto_gamma=True) + assert np.all(reconstructed_df["int"] == my_df["int"]) + assert np.all(reconstructed_df["float"] == my_df["float"]) + assert np.all([e is None for e in reconstructed_df["Noneval"]]) + assert np.all(reconstructed_df["Obs1"] == my_df["Obs1"]) + assert np.all(reconstructed_df["Obs2"] == my_df["Obs2"]) + + def test_df_Corr(tmp_path): my_corr = pe.Corr([pe.pseudo_Obs(-0.48, 0.04, "test"), pe.pseudo_Obs(-0.154, 0.03, "test")]) From 1a811b04f39a0f2ad7d80186892c0a159e043e34 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Fri, 26 May 2023 14:01:52 +0200 Subject: [PATCH 006/107] Feat/files idl xsf (#185) * added kwrg idl to ms5_xsf_read * change output, warn user if expected idl not found --- pyerrors/input/openQCD.py | 47 ++++++++++++++++++++++++--------------- tests/openQCD_in_test.py | 33 ++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/pyerrors/input/openQCD.py b/pyerrors/input/openQCD.py index 7897c1de..36fa21b6 100644 --- a/pyerrors/input/openQCD.py +++ b/pyerrors/input/openQCD.py @@ -1163,6 +1163,8 @@ def read_ms5_xsf(path, prefix, qc, corr, sep="r", **kwargs): Additional keyword arguments. The following keyword arguments are recognized: - names (List[str]): A list of names to use for the replicas. + - files (List[str]): A list of files to read data from. + - idl (List[List[int]]): A list of idls per replicum, resticting data to the idls given. Returns ------- @@ -1211,7 +1213,8 @@ def read_ms5_xsf(path, prefix, qc, corr, sep="r", **kwargs): names.append(se.split(sep)[0] + "|r" + se.split(sep)[1]) else: names.append(prefix) - + if 'idl' in kwargs: + expected_idl = kwargs.get('idl') names = sorted(names) files = sorted(files) @@ -1254,33 +1257,41 @@ def read_ms5_xsf(path, prefix, qc, corr, sep="r", **kwargs): for t in range(tmax): realsamples[repnum].append([]) imagsamples[repnum].append([]) - + if 'idl' in kwargs: + left_idl = set(expected_idl[repnum]) while True: cnfgt = fp.read(chunksize) if not cnfgt: break asascii = struct.unpack(packstr, cnfgt) cnfg = asascii[0] - cnfgs[repnum].append(cnfg) + idl_wanted = True + if 'idl' in kwargs: + idl_wanted = (cnfg in expected_idl[repnum]) + left_idl = left_idl - set([cnfg]) + if idl_wanted: + cnfgs[repnum].append(cnfg) - if corr not in placesBB: - tmpcorr = asascii[1 + 2 * tmax * placesBI.index(corr):1 + 2 * tmax * placesBI.index(corr) + 2 * tmax] - else: - tmpcorr = asascii[1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr):1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr) + 2] + if corr not in placesBB: + tmpcorr = asascii[1 + 2 * tmax * placesBI.index(corr):1 + 2 * tmax * placesBI.index(corr) + 2 * tmax] + else: + tmpcorr = asascii[1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr):1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr) + 2] - corrres = [[], []] - for i in range(len(tmpcorr)): - corrres[i % 2].append(tmpcorr[i]) - for t in range(int(len(tmpcorr) / 2)): - realsamples[repnum][t].append(corrres[0][t]) - for t in range(int(len(tmpcorr) / 2)): - imagsamples[repnum][t].append(corrres[1][t]) + corrres = [[], []] + for i in range(len(tmpcorr)): + corrres[i % 2].append(tmpcorr[i]) + for t in range(int(len(tmpcorr) / 2)): + realsamples[repnum][t].append(corrres[0][t]) + for t in range(int(len(tmpcorr) / 2)): + imagsamples[repnum][t].append(corrres[1][t]) + if 'idl' in kwargs: + left_idl = list(left_idl) + if len(left_idl) > 0: + warnings.warn('Could not find idls ' + str(left_idl) + ' in replikum of file ' + file, UserWarning) repnum += 1 - - s = "Read correlator " + corr + " from " + str(repnum) + " replika with " + str(len(realsamples[0][t])) + s = "Read correlator " + corr + " from " + str(repnum) + " replika with idls" + str(realsamples[0][t]) for rep in range(1, repnum): - s += ", " + str(len(realsamples[rep][t])) - s += " samples" + s += ", " + str(realsamples[rep][t]) print(s) print("Asserted run parameters:\n T:", tmax, "kappa:", kappa, "csw:", csw, "dF:", dF, "zF:", zF, "bnd:", bnd) diff --git a/tests/openQCD_in_test.py b/tests/openQCD_in_test.py index dd9f4272..8122dcb8 100644 --- a/tests/openQCD_in_test.py +++ b/tests/openQCD_in_test.py @@ -121,7 +121,7 @@ def test_gf_coupling(): def test_read_ms5_xsf(): - path = './tests//data/openqcd_test/' + path = './tests/data/openqcd_test/' prefix = "ms5_xsf_T24L16" corr = "gA" qc = 'dd' @@ -145,6 +145,37 @@ def test_read_ms5_xsf(): pe.input.openQCD.read_ms5_xsf(path, prefix, qc, fcorr) +def test_read_ms5_xsf_idl(): + path = './tests/data/openqcd_test/' + prefix = "ms5_xsf_T24L16" + corr = "gA" + qc = 'dd' + + c = pe.input.openQCD.read_ms5_xsf(path, prefix, qc, corr, idl=[range(1, 6), range(1, 7), range(1, 8)]) + + assert c.real[12].names == ['ms5_xsf_T24L16|r1', 'ms5_xsf_T24L16|r2', 'ms5_xsf_T24L16|r3'] + + assert (c.real[12].shape['ms5_xsf_T24L16|r1'] == 5) + assert (c.real[12].shape['ms5_xsf_T24L16|r2'] == 6) + assert (c.real[12].shape['ms5_xsf_T24L16|r3'] == 7) + + assert (c.real[12].idl['ms5_xsf_T24L16|r1'] == range(1, 6)) + assert (c.real[12].idl['ms5_xsf_T24L16|r2'] == range(1, 7)) + assert (c.real[12].idl['ms5_xsf_T24L16|r3'] == range(1, 8)) + + c = pe.input.openQCD.read_ms5_xsf(path, prefix, qc, corr, idl=[range(1, 11, 2), range(1, 11, 2), range(1, 11, 2)]) + + assert c.real[12].names == ['ms5_xsf_T24L16|r1', 'ms5_xsf_T24L16|r2', 'ms5_xsf_T24L16|r3'] + + assert (c.real[12].shape['ms5_xsf_T24L16|r1'] == 5) + assert (c.real[12].shape['ms5_xsf_T24L16|r2'] == 5) + assert (c.real[12].shape['ms5_xsf_T24L16|r3'] == 5) + + assert (c.real[12].idl['ms5_xsf_T24L16|r1'] == range(1, 11, 2)) + assert (c.real[12].idl['ms5_xsf_T24L16|r2'] == range(1, 11, 2)) + assert (c.real[12].idl['ms5_xsf_T24L16|r3'] == range(1, 11, 2)) + + def test_find_files(): path = './tests/data/openqcd_test/' prefix = "ms5_xsf_T24L16" From dc73456759a316c7ab00c6e11ed7ba924e4d9028 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 30 May 2023 12:52:10 +0100 Subject: [PATCH 007/107] tests: np.alltrue replaced by np.all. --- tests/obs_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/obs_test.py b/tests/obs_test.py index 084edd64..4bf923c8 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1096,7 +1096,7 @@ def test_reduce_deltas(): for idx_new in idl: new = pe.obs._reduce_deltas(deltas, idx_old, idx_new) print(new) - assert(np.alltrue([float(i) for i in idx_new] == new)) + assert(np.all([float(i) for i in idx_new] == new)) def test_cobs_array(): From 095f7c8a46ac8561f0b36d303cc18f8f8cbbddf3 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 30 May 2023 14:03:01 +0100 Subject: [PATCH 008/107] fix: adjusted maximal value for rho in test_gamma_method_irregular. (#188) --- tests/obs_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/obs_test.py b/tests/obs_test.py index 4bf923c8..73bedd55 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -762,7 +762,7 @@ def test_gamma_method_irregular(): N = 15 for i in range(10): arr = np.random.normal(1, .2, size=N) - for rho in .1 * np.arange(20): + for rho in .05 * np.arange(20): carr = gen_autocorrelated_array(arr, rho) a = pe.Obs([carr], ['a']) a.gm() From 34fe7f44fcc9503357e430642b7d3d15af178c02 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Tue, 30 May 2023 16:29:22 +0200 Subject: [PATCH 009/107] Bug fix for edge case in _compute_drho (#189) --- pyerrors/obs.py | 2 +- tests/obs_test.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index a49320a5..e00a3241 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -278,7 +278,7 @@ class Obs: def _compute_drho(i): tmp = (self.e_rho[e_name][i + 1:w_max] - + np.concatenate([self.e_rho[e_name][i - 1:None if i - w_max // 2 <= 0 else 2 * (i - w_max // 2):-1], + + np.concatenate([self.e_rho[e_name][i - 1:None if i - w_max // 2 <= 0 else (2 * i - (2 * w_max) // 2):-1], self.e_rho[e_name][1:max(1, w_max - 2 * i)]]) - 2 * self.e_rho[e_name][i] * self.e_rho[e_name][1:w_max - i]) self.e_drho[e_name][i] = np.sqrt(np.sum(tmp ** 2) / e_N) diff --git a/tests/obs_test.py b/tests/obs_test.py index 73bedd55..d5d8d5e1 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -767,6 +767,17 @@ def test_gamma_method_irregular(): a = pe.Obs([carr], ['a']) a.gm() + arr = np.random.normal(1, .2, size=999) + carr = gen_autocorrelated_array(arr, .8) + o = pe.Obs([carr], ['test']) + o.gamma_method() + no = np.NaN * o + no.gamma_method() + o.idl['test'] = range(1, 1998, 2) + o.gamma_method() + no = np.NaN * o + no.gamma_method() + def test_irregular_gapped_dtauint(): my_idl = list(range(0, 5010, 10)) From ca70c7571bf9f2fe26a9cae27701cd4aae9e7e4c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 31 May 2023 16:59:27 +0100 Subject: [PATCH 010/107] fix: Conversion of an array with ndim > 0 to a scalar deprecation fixed. (#186) --- pyerrors/covobs.py | 2 +- pyerrors/input/dobs.py | 2 +- pyerrors/obs.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyerrors/covobs.py b/pyerrors/covobs.py index 49953983..64d9d6ec 100644 --- a/pyerrors/covobs.py +++ b/pyerrors/covobs.py @@ -42,7 +42,7 @@ class Covobs: def errsq(self): """ Return the variance (= square of the error) of the Covobs """ - return float(np.dot(np.transpose(self.grad), np.dot(self.cov, self.grad))) + return np.dot(np.transpose(self.grad), np.dot(self.cov, self.grad)).item() def _set_cov(self, cov): """ Set the covariance matrix of the covobs diff --git a/pyerrors/input/dobs.py b/pyerrors/input/dobs.py index 4757baae..b8b005ff 100644 --- a/pyerrors/input/dobs.py +++ b/pyerrors/input/dobs.py @@ -850,7 +850,7 @@ def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=N for i in range(ncov): for o in obsl: if cname in o.covobs: - val = o.covobs[cname].grad[i] + val = o.covobs[cname].grad[i].item() if val != 0: ds += '%1.14e ' % (val) else: diff --git a/pyerrors/obs.py b/pyerrors/obs.py index e00a3241..66f137d5 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1521,7 +1521,7 @@ def _covariance_element(obs1, obs2): if e_name not in obs2.cov_names: continue - dvalue += float(np.dot(np.transpose(obs1.covobs[e_name].grad), np.dot(obs1.covobs[e_name].cov, obs2.covobs[e_name].grad))) + dvalue += np.dot(np.transpose(obs1.covobs[e_name].grad), np.dot(obs1.covobs[e_name].cov, obs2.covobs[e_name].grad)).item() return dvalue From bb43a8afb7e785a965e3dd9754154c150946b132 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 31 May 2023 18:07:38 +0100 Subject: [PATCH 011/107] Fix Obs in f-strings without specifier (#190) * fix: Conversion of an array with ndim > 0 to a scalar deprecation fixed. * fix: adjusted maximal value for rho in test_gamma_method_irregular. * fix: obs in f-strings now work again when no specifier is provided. --- pyerrors/obs.py | 6 +++++- tests/obs_test.py | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 66f137d5..7d8f9911 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -694,8 +694,12 @@ class Obs: return _format_uncertainty(self.value, self._dvalue) def __format__(self, format_type): + if format_type == "": + significance = 2 + else: + significance = int(float(format_type.replace("+", "").replace("-", ""))) my_str = _format_uncertainty(self.value, self._dvalue, - significance=int(float(format_type.replace("+", "").replace("-", "")))) + significance=significance) for char in ["+", " "]: if format_type.startswith(char): if my_str[0] != "-": diff --git a/tests/obs_test.py b/tests/obs_test.py index d5d8d5e1..a77c6863 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1274,3 +1274,11 @@ def test_format(): assert o1.__format__("+3") == '+0.3480(123)' assert o1.__format__("+2") == '+0.348(12)' assert o1.__format__(" 2") == ' 0.348(12)' + +def test_f_string_obs(): + o1 = pe.pseudo_Obs(0.348, 0.0123, "test") + print(f"{o1}") + print(f"{o1:3}") + print(f"{o1:+3}") + print(f"{o1:-1}") + print(f"{o1: 8}") From 9489b87a7e82cd3b765015a89a9ecfb95581ad4c Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:07:16 +0200 Subject: [PATCH 012/107] fix nan to None test (#192) --- tests/pandas_test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/pandas_test.py b/tests/pandas_test.py index 3a02b97e..f86458f8 100644 --- a/tests/pandas_test.py +++ b/tests/pandas_test.py @@ -46,10 +46,9 @@ def test_nan_df_export_import(tmp_path): my_df.loc[1, "int"] = np.nan for gz in [True, False]: - pe.input.pandas.dump_df(my_df, (tmp_path / 'df_output').as_posix(), gz=gz) - reconstructed_df = pe.input.pandas.load_df((tmp_path / 'df_output').as_posix(), auto_gamma=True, gz=gz) with pytest.warns(UserWarning, match="nan value in column int will be replaced by None"): - warnings.warn("nan value in column int will be replaced by None", UserWarning) + pe.input.pandas.dump_df(my_df, (tmp_path / 'df_output').as_posix(), gz=gz) + reconstructed_df = pe.input.pandas.load_df((tmp_path / 'df_output').as_posix(), auto_gamma=True, gz=gz) assert reconstructed_df.loc[1, "int"] is None assert np.all(reconstructed_df.loc[:, "float"] == my_df.loc[:, "float"]) assert np.all(reconstructed_df.loc[:, "Obs1"] == my_df.loc[:, "Obs1"]) From 525c61ed203ab847dbf5871ed4ec65daebc28f9a Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 2 Jun 2023 15:04:15 +0100 Subject: [PATCH 013/107] Fix another edge case in _compute_drho (#194) * tests: failing test for compute_drho edge case added. * tests: example file for failing compute_drho added. * tests: assertion that dvalue stays the same added to compute drho test. * fix: another edge case in computation of drho fixed. --- pyerrors/obs.py | 2 +- tests/data/compute_drho_fails.json.gz | Bin 0 -> 49713 bytes tests/obs_test.py | 5 +++++ 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/data/compute_drho_fails.json.gz diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 7d8f9911..d1e4f5c7 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -278,7 +278,7 @@ class Obs: def _compute_drho(i): tmp = (self.e_rho[e_name][i + 1:w_max] - + np.concatenate([self.e_rho[e_name][i - 1:None if i - w_max // 2 <= 0 else (2 * i - (2 * w_max) // 2):-1], + + np.concatenate([self.e_rho[e_name][i - 1:None if i - (w_max - 1) // 2 <= 0 else (2 * i - (2 * w_max) // 2):-1], self.e_rho[e_name][1:max(1, w_max - 2 * i)]]) - 2 * self.e_rho[e_name][i] * self.e_rho[e_name][1:w_max - i]) self.e_drho[e_name][i] = np.sqrt(np.sum(tmp ** 2) / e_N) diff --git a/tests/data/compute_drho_fails.json.gz b/tests/data/compute_drho_fails.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..3fc335bd46ac1f7fdc8c84e89504dd9285447530 GIT binary patch literal 49713 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 a_u@CoIRA(KWB-5u@Bah0W8~?K Date: Fri, 2 Jun 2023 15:46:27 +0100 Subject: [PATCH 014/107] fix: bug in _find_correlator fixed. (#193) --- pyerrors/input/sfcf.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index 7ec26f03..e6cb0b49 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -286,28 +286,26 @@ def _make_pattern(version, name, noffset, wf, wf2, b2b, quarks): def _find_correlator(file_name, version, pattern, b2b, silent=False): T = 0 - file = open(file_name, "r") + with open(file_name, "r") as my_file: + + content = my_file.read() + match = re.search(pattern, content) + if match: + if version == "0.0": + start_read = content.count('\n', 0, match.start()) + 1 + T = content.count('\n', start_read) + else: + start_read = content.count('\n', 0, match.start()) + 5 + b2b + end_match = re.search(r'\n\s*\n', content[match.start():]) + T = content[match.start():].count('\n', 0, end_match.start()) - 4 - b2b + if not T > 0: + raise ValueError("Correlator with pattern\n" + pattern + "\nis empty!") + if not silent: + print(T, 'entries, starting to read in line', start_read) - content = file.read() - match = re.search(pattern, content) - if match: - if version == "0.0": - start_read = content.count('\n', 0, match.start()) + 1 - T = content.count('\n', start_read) else: - start_read = content.count('\n', 0, match.start()) + 5 + b2b - end_match = re.search(r'\n\s*\n', content[match.start():]) - T = content[match.start():].count('\n', 0, end_match.start()) - 4 - b2b - if not T > 0: - raise ValueError("Correlator with pattern\n" + pattern + "\nis empty!") - if not silent: - print(T, 'entries, starting to read in line', start_read) + raise ValueError('Correlator with pattern\n' + pattern + '\nnot found.') - else: - file.close() - raise ValueError('Correlator with pattern\n' + pattern + '\nnot found.') - - file.close() return start_read, T From 5b80f44485fee84a7188d92afd3907bce7a9f5de Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 2 Jun 2023 15:48:35 +0100 Subject: [PATCH 015/107] ci: pytest workflow updated to fail on warnings. --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 320e3543..b25c7d17 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -45,4 +45,4 @@ jobs: pip freeze - name: Run tests - run: pytest --cov=pyerrors -vv + run: pytest --cov=pyerrors -vv -Werror From 4886197a03bb576d8f45d6189e1839c0fa9a1499 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 2 Jun 2023 15:49:42 +0100 Subject: [PATCH 016/107] docs: changes to pytest workflow documented in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bfe66228..86267ae2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Please add docstrings to any new function, class or method you implement. The do When implementing a new feature or fixing a bug please add meaningful tests to the files in the `tests` directory which cover the new code. For all pull requests tests are executed for the most recent python releases via ``` -pytest +pytest -vv -Werror pytest --nbmake examples/*.ipynb ``` requiring `pytest`, `pytest-cov`, `pytest-benchmark`, `hypothesis` and `nbmake`. To install the test dependencies one can run `pip install pyerrors[test]` From cdadbd0fad3be12736655a18a1c527b06c7a6918 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 2 Jun 2023 16:16:04 +0100 Subject: [PATCH 017/107] ci: windows removed from pytest workflow, mac-os python version bumped to 3.10 --- .github/workflows/pytest.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index b25c7d17..10f24fc4 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -19,9 +19,7 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11"] include: - os: macos-latest - python-version: 3.9 - - os: windows-latest - python-version: 3.9 + python-version: 3.10 steps: - name: Checkout source From 1b45e71e7983a8965a57584a0b8bdef0b01a3ee3 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 2 Jun 2023 16:17:28 +0100 Subject: [PATCH 018/107] ci: mac-os python version fixed. --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 10f24fc4..d4abcfc3 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -19,7 +19,7 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11"] include: - os: macos-latest - python-version: 3.10 + python-version: "3.10" steps: - name: Checkout source From f14042132f76f5e8b073b45a630f0b2b072bb068 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 2 Jun 2023 17:19:20 +0100 Subject: [PATCH 019/107] build: bumped dependency versions to the latest releases. The idea is to reduce the number of potential version combinations for easier debugging in the future. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f503358a..a9155101 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ setup(name='pyerrors', license="MIT", packages=find_packages(), python_requires='>=3.8.0', - install_requires=['numpy>=1.21', 'autograd>=1.5', 'numdifftools>=0.9.41', 'matplotlib>=3.5', 'scipy>=1.7', 'iminuit>=2.17', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.9', 'pandas>=1.1'], + install_requires=['numpy>=1.24', 'autograd>=1.5', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis']}, classifiers=[ 'Development Status :: 5 - Production/Stable', From 85f66d39cbc1832afd17f0104b94dc9b679fa092 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 22 Jun 2023 13:53:17 +0100 Subject: [PATCH 020/107] build: bumped autograd dependency to version 1.6 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a9155101..980ce3ab 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ setup(name='pyerrors', license="MIT", packages=find_packages(), python_requires='>=3.8.0', - install_requires=['numpy>=1.24', 'autograd>=1.5', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], + install_requires=['numpy>=1.24', 'autograd>=1.6', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis']}, classifiers=[ 'Development Status :: 5 - Production/Stable', From dac7193398e0bec3d2e047fcb9d5818805901848 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 23 Jun 2023 10:17:09 +0100 Subject: [PATCH 021/107] build: autograd dependency bumped to 1.6.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 980ce3ab..b41b95b3 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ setup(name='pyerrors', license="MIT", packages=find_packages(), python_requires='>=3.8.0', - install_requires=['numpy>=1.24', 'autograd>=1.6', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], + install_requires=['numpy>=1.24', 'autograd>=1.6.2', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis']}, classifiers=[ 'Development Status :: 5 - Production/Stable', From 838118970865e88edeb7b9d4c1d9eeba48bac0c5 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 23 Jun 2023 13:24:35 +0100 Subject: [PATCH 022/107] refactor: plot_piechart slightly simplified. --- pyerrors/obs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index d1e4f5c7..e32ca821 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -627,7 +627,7 @@ class Obs: if save: fig1.savefig(save) - return dict(zip(self.e_names, sizes)) + return dict(zip(labels, sizes)) def dump(self, filename, datatype="json.gz", description="", **kwargs): """Dump the Obs to a file 'name' of chosen format. From 94b0322f0be2f7b3cc2a1f6a77075834660f7283 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:32:27 +0200 Subject: [PATCH 023/107] new variant of second derivative, bigger stencil (#197) * new variant of second derivative, bigger stencil * better docstring dor second_deriv * forgot to "r" in front of docstring * flake8 compliance --- pyerrors/correlators.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 266ed428..8573cdbf 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -577,13 +577,21 @@ class Corr: raise Exception("Unknown variant.") def second_deriv(self, variant="symmetric"): - """Return the second derivative of the correlator with respect to x0. + r"""Return the second derivative of the correlator with respect to x0. Parameters ---------- variant : str decides which definition of the finite differences derivative is used. - Available choice: symmetric, improved, log, default: symmetric + Available choice: + - symmetric (default) + $$\tilde{\partial}^2_0 f(x_0) = f(x_0+1)-2f(x_0)+f(x_0-1)$$ + - big_symmetric + $$\partial^2_0 f(x_0) = \frac{f(x_0+2)-2f(x_0)+f(x_0-2)}{4}$$ + - improved + $$\partial^2_0 f(x_0) = \frac{-f(x_0+2) + 16 * f(x_0+1) - 30 * f(x_0) + 16 * f(x_0-1) - f(x_0-2)}{12}$$ + - log + $$f(x) = \tilde{\partial}^2_0 log(f(x_0))+(\tilde{\partial}_0 log(f(x_0)))^2$$ """ if self.N != 1: raise Exception("second_deriv only implemented for one-dimensional correlators.") @@ -597,6 +605,16 @@ class Corr: if (all([x is None for x in newcontent])): raise Exception("Derivative is undefined at all timeslices") return Corr(newcontent, padding=[1, 1]) + elif variant == "big_symmetric": + newcontent = [] + for t in range(2, self.T - 2): + if (self.content[t - 2] is None) or (self.content[t + 2] is None): + newcontent.append(None) + else: + newcontent.append((self.content[t + 2] - 2 * self.content[t] + self.content[t - 2]) / 4) + if (all([x is None for x in newcontent])): + raise Exception("Derivative is undefined at all timeslices") + return Corr(newcontent, padding=[2, 2]) elif variant == "improved": newcontent = [] for t in range(2, self.T - 2): From 9589820253f7ae75ae8f1ec15fef02e9eea2d599 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 15:35:37 +0100 Subject: [PATCH 024/107] tests: coverage for all derivative variants extended. --- tests/correlators_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/correlators_test.py b/tests/correlators_test.py index cc08544c..c5b530cb 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -58,12 +58,16 @@ def test_modify_correlator(): for pad in [0, 2]: corr = pe.Corr(corr_content, padding=[pad, pad]) corr.roll(np.random.randint(100)) - corr.deriv(variant="forward") corr.deriv(variant="symmetric") + corr.deriv(variant="forward") + corr.deriv(variant="backward") corr.deriv(variant="improved") + corr.deriv(variant="log") corr.deriv().deriv() corr.second_deriv(variant="symmetric") + corr.second_deriv(variant="big_symmetric") corr.second_deriv(variant="improved") + corr.second_deriv(variant="log") corr.second_deriv().second_deriv() for i, e in enumerate(corr.content): From db79cb2d9575aaf435fa6ca89cfdc373270d65c8 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 15:44:06 +0100 Subject: [PATCH 025/107] tests: test for print_config added. --- tests/misc_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/misc_test.py b/tests/misc_test.py index 3211a806..99bb5056 100644 --- a/tests/misc_test.py +++ b/tests/misc_test.py @@ -18,3 +18,7 @@ def test_obs_errorbar(): pe.errorbar(x_obs, y_obs, marker="x", ms=2, xerr=xerr, yerr=yerr) plt.close('all') + + +def test_print_config(): + pe.print_config() From 47bb746b636260ca9884a230191bb3185bfaebde Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 15:53:33 +0100 Subject: [PATCH 026/107] docs: citing pyerrors section added to README. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index ddb08471..3e686e51 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,10 @@ conda update -c conda-forge pyerrors # Update ## Contributing We appreciate all contributions to the code, the documentation and the examples. If you want to get involved please have a look at our [contribution guideline](https://github.com/fjosw/pyerrors/blob/develop/CONTRIBUTING.md). + +## Citing pyerrors +If you use `pyerrors` for research that leads to a publication we suggest citing the following papers: +- Fabian Joswig, Simon Kuberski, Justus T. Kuhlmann, Jan Neuendorf, *pyerrors: a python framework for error analysis of Monte Carlo data*. Comput.Phys.Commun. 288 (2023) 108750. +- Ulli Wolff, *Monte Carlo errors with less errors*. Comput.Phys.Commun. 156 (2004) 143-153, Comput.Phys.Commun. 176 (2007) 383 (erratum). +- Alberto Ramos, *Automatic differentiation for error analysis of Monte Carlo data*. Comput.Phys.Commun. 238 (2019) 19-35. +- Stefan Schaefer, Rainer Sommer, Francesco Virotta, *Critical slowing down and error analysis in lattice QCD simulations*. Nucl.Phys.B 845 (2011) 93-119. From 5b024c73794acfc2375ab25f55e1b6992dd7b769 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 16:06:00 +0100 Subject: [PATCH 027/107] build: nbmake added to test build option in setup.py. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b41b95b3..7a19394c 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup(name='pyerrors', packages=find_packages(), python_requires='>=3.8.0', install_requires=['numpy>=1.24', 'autograd>=1.6.2', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], - extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis']}, + extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis', 'nbmake']}, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', From c47e1ccac70e7c7873ebf07d6a484be9393c012a Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 16:06:55 +0100 Subject: [PATCH 028/107] build: flake8 added to test build option in setup.py. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7a19394c..ab038e8d 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup(name='pyerrors', packages=find_packages(), python_requires='>=3.8.0', install_requires=['numpy>=1.24', 'autograd>=1.6.2', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], - extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis', 'nbmake']}, + extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis', 'nbmake', 'flake8']}, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', From bf25ecf2ee1c12cce457fd2cb94e105b5d6d20c8 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 16:11:25 +0100 Subject: [PATCH 029/107] docs: Contributing guidelines clarified. --- CONTRIBUTING.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86267ae2..d4160430 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,19 +19,17 @@ Please add docstrings to any new function, class or method you implement. The do ### Tests When implementing a new feature or fixing a bug please add meaningful tests to the files in the `tests` directory which cover the new code. +We follow the [PEP8](https://peps.python.org/pep-0008/) code style which is checked by `flake8`. For all pull requests tests are executed for the most recent python releases via ``` pytest -vv -Werror pytest --nbmake examples/*.ipynb +flake8 --ignore=E501,W503 --exclude=__init__.py pyerrors ``` -requiring `pytest`, `pytest-cov`, `pytest-benchmark`, `hypothesis` and `nbmake`. To install the test dependencies one can run `pip install pyerrors[test]` +The tests require `pytest`, `pytest-cov`, `pytest-benchmark`, `hypothesis`, `nbmake` and `flake8`. To install the test dependencies one can run `pip install pyerrors[test]`. +Please make sure that all tests pass for a new pull requests. To get a coverage report in html run ``` pytest --cov=pyerrors --cov-report html ``` -The linter `flake8` is executed with the command -``` -flake8 --ignore=E501,W503 --exclude=__init__.py pyerrors -``` -Please make sure that all tests pass for a new pull requests. From e73e393695ecf761844640474f9c7909018a0e3d Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 16:12:00 +0100 Subject: [PATCH 030/107] docs: pip installation instructions changed. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e686e51..84eb37dd 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ ## Installation Install the most recent release using pip and [pypi](https://pypi.org/project/pyerrors/): ```bash -pip install pyerrors # Fresh install -pip install -U pyerrors # Update +python -m pip install pyerrors # Fresh install +python -m pip install -U pyerrors # Update ``` Install the most recent release using conda and [conda-forge](https://anaconda.org/conda-forge/pyerrors): ```bash From 305b458d1954b17a9b7b234755e1f9aaa31149f2 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 16:13:32 +0100 Subject: [PATCH 031/107] docs: link to Changelog added to README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 84eb37dd..e3436706 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ - **Documentation:** https://fjosw.github.io/pyerrors/pyerrors.html - **Examples:** https://github.com/fjosw/pyerrors/tree/develop/examples +- **Changelog:** https://github.com/fjosw/pyerrors/blob/develop/CHANGELOG.md - **Bug reports:** https://github.com/fjosw/pyerrors/issues ## Installation From 2cd076dbd7aa636c123006b35f854adba0ff6afa Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 10 Jul 2023 16:33:09 +0100 Subject: [PATCH 032/107] docs: details in documentation clarified. --- pyerrors/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pyerrors/__init__.py b/pyerrors/__init__.py index 9abd9935..df2c983a 100644 --- a/pyerrors/__init__.py +++ b/pyerrors/__init__.py @@ -27,8 +27,8 @@ There exist similar publicly available implementations of gamma method error ana Install the most recent release using pip and [pypi](https://pypi.org/project/pyerrors/): ```bash -pip install pyerrors # Fresh install -pip install -U pyerrors # Update +python -m pip install pyerrors # Fresh install +python -m pip install -U pyerrors # Update ``` Install the most recent release using conda and [conda-forge](https://anaconda.org/conda-forge/pyerrors): ```bash @@ -37,7 +37,7 @@ conda update -c conda-forge pyerrors # Update ``` Install the current `develop` version: ```bash -pip install git+https://github.com/fjosw/pyerrors.git@develop +python -m pip install git+https://github.com/fjosw/pyerrors.git@develop ``` ## Basic example @@ -108,6 +108,7 @@ my_sum.details() > · Ensemble 'ensemble_name' : 1000 configurations (from 1 to 1000) ``` +The `gamma_method` is not automatically called after every intermediate step in order to prevent computational overhead. We use the following definition of the integrated autocorrelation time established in [Madras & Sokal 1988](https://link.springer.com/article/10.1007/BF01022990) $$\tau_\mathrm{int}=\frac{1}{2}+\sum_{t=1}^{W}\rho(t)\geq \frac{1}{2}\,.$$ @@ -305,7 +306,7 @@ print(my_derived_cobs) # The `Covobs` class In many projects, auxiliary data that is not based on Monte Carlo chains enters. Examples are experimentally determined mesons masses which are used to set the scale or renormalization constants. These numbers come with an error that has to be propagated through the analysis. The `Covobs` class allows to define such quantities in `pyerrors`. Furthermore, external input might consist of correlated quantities. An example are the parameters of an interpolation formula, which are defined via mean values and a covariance matrix between all parameters. The contribution of the interpolation formula to the error of a derived quantity therefore might depend on the complete covariance matrix. -This concept is built into the definition of `Covobs`. In `pyerrors`, external input is defined by $M$ mean values, a $M\times M$ covariance matrix, where $M=1$ is permissible, and a name that uniquely identifies the covariance matrix. Below, we define the pion mass, based on its mean value and error, 134.9768(5). Note, that the square of the error enters `cov_Obs`, since the second argument of this function is the covariance matrix of the `Covobs`. +This concept is built into the definition of `Covobs`. In `pyerrors`, external input is defined by $M$ mean values, a $M\times M$ covariance matrix, where $M=1$ is permissible, and a name that uniquely identifies the covariance matrix. Below, we define the pion mass, based on its mean value and error, 134.9768(5). **Note, that the square of the error enters `cov_Obs`**, since the second argument of this function is the covariance matrix of the `Covobs`. ```python import pyerrors.obs as pe @@ -387,11 +388,12 @@ def func(a, x): `pyerrors` also supports correlated fits which can be triggered via the parameter `correlated_fit=True`. Details about how the required covariance matrix is estimated can be found in `pyerrors.obs.covariance`. +Direct visualizations of the performed fits can be triggered via `resplot=True` or `qqplot=True`. -Direct visualizations of the performed fits can be triggered via `resplot=True` or `qqplot=True`. For all available options see `pyerrors.fits.least_squares`. +For all available options including combined fits to multiple datasets see `pyerrors.fits.least_squares`. ## Total least squares fits -`pyerrors` can also fit data with errors on both the dependent and independent variables using the total least squares method also referred to orthogonal distance regression as implemented in [scipy](https://docs.scipy.org/doc/scipy/reference/odr.html), see `pyerrors.fits.least_squares`. The syntax is identical to the standard least squares case, the only difference being that `x` also has to be a `list` or `numpy.array` of `Obs`. +`pyerrors` can also fit data with errors on both the dependent and independent variables using the total least squares method also referred to as orthogonal distance regression as implemented in [scipy](https://docs.scipy.org/doc/scipy/reference/odr.html), see `pyerrors.fits.least_squares`. The syntax is identical to the standard least squares case, the only difference being that `x` also has to be a `list` or `numpy.array` of `Obs`. For the full API see `pyerrors.fits` for fits and `pyerrors.roots` for finding roots of functions. From b62a18643ed84e680810f66a0bb528969141ae66 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 14 Jul 2023 13:12:11 +0100 Subject: [PATCH 033/107] Bootstrap export/import (#198) * feat: export_bootstrap method added. * feat: import_bootstrap function added. * tests: first test for import/export bootstrap added. * feat: bootstrap feature cleaned up. * docs: boostrap docstrings improved. --- pyerrors/obs.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++ tests/obs_test.py | 14 +++++++++ 2 files changed, 88 insertions(+) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index e32ca821..85fdf851 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -3,6 +3,7 @@ import hashlib import pickle import numpy as np import autograd.numpy as anp # Thinly-wrapped numpy +import scipy from autograd import jacobian import matplotlib.pyplot as plt from scipy.stats import skew, skewtest, kurtosis, kurtosistest @@ -684,6 +685,49 @@ class Obs: tmp_jacks[1:] = (n * mean - full_data) / (n - 1) return tmp_jacks + def export_bootstrap(self, samples=500, random_numbers=None, save_rng=None): + """Export bootstrap samples from the Obs + + Parameters + ---------- + samples : int + Number of bootstrap samples to generate. + random_numbers : np.ndarray + Array of shape (samples, length) containing the random numbers to generate the bootstrap samples. + If not provided the bootstrap samples are generated bashed on the md5 hash of the enesmble name. + save_rng : str + Save the random numbers to a file if a path is specified. + + Returns + ------- + numpy.ndarray + Returns a numpy array of length N + 1 where N is the number of samples + for the given ensemble and replicum. The zeroth entry of the array contains + the mean value of the Obs, entries 1 to N contain the N import_bootstrap samples + derived from the Obs. The current implementation only works for observables + defined on exactly one ensemble and replicum. The derived bootstrap samples + should agree with samples from a full bootstrap analysis up to O(1/N). + """ + if len(self.names) != 1: + raise Exception("'export_boostrap' is only implemented for Obs defined on one ensemble and replicum.") + + name = self.names[0] + length = self.N + + if random_numbers is None: + seed = int(hashlib.md5(name.encode()).hexdigest(), 16) & 0xFFFFFFFF + rng = np.random.default_rng(seed) + random_numbers = rng.integers(0, length, size=(samples, length)) + + if save_rng is not None: + np.savetxt(save_rng, random_numbers, fmt='%i') + + proj = np.vstack([np.bincount(o, minlength=length) for o in random_numbers]) / length + ret = np.zeros(samples + 1) + ret[0] = self.value + ret[1:] = proj @ (self.deltas[name] + self.r_values[name]) + return ret + def __float__(self): return float(self.value) @@ -1550,6 +1594,36 @@ def import_jackknife(jacks, name, idl=None): return new_obs +def import_bootstrap(boots, name, random_numbers): + """Imports bootstrap samples and returns an Obs + + Parameters + ---------- + boots : numpy.ndarray + numpy array containing the mean value as zeroth entry and + the N bootstrap samples as first to Nth entry. + name : str + name of the ensemble the samples are defined on. + random_numbers : np.ndarray + Array of shape (samples, length) containing the random numbers to generate the bootstrap samples, + where samples is the number of bootstrap samples and length is the length of the original Monte Carlo + chain to be reconstructed. + """ + samples, length = random_numbers.shape + if samples != len(boots) - 1: + raise ValueError("Random numbers do not have the correct shape.") + + if samples < length: + raise ValueError("Obs can't be reconstructed if there are fewer bootstrap samples than Monte Carlo data points.") + + proj = np.vstack([np.bincount(o, minlength=length) for o in random_numbers]) / length + + samples = scipy.linalg.lstsq(proj, boots[1:])[0] + ret = Obs([samples], [name]) + ret._value = boots[0] + return ret + + def merge_obs(list_of_obs): """Combine all observables in list_of_obs into one new observable diff --git a/tests/obs_test.py b/tests/obs_test.py index c8b741c2..ed538943 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1094,6 +1094,20 @@ def test_import_jackknife(): assert my_obs == reconstructed_obs +def test_import_bootstrap(): + seed = 4321 + samples = 1234 + length = 820 + name = "test" + + rng = np.random.default_rng(seed) + random_numbers = rng.integers(0, length, size=(samples, length)) + obs = pe.pseudo_Obs(2.447, 0.14, name, length) + boots = obs.export_bootstrap(1234, random_numbers=random_numbers) + re_obs = pe.import_bootstrap(boots, name, random_numbers=random_numbers) + assert obs == re_obs + + def test_reduce_deltas(): idx_old = range(1, 101) deltas = [float(i) for i in idx_old] From 8736d1cd3cb76a188b78769f7b2bbc38ecc39081 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 14 Jul 2023 13:38:21 +0100 Subject: [PATCH 034/107] feat: CObs format added and complex Corr print improved. (#200) --- pyerrors/correlators.py | 4 +--- pyerrors/obs.py | 8 ++++++++ tests/obs_test.py | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 8573cdbf..85e28f4d 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -998,8 +998,6 @@ class Corr: content_string += "Description: " + self.tag + "\n" if self.N != 1: return content_string - if isinstance(self[0], CObs): - return content_string if print_range[1]: print_range[1] += 1 @@ -1010,7 +1008,7 @@ class Corr: else: content_string += str(i + print_range[0]) for element in sub_corr: - content_string += '\t' + ' ' * int(element >= 0) + str(element) + content_string += f"\t{element:+2}" content_string += '\n' return content_string diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 85fdf851..a25dceb8 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1023,6 +1023,14 @@ class CObs: def __repr__(self): return 'CObs[' + str(self) + ']' + def __format__(self, format_type): + if format_type == "": + significance = 2 + format_type = "2" + else: + significance = int(float(format_type.replace("+", "").replace("-", ""))) + return f"({self.real:{format_type}}{self.imag:+{significance}}j)" + def _format_uncertainty(value, dvalue, significance=2): """Creates a string of a value and its error in paranthesis notation, e.g., 13.02(45)""" diff --git a/tests/obs_test.py b/tests/obs_test.py index ed538943..924d98cd 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1297,6 +1297,16 @@ def test_f_string_obs(): print(f"{o1:-1}") print(f"{o1: 8}") +def test_f_string_cobs(): + o_real = pe.pseudo_Obs(0.348, 0.0123, "test") + o_imag = pe.pseudo_Obs(0.348, 0.0123, "test") + o1 = pe.CObs(o_real, o_imag) + print(f"{o1}") + print(f"{o1:3}") + print(f"{o1:+3}") + print(f"{o1:-1}") + print(f"{o1: 8}") + def test_compute_drho_fails(): obs = pe.input.json.load_json("tests/data/compute_drho_fails.json.gz") obs.gm() From 6dcd0c35182d26c93263191f255bb445c9667ba1 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Fri, 14 Jul 2023 15:21:59 +0200 Subject: [PATCH 035/107] feat: Added numerical integration of generic functions (#201) * feat: Added numerical integration of generic functions * refactored integration routines * tests: two trivial tests for integration added. * docs: quad docstring corrected. * Small bugfix for integration without obs --------- Co-authored-by: Fabian Joswig --- pyerrors/__init__.py | 1 + pyerrors/integrate.py | 87 +++++++++++++++++++++++++++++++++++++++++ tests/integrate_test.py | 51 ++++++++++++++++++++++++ tests/linalg_test.py | 4 +- 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 pyerrors/integrate.py create mode 100644 tests/integrate_test.py diff --git a/pyerrors/__init__.py b/pyerrors/__init__.py index df2c983a..ee259b24 100644 --- a/pyerrors/__init__.py +++ b/pyerrors/__init__.py @@ -485,5 +485,6 @@ from . import input from . import linalg from . import mpm from . import roots +from . import integrate from .version import __version__ diff --git a/pyerrors/integrate.py b/pyerrors/integrate.py new file mode 100644 index 00000000..3b48f3fb --- /dev/null +++ b/pyerrors/integrate.py @@ -0,0 +1,87 @@ +import numpy as np +from .obs import derived_observable, Obs +from autograd import jacobian +from scipy.integrate import quad as squad + + +def quad(func, p, a, b, **kwargs): + '''Performs a (one-dimensional) numeric integration of f(p, x) from a to b. + + The integration is performed using scipy.integrate.quad(). + All parameters that can be passed to scipy.integrate.quad may also be passed to this function. + The output is the same as for scipy.integrate.quad, the first element being an Obs. + + Parameters + ---------- + func : object + function to integrate, has to be of the form + + ```python + import autograd.numpy as anp + + def func(p, x): + return p[0] + p[1] * x + p[2] * anp.sinh(x) + ``` + where x is the integration variable. + p : list of floats or Obs + parameters of the function func. + a: float or Obs + Lower limit of integration (use -numpy.inf for -infinity). + b: float or Obs + Upper limit of integration (use -numpy.inf for -infinity). + All parameters of scipy.integrate.quad + + Returns + ------- + y : Obs + The integral of func from `a` to `b`. + abserr : float + An estimate of the absolute error in the result. + infodict : dict + A dictionary containing additional information. + Run scipy.integrate.quad_explain() for more information. + message + A convergence message. + explain + Appended only with 'cos' or 'sin' weighting and infinite + integration limits, it contains an explanation of the codes in + infodict['ierlst'] + ''' + + Np = len(p) + isobs = [True if isinstance(pi, Obs) else False for pi in p] + pval = np.array([p[i].value if isobs[i] else p[i] for i in range(Np)],) + pobs = [p[i] for i in range(Np) if isobs[i]] + + bounds = [a, b] + isobs_b = [True if isinstance(bi, Obs) else False for bi in bounds] + bval = np.array([bounds[i].value if isobs_b[i] else bounds[i] for i in range(2)]) + bobs = [bounds[i] for i in range(2) if isobs_b[i]] + bsign = [-1, 1] + + ifunc = np.vectorize(lambda x: func(pval, x)) + + intpars = squad.__code__.co_varnames[3:3 + len(squad.__defaults__)] + ikwargs = {k: kwargs[k] for k in intpars if k in kwargs} + + integration_result = squad(ifunc, bval[0], bval[1], **ikwargs) + val = integration_result[0] + + jac = jacobian(func) + + derivint = [] + for i in range(Np): + if isobs[i]: + ifunc = np.vectorize(lambda x: jac(pval, x)[i]) + derivint.append(squad(ifunc, bounds[0], bounds[1], **ikwargs)[0]) + + for i in range(2): + if isobs_b[i]: + derivint.append(bsign[i] * func(pval, bval[i])) + + if len(derivint) == 0: + return integration_result + + res = derived_observable(lambda x, **kwargs: 0 * (x[0] + np.finfo(np.float64).eps) * (pval[0] + np.finfo(np.float64).eps) + val, pobs + bobs, man_grad=derivint) + + return (res, *integration_result[1:]) diff --git a/tests/integrate_test.py b/tests/integrate_test.py new file mode 100644 index 00000000..07a4313b --- /dev/null +++ b/tests/integrate_test.py @@ -0,0 +1,51 @@ +import numpy as np +import autograd.numpy as anp +import pyerrors as pe + + +def test_integration(): + def f(p, x): + return p[0] * x + p[1] * x**2 - p[2] / x + + def F(p, x): + return p[0] * x**2 / 2. + p[1] * x**3 / 3. - anp.log(x) * p[2] + + def check_ana_vs_int(p, l, u, **kwargs): + numint_full = pe.integrate.quad(f, p, l, u, **kwargs) + numint = numint_full[0] + + anaint = F(p, u) - F(p, l) + diff = (numint - anaint) + + if isinstance(numint, pe.Obs): + numint.gm() + anaint.gm() + + assert(diff.is_zero()) + else: + assert(np.isclose(0, diff)) + + pobs = np.array([pe.cov_Obs(1., .1**2, '0'), pe.cov_Obs(2., .2**2, '1'), pe.cov_Obs(2.2, .17**2, '2')]) + lobs = pe.cov_Obs(.123, .012**2, 'l') + uobs = pe.cov_Obs(1., .05**2, 'u') + + check_ana_vs_int(pobs, lobs, uobs) + check_ana_vs_int(pobs, lobs.value, uobs) + check_ana_vs_int(pobs, lobs, uobs.value) + check_ana_vs_int(pobs, lobs.value, uobs.value) + for i in range(len(pobs)): + p = [pi for pi in pobs] + p[i] = pobs[i].value + check_ana_vs_int(p, lobs, uobs) + + check_ana_vs_int([pi.value for pi in pobs], lobs, uobs) + check_ana_vs_int([pi.value for pi in pobs], lobs.value, uobs.value) + + check_ana_vs_int(pobs, lobs, uobs, epsabs=1.e-9, epsrel=1.236e-10, limit=100) + assert(len(pe.integrate.quad(f, pobs, lobs, uobs, full_output=True)) > 2) + + r1, _ = pe.integrate.quad(F, pobs, 1, 0.1) + r2, _ = pe.integrate.quad(F, pobs, 0.1, 1) + assert r1 == -r2 + iamzero, _ = pe.integrate.quad(F, pobs, 1, 1) + assert iamzero == 0 diff --git a/tests/linalg_test.py b/tests/linalg_test.py index babf3797..ec0591ba 100644 --- a/tests/linalg_test.py +++ b/tests/linalg_test.py @@ -14,9 +14,9 @@ def get_real_matrix(dimension): exponent_imag = np.random.normal(0, 1) base_matrix[n, m] = pe.Obs([np.random.normal(1.0, 0.1, 100)], ['t']) - return base_matrix + def get_complex_matrix(dimension): base_matrix = np.empty((dimension, dimension), dtype=object) for (n, m), entry in np.ndenumerate(base_matrix): @@ -109,7 +109,6 @@ def test_einsum(): assert np.all([o.imag.is_zero_within_error(0.001) for o in arr]) assert np.all([o.imag.dvalue < 0.001 for o in arr]) - tt = [get_real_matrix(4), get_real_matrix(3)] q = np.tensordot(tt[0], tt[1], 0) c1 = tt[1] @ q @@ -355,3 +354,4 @@ def test_complex_matrix_real_entries(): my_mat[0, 1] = 4 my_mat[2, 0] = pe.Obs([np.random.normal(1.0, 0.1, 100)], ['t']) assert np.all((my_mat @ pe.linalg.inv(my_mat) - np.identity(4)) == 0) + From 5c2a6de56f013c977dbe8e466aa5243f6d52e7c0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 14 Jul 2023 14:26:54 +0100 Subject: [PATCH 036/107] fix: explicit Exception for combined fit constant edge case. (#202) --- pyerrors/fits.py | 6 ++++++ tests/fits_test.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 04051ffd..5ec857e0 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -230,6 +230,12 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): n_parms_ls.append(n_loc) n_parms = max(n_parms_ls) + + if len(key_ls) > 1: + for key in key_ls: + if np.asarray(yd[key]).shape != funcd[key](np.arange(n_parms), xd[key]).shape: + raise ValueError(f"Fit function {key} returns the wrong shape ({funcd[key](np.arange(n_parms), xd[key]).shape} instead of {xd[key].shape})\nIf the fit function is just a constant you could try adding x*0 to get the correct shape.") + if not silent: print('Fit with', n_parms, 'parameter' + 's' * (n_parms > 1)) diff --git a/tests/fits_test.py b/tests/fits_test.py index 48e788bd..80b6de5a 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -1143,6 +1143,23 @@ def test_fit_dof(): assert np.all(np.array(cd[1:]) > 0) +def test_combined_fit_constant_shape(): + N1 = 16 + N2 = 10 + x = {"a": np.arange(N1), + "": np.arange(N2)} + y = {"a": [pe.pseudo_Obs(o + np.random.normal(0.0, 0.1), 0.1, "test") for o in range(N1)], + "": [pe.pseudo_Obs(o + np.random.normal(0.0, 0.1), 0.1, "test") for o in range(N2)]} + funcs = {"a": lambda a, x: a[0] + a[1] * x, + "": lambda a, x: a[1]} + with pytest.raises(ValueError): + pe.fits.least_squares(x, y, funcs, method='migrad') + + funcs = {"a": lambda a, x: a[0] + a[1] * x, + "": lambda a, x: a[1] + x * 0} + pe.fits.least_squares(x, y, funcs, method='migrad') + + def fit_general(x, y, func, silent=False, **kwargs): """Performs a non-linear fit to y = func(x) and returns a list of Obs corresponding to the fit parameters. From 13433912123086beaaa596351d6461007c7044d4 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 17 Jul 2023 10:19:20 +0100 Subject: [PATCH 037/107] docs: ask a question added to README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e3436706..93afe409 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ - **Documentation:** https://fjosw.github.io/pyerrors/pyerrors.html - **Examples:** https://github.com/fjosw/pyerrors/tree/develop/examples +- **Ask a question:** https://github.com/fjosw/pyerrors/discussions/new?category=q-a - **Changelog:** https://github.com/fjosw/pyerrors/blob/develop/CHANGELOG.md - **Bug reports:** https://github.com/fjosw/pyerrors/issues From f1150f09c826ae78b828866401bc6fda1b668edd Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 17 Jul 2023 11:48:57 +0100 Subject: [PATCH 038/107] Matmul overloaded for correlator class. (#199) * feat: matmul method added to correlator class. * feat: corr, corr matmul and correlator matrix trace added. * tests: tests for matmul and trace added. * tests: slightly reduced tolerance and good guess bad guess test. * feat: rmatmul added and __array_priority__ set. * tests: additional tests for rmatmul added. * tests: one more tests for rmatmul added. * docs: docstring added to Corr.trace. * tests: associative property test added for complex Corr matmul. * fix: Corr.roll method now also works for correlator matrices by explicitly specifying the axis. Co-authored-by: Matteo Di Carlo * feat: exception type for correlator trace of 1dim correlator changed. * tests: trace N=1 exception tested. --------- Co-authored-by: Matteo Di Carlo --- pyerrors/correlators.py | 63 +++++++++++++++++++- tests/correlators_test.py | 122 ++++++++++++++++++++++++++++++++++++++ tests/fits_test.py | 6 +- 3 files changed, 185 insertions(+), 6 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 85e28f4d..d78a3c28 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -220,7 +220,7 @@ class Corr: def anti_symmetric(self): """Anti-symmetrize the correlator around x0=0.""" if self.N != 1: - raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.') + raise TypeError('anti_symmetric cannot be safely applied to multi-dimensional correlators.') if self.T % 2 != 0: raise Exception("Can not symmetrize odd T") @@ -242,7 +242,7 @@ class Corr: def is_matrix_symmetric(self): """Checks whether a correlator matrices is symmetric on every timeslice.""" if self.N == 1: - raise Exception("Only works for correlator matrices.") + raise TypeError("Only works for correlator matrices.") for t in range(self.T): if self[t] is None: continue @@ -254,6 +254,18 @@ class Corr: return False return True + def trace(self): + """Calculates the per-timeslice trace of a correlator matrix.""" + if self.N == 1: + raise ValueError("Only works for correlator matrices.") + newcontent = [] + for t in range(self.T): + if _check_for_none(self, self.content[t]): + newcontent.append(None) + else: + newcontent.append(np.trace(self.content[t])) + return Corr(newcontent) + def matrix_symmetric(self): """Symmetrizes the correlator matrices on every timeslice.""" if self.N == 1: @@ -405,7 +417,7 @@ class Corr: dt : int number of timeslices """ - return Corr(list(np.roll(np.array(self.content, dtype=object), dt))) + return Corr(list(np.roll(np.array(self.content, dtype=object), dt, axis=0))) def reverse(self): """Reverse the time ordering of the Corr""" @@ -1020,6 +1032,8 @@ class Corr: # This is because Obs*Corr checks Obs.__mul__ first and does not catch an exception. # One could try and tell Obs to check if the y in __mul__ is a Corr and + __array_priority__ = 10000 + def __add__(self, y): if isinstance(y, Corr): if ((self.N != y.N) or (self.T != y.T)): @@ -1076,6 +1090,49 @@ class Corr: else: raise TypeError("Corr * wrong type") + def __matmul__(self, y): + if isinstance(y, np.ndarray): + if y.ndim != 2 or y.shape[0] != y.shape[1]: + raise ValueError("Can only multiply correlators by square matrices.") + if not self.N == y.shape[0]: + raise ValueError("matmul: mismatch of matrix dimensions") + newcontent = [] + for t in range(self.T): + if _check_for_none(self, self.content[t]): + newcontent.append(None) + else: + newcontent.append(self.content[t] @ y) + return Corr(newcontent) + elif isinstance(y, Corr): + if not self.N == y.N: + raise ValueError("matmul: mismatch of matrix dimensions") + newcontent = [] + for t in range(self.T): + if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): + newcontent.append(None) + else: + newcontent.append(self.content[t] @ y.content[t]) + return Corr(newcontent) + + else: + return NotImplemented + + def __rmatmul__(self, y): + if isinstance(y, np.ndarray): + if y.ndim != 2 or y.shape[0] != y.shape[1]: + raise ValueError("Can only multiply correlators by square matrices.") + if not self.N == y.shape[0]: + raise ValueError("matmul: mismatch of matrix dimensions") + newcontent = [] + for t in range(self.T): + if _check_for_none(self, self.content[t]): + newcontent.append(None) + else: + newcontent.append(y @ self.content[t]) + return Corr(newcontent) + else: + return NotImplemented + def __truediv__(self, y): if isinstance(y, Corr): if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): diff --git a/tests/correlators_test.py b/tests/correlators_test.py index c5b530cb..0502462c 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -570,3 +570,125 @@ def test_corr_symmetric(): assert scorr[1] == scorr[3] assert scorr[2] == corr[2] assert scorr[0] == corr[0] + + +def test_two_matrix_corr_inits(): + T = 4 + rn = lambda : np.random.normal(0.5, 0.1) + + # Generate T random CObs in a list + list_of_timeslices =[] + for i in range(T): + re = pe.pseudo_Obs(rn(), rn(), "test") + im = pe.pseudo_Obs(rn(), rn(), "test") + list_of_timeslices.append(pe.CObs(re, im)) + + # First option: Correlator of matrix of correlators + corr = pe.Corr(list_of_timeslices) + mat_corr1 = pe.Corr(np.array([[corr, corr], [corr, corr]])) + + # Second option: Correlator of list of arrays per timeslice + list_of_arrays = [np.array([[elem, elem], [elem, elem]]) for elem in list_of_timeslices] + mat_corr2 = pe.Corr(list_of_arrays) + + for el in mat_corr1 - mat_corr2: + assert np.all(el == 0) + + +def test_matmul_overloading(): + N = 4 + rn = lambda : np.random.normal(0.5, 0.1) + + # Generate N^2 random CObs and assemble them in an array + ll =[] + for i in range(N ** 2): + re = pe.pseudo_Obs(rn(), rn(), "test") + im = pe.pseudo_Obs(rn(), rn(), "test") + ll.append(pe.CObs(re, im)) + mat = np.array(ll).reshape(N, N) + + # Multiply with gamma matrix + corr = pe.Corr([mat] * 4, padding=[0, 1]) + + # __matmul__ + mcorr = corr @ pe.dirac.gammaX + comp = mat @ pe.dirac.gammaX + for i in range(4): + assert np.all(mcorr[i] == comp) + + # __rmatmul__ + mcorr = pe.dirac.gammaX @ corr + comp = pe.dirac.gammaX @ mat + for i in range(4): + assert np.all(mcorr[i] == comp) + + test_mat = pe.dirac.gamma5 + pe.dirac.gammaX + icorr = corr @ test_mat @ np.linalg.inv(test_mat) + tt = corr - icorr + for i in range(4): + assert np.all(tt[i] == 0) + + # associative property + tt = (corr.real @ pe.dirac.gammaX + corr.imag @ (pe.dirac.gammaX * 1j)) - corr @ pe.dirac.gammaX + for el in tt: + if el is not None: + assert np.all(el == 0) + + corr2 = corr @ corr + for i in range(4): + np.all(corr2[i] == corr[i] @ corr[i]) + + +def test_matrix_trace(): + N = 4 + rn = lambda : np.random.normal(0.5, 0.1) + + # Generate N^2 random CObs and assemble them in an array + ll =[] + for i in range(N ** 2): + re = pe.pseudo_Obs(rn(), rn(), "test") + im = pe.pseudo_Obs(rn(), rn(), "test") + ll.append(pe.CObs(re, im)) + mat = np.array(ll).reshape(N, N) + + corr = pe.Corr([mat] * 4) + + # Explicitly check trace + for el in corr.trace(): + el == np.sum(np.diag(mat)) + + # Trace is cyclic + for one, two in zip((pe.dirac.gammaX @ corr).trace(), (corr @ pe.dirac.gammaX).trace()): + assert np.all(one == two) + + # Antisymmetric matrices are traceless. + mat = (mat - mat.T) / 2 + corr = pe.Corr([mat] * 4) + for el in corr.trace(): + assert el == 0 + + + with pytest.raises(ValueError): + corr.item(0, 0).trace() + + +def test_corr_roll(): + T = 4 + rn = lambda : np.random.normal(0.5, 0.1) + + ll = [] + for i in range(T): + re = pe.pseudo_Obs(rn(), rn(), "test") + im = pe.pseudo_Obs(rn(), rn(), "test") + ll.append(pe.CObs(re, im)) + + # Rolling by T should produce the same correlator + corr = pe.Corr(ll) + tt = corr - corr.roll(T) + for el in tt: + assert np.all(el == 0) + + mcorr = pe.Corr(np.array([[corr, corr + 0.1], [corr - 0.1, 2 * corr]])) + tt = mcorr.roll(T) - mcorr + for el in tt: + assert np.all(el == 0) diff --git a/tests/fits_test.py b/tests/fits_test.py index 80b6de5a..dbc227dc 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -235,12 +235,12 @@ def test_fit_corr_independent(): def test_linear_fit_guesses(): - for err in [10, 0.1, 0.001]: + for err in [1.2, 0.1, 0.001]: xvals = [] yvals = [] for x in range(1, 8, 2): xvals.append(x) - yvals.append(pe.pseudo_Obs(x + np.random.normal(0.0, err), err, 'test1') + pe.pseudo_Obs(0, err / 100, 'test2', samples=87)) + yvals.append(pe.pseudo_Obs(x + np.random.normal(0.0, err), err, 'test1') + pe.pseudo_Obs(0, err / 97, 'test2', samples=87)) lin_func = lambda a, x: a[0] + a[1] * x with pytest.raises(Exception): pe.least_squares(xvals, yvals, lin_func) @@ -251,7 +251,7 @@ def test_linear_fit_guesses(): bad_guess = pe.least_squares(xvals, yvals, lin_func, initial_guess=[999, 999]) good_guess = pe.least_squares(xvals, yvals, lin_func, initial_guess=[0, 1]) assert np.isclose(bad_guess.chisquare, good_guess.chisquare, atol=1e-8) - assert np.all([(go - ba).is_zero(atol=1e-6) for (go, ba) in zip(good_guess, bad_guess)]) + assert np.all([(go - ba).is_zero(atol=5e-5) for (go, ba) in zip(good_guess, bad_guess)]) def test_total_least_squares(): From bf1c7621482aab22e91ea8ce0a2b02c93c691309 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 17 Jul 2023 13:34:39 +0100 Subject: [PATCH 039/107] docs: read_meson docstring improved. --- pyerrors/input/hadrons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/input/hadrons.py b/pyerrors/input/hadrons.py index 18c6ec66..0a80f122 100644 --- a/pyerrors/input/hadrons.py +++ b/pyerrors/input/hadrons.py @@ -70,7 +70,7 @@ def read_meson_hd5(path, filestem, ens_id, meson='meson_0', idl=None, gammas=Non corresponds to the pseudoscalar pseudoscalar two-point function. gammas : tuple of strings Instrad of a meson label one can also provide a tuple of two strings - indicating the gamma matrices at source and sink. + indicating the gamma matrices at sink and source (gamma_snk, gamma_src). ("Gamma5", "Gamma5") corresponds to the pseudoscalar pseudoscalar two-point function. The gammas argument dominateds over meson. idl : range From ad188acec4f0165dfca81ae3817dcfbd3b38a169 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 17 Jul 2023 15:34:25 +0100 Subject: [PATCH 040/107] docs: instruction for installing develop branch updated. --- pyerrors/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyerrors/__init__.py b/pyerrors/__init__.py index ee259b24..731c43cf 100644 --- a/pyerrors/__init__.py +++ b/pyerrors/__init__.py @@ -37,8 +37,9 @@ conda update -c conda-forge pyerrors # Update ``` Install the current `develop` version: ```bash -python -m pip install git+https://github.com/fjosw/pyerrors.git@develop +python -m pip install -U --no-deps --force-reinstall git+https://github.com/fjosw/pyerrors.git@develop ``` +(Also works for any feature branch). ## Basic example From 491c7bcb0452cb4b0355fd44d6bca941bec86718 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 18 Jul 2023 10:36:19 +0100 Subject: [PATCH 041/107] fix: split meson on last underscore in read_meson_hdf5. (#204) --- pyerrors/input/hadrons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/input/hadrons.py b/pyerrors/input/hadrons.py index 0a80f122..24e86c6d 100644 --- a/pyerrors/input/hadrons.py +++ b/pyerrors/input/hadrons.py @@ -84,7 +84,7 @@ def read_meson_hd5(path, filestem, ens_id, meson='meson_0', idl=None, gammas=Non files, idx = _get_files(path, filestem, idl) - tree = meson.rsplit('_')[0] + tree = meson.rsplit('_', 1)[0] if gammas is not None: h5file = h5py.File(path + '/' + files[0], "r") found_meson = None From 66d5f8be24fd252a320b5bf510621eb3d63b6ed0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 18 Jul 2023 12:07:46 +0100 Subject: [PATCH 042/107] Corr array initialization generalized (#203) * feat: corr array initialization generalized. * feat: additional checks for three-dimensional arrays added. * docs: Corr docstring improved. * docs: typos corrected. * docs: details about None padding added to Corr docstring. --- pyerrors/correlators.py | 98 ++++++++++++++++++++++++--------------- tests/correlators_test.py | 21 +++++++++ 2 files changed, 81 insertions(+), 38 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index d78a3c28..002d2c87 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -11,16 +11,32 @@ from .roots import find_root class Corr: - """The class for a correlator (time dependent sequence of pe.Obs). + r"""The class for a correlator (time dependent sequence of pe.Obs). Everything, this class does, can be achieved using lists or arrays of Obs. But it is simply more convenient to have a dedicated object for correlators. One often wants to add or multiply correlators of the same length at every timeslice and it is inconvenient to iterate over all timeslices for every operation. This is especially true, when dealing with matrices. - The correlator can have two types of content: An Obs at every timeslice OR a GEVP - matrix at every timeslice. Other dependency (eg. spatial) are not supported. + The correlator can have two types of content: An Obs at every timeslice OR a matrix at every timeslice. + Other dependency (eg. spatial) are not supported. + The Corr class can also deal with missing measurements or paddings for fixed boundary conditions. + The missing entries are represented via the `None` object. + + Initialization + -------------- + A simple correlator can be initialized with a list or a one-dimensional array of `Obs` or `Cobs` + ```python + corr11 = pe.Corr([obs1, obs2]) + corr11 = pe.Corr(np.array([obs1, obs2])) + ``` + A matrix-valued correlator can either be initialized via a two-dimensional array of `Corr` objects + ```python + matrix_corr = pe.Corr(np.array([[corr11, corr12], [corr21, corr22]])) + ``` + or alternatively via a three-dimensional array of `Obs` or `CObs` of shape (T, N, N) where T is + the temporal extent of the correlator and N is the dimension of the matrix. """ __slots__ = ["content", "N", "T", "tag", "prange"] @@ -31,43 +47,50 @@ class Corr: Parameters ---------- data_input : list or array - list of Obs or list of arrays of Obs or array of Corrs + list of Obs or list of arrays of Obs or array of Corrs (see class docstring for details). padding : list, optional List with two entries where the first labels the padding at the front of the correlator and the second the padding at the back. prange : list, optional List containing the first and last timeslice of the plateau - region indentified for this correlator. + region identified for this correlator. """ if isinstance(data_input, np.ndarray): + if data_input.ndim == 1: + data_input = list(data_input) + elif data_input.ndim == 2: + if not data_input.shape[0] == data_input.shape[1]: + raise ValueError("Array needs to be square.") + if not all([isinstance(item, Corr) for item in data_input.flatten()]): + raise ValueError("If the input is an array, its elements must be of type pe.Corr.") + if not all([item.N == 1 for item in data_input.flatten()]): + raise ValueError("Can only construct matrix correlator from single valued correlators.") + if not len(set([item.T for item in data_input.flatten()])) == 1: + raise ValueError("All input Correlators must be defined over the same timeslices.") - # This only works, if the array fulfills the conditions below - if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]: - raise Exception("Incompatible array shape") - if not all([isinstance(item, Corr) for item in data_input.flatten()]): - raise Exception("If the input is an array, its elements must be of type pe.Corr") - if not all([item.N == 1 for item in data_input.flatten()]): - raise Exception("Can only construct matrix correlator from single valued correlators") - if not len(set([item.T for item in data_input.flatten()])) == 1: - raise Exception("All input Correlators must be defined over the same timeslices.") - - T = data_input[0, 0].T - N = data_input.shape[0] - input_as_list = [] - for t in range(T): - if any([(item.content[t] is None) for item in data_input.flatten()]): - if not all([(item.content[t] is None) for item in data_input.flatten()]): - warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning) - input_as_list.append(None) - else: - array_at_timeslace = np.empty([N, N], dtype="object") - for i in range(N): - for j in range(N): - array_at_timeslace[i, j] = data_input[i, j][t] - input_as_list.append(array_at_timeslace) - data_input = input_as_list + T = data_input[0, 0].T + N = data_input.shape[0] + input_as_list = [] + for t in range(T): + if any([(item.content[t] is None) for item in data_input.flatten()]): + if not all([(item.content[t] is None) for item in data_input.flatten()]): + warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss.!", RuntimeWarning) + input_as_list.append(None) + else: + array_at_timeslace = np.empty([N, N], dtype="object") + for i in range(N): + for j in range(N): + array_at_timeslace[i, j] = data_input[i, j][t] + input_as_list.append(array_at_timeslace) + data_input = input_as_list + elif data_input.ndim == 3: + if not data_input.shape[1] == data_input.shape[2]: + raise ValueError("Array needs to be square.") + data_input = list(data_input) + else: + raise ValueError("Arrays with ndim>3 not supported.") if isinstance(data_input, list): @@ -75,19 +98,18 @@ class Corr: _assert_equal_properties([o for o in data_input if o is not None]) self.content = [np.asarray([item]) if item is not None else None for item in data_input] self.N = 1 - elif all([isinstance(item, np.ndarray) or item is None for item in data_input]) and any([isinstance(item, np.ndarray) for item in data_input]): self.content = data_input noNull = [a for a in self.content if not (a is None)] # To check if the matrices are correct for all undefined elements self.N = noNull[0].shape[0] if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]: - raise Exception("Smearing matrices are not NxN") + raise ValueError("Smearing matrices are not NxN.") if (not all([item.shape == noNull[0].shape for item in noNull])): - raise Exception("Items in data_input are not of identical shape." + str(noNull)) + raise ValueError("Items in data_input are not of identical shape." + str(noNull)) else: - raise Exception("data_input contains item of wrong type") + raise TypeError("'data_input' contains item of wrong type.") else: - raise Exception("Data input was not given as list or correct array") + raise TypeError("Data input was not given as list or correct array.") self.tag = None @@ -500,7 +522,7 @@ class Corr: ---------- partner : Corr Time symmetry partner of the Corr - partity : int + parity : int Parity quantum number of the correlator, can be +1 or -1 """ if self.N != 1: @@ -658,8 +680,8 @@ class Corr: ---------- variant : str log : uses the standard effective mass log(C(t) / C(t+1)) - cosh, periodic : Use periodicitiy of the correlator by solving C(t) / C(t+1) = cosh(m * (t - T/2)) / cosh(m * (t + 1 - T/2)) for m. - sinh : Use anti-periodicitiy of the correlator by solving C(t) / C(t+1) = sinh(m * (t - T/2)) / sinh(m * (t + 1 - T/2)) for m. + cosh, periodic : Use periodicity of the correlator by solving C(t) / C(t+1) = cosh(m * (t - T/2)) / cosh(m * (t + 1 - T/2)) for m. + sinh : Use anti-periodicity of the correlator by solving C(t) / C(t+1) = sinh(m * (t - T/2)) / sinh(m * (t + 1 - T/2)) for m. See, e.g., arXiv:1205.5380 arccosh : Uses the explicit form of the symmetrized correlator (not recommended) logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2 diff --git a/tests/correlators_test.py b/tests/correlators_test.py index 0502462c..5b1c5e62 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -572,6 +572,27 @@ def test_corr_symmetric(): assert scorr[0] == corr[0] +def test_corr_array_ndim1_init(): + y = [pe.pseudo_Obs(2 + np.random.normal(0.0, 0.1), .1, 't') for i in np.arange(5)] + cc1 = pe.Corr(y) + cc2 = pe.Corr(np.array(y)) + assert np.all([o1 == o2 for o1, o2 in zip(cc1, cc2)]) + + +def test_corr_array_ndim3_init(): + y = np.array([pe.pseudo_Obs(np.random.normal(2.0, 0.1), .1, 't') for i in np.arange(12)]).reshape(3, 2, 2) + tt1 = pe.Corr(list(y)) + tt2 = pe.Corr(y) + tt3 = pe.Corr(np.array([pe.Corr(o) for o in y.reshape(3, 4).T]).reshape(2, 2)) + assert np.all([o1 == o2 for o1, o2 in zip(tt1, tt2)]) + assert np.all([o1 == o2 for o1, o2 in zip(tt1, tt3)]) + assert tt1.T == y.shape[0] + assert tt1.N == y.shape[1] == y.shape[2] + + with pytest.raises(ValueError): + pe.Corr(y.reshape(6, 2, 1)) + + def test_two_matrix_corr_inits(): T = 4 rn = lambda : np.random.normal(0.5, 0.1) From 6b7846486d6bc3c636d551f811d6e754d9e6ac55 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 18 Jul 2023 16:04:59 +0100 Subject: [PATCH 043/107] General hadrons hdf5 reader added (#205) * feat: new general read_hd5 function added. * feat: real or imaginary part can be specified in read_hd5. * fix: spacing fixed. * feat: Added the option to extract complex correlators in read_hd5. --- pyerrors/input/hadrons.py | 135 +++++++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 39 deletions(-) diff --git a/pyerrors/input/hadrons.py b/pyerrors/input/hadrons.py index 24e86c6d..5b65f3c6 100644 --- a/pyerrors/input/hadrons.py +++ b/pyerrors/input/hadrons.py @@ -54,6 +54,92 @@ def _get_files(path, filestem, idl): return filtered_files, idx +def read_hd5(filestem, ens_id, group, attrs=None, idl=None, part="real"): + r'''Read hadrons hdf5 file and extract entry based on attributes. + + Parameters + ----------------- + filestem : str + Full namestem of the files to read, including the full path. + ens_id : str + name of the ensemble, required for internal bookkeeping + group : str + label of the group to be extracted. + attrs : dict or int + Dictionary containing the attributes. For example + ```python + attrs = {"gamma_snk": "Gamma5", + "gamma_src": "Gamma5"} + ``` + Alternatively an integer can be specified to identify the sub group. + This is discouraged as the order in the file is not guaranteed. + idl : range + If specified only configurations in the given range are read in. + part: str + string specifying whether to extract the real part ('real'), + the imaginary part ('imag') or a complex correlator ('complex'). + Default 'real'. + + Returns + ------- + corr : Corr + Correlator of the source sink combination in question. + ''' + + path_obj = Path(filestem) + path = path_obj.parent.as_posix() + filestem = path_obj.stem + + files, idx = _get_files(path, filestem, idl) + + if isinstance(attrs, dict): + h5file = h5py.File(path + '/' + files[0], "r") + entry = None + for key in h5file[group].keys(): + if attrs.items() <= {k: v[0].decode() for k, v in h5file[group][key].attrs.items()}.items(): + if entry is None: + entry = key + else: + raise ValueError("More than one fitting entry found. More constraint on attributes needed.") + h5file.close() + if entry is None: + raise ValueError(f"Entry with attributes {attrs} not found.") + elif isinstance(attrs, int): + entry = group + f"_{attrs}" + else: + raise TypeError("Invalid type for 'attrs'. Needs to be dict or int.") + + corr_data = [] + infos = [] + for hd5_file in files: + h5file = h5py.File(path + '/' + hd5_file, "r") + if not group + '/' + entry in h5file: + raise Exception("Entry '" + entry + "' not contained in the files.") + raw_data = h5file[group + '/' + entry + '/corr'] + real_data = raw_data[:].view("complex") + corr_data.append(real_data) + if not infos: + for k, i in h5file[group + '/' + entry].attrs.items(): + infos.append(k + ': ' + i[0].decode()) + h5file.close() + corr_data = np.array(corr_data) + + if part == "complex": + l_obs = [] + for c in corr_data.T: + l_obs.append(CObs(Obs([c.real], [ens_id], idl=[idx]), + Obs([c.imag], [ens_id], idl=[idx]))) + else: + corr_data = getattr(corr_data, part) + l_obs = [] + for c in corr_data.T: + l_obs.append(Obs([c], [ens_id], idl=[idx])) + + corr = Corr(l_obs) + corr.tag = r", ".join(infos) + return corr + + def read_meson_hd5(path, filestem, ens_id, meson='meson_0', idl=None, gammas=None): r'''Read hadrons meson hdf5 file and extract the meson labeled 'meson' @@ -81,45 +167,16 @@ def read_meson_hd5(path, filestem, ens_id, meson='meson_0', idl=None, gammas=Non corr : Corr Correlator of the source sink combination in question. ''' - - files, idx = _get_files(path, filestem, idl) - - tree = meson.rsplit('_', 1)[0] - if gammas is not None: - h5file = h5py.File(path + '/' + files[0], "r") - found_meson = None - for key in h5file[tree].keys(): - if gammas[0] == h5file[tree][key].attrs["gamma_snk"][0].decode() and h5file[tree][key].attrs["gamma_src"][0].decode() == gammas[1]: - found_meson = key - break - h5file.close() - if found_meson: - meson = found_meson - else: - raise Exception("Source Sink combination " + str(gammas) + " not found.") - - corr_data = [] - infos = [] - for hd5_file in files: - h5file = h5py.File(path + '/' + hd5_file, "r") - if not tree + '/' + meson in h5file: - raise Exception("Entry '" + meson + "' not contained in the files.") - raw_data = h5file[tree + '/' + meson + '/corr'] - real_data = raw_data[:]["re"].astype(np.double) - corr_data.append(real_data) - if not infos: - for k, i in h5file[tree + '/' + meson].attrs.items(): - infos.append(k + ': ' + i[0].decode()) - h5file.close() - corr_data = np.array(corr_data) - - l_obs = [] - for c in corr_data.T: - l_obs.append(Obs([c], [ens_id], idl=[idx])) - - corr = Corr(l_obs) - corr.tag = r", ".join(infos) - return corr + if gammas is None: + attrs = int(meson.rsplit('_', 1)[-1]) + else: + if len(gammas) != 2: + raise ValueError("'gammas' needs to have exactly two entries") + attrs = {"gamma_snk": gammas[0], + "gamma_src": gammas[1]} + return read_hd5(filestem=path + "/" + filestem, ens_id=ens_id, + group=meson.rsplit('_', 1)[0], attrs=attrs, idl=idl, + part="real") def _extract_real_arrays(path, files, tree, keys): From 1e438356fd56aa0644f101c9970db05cd3ed2275 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 19 Jul 2023 12:13:20 +0100 Subject: [PATCH 044/107] Vectorized gamma_method (#207) * feat: vectorized gamma_method function added. * feat: vectorized gamma method generalized to also work on other objects like Corr or Fit_result. * feat: alias gamma_method for vectorized gamma_method added. * docs: example 5 updated to include vectorized gamma_method. * docs: output of example 5 updated. --- examples/05_matrix_operations.ipynb | 73 +++++++++++++---------------- pyerrors/obs.py | 16 +++++++ tests/obs_test.py | 33 ++++++++++--- 3 files changed, 76 insertions(+), 46 deletions(-) diff --git a/examples/05_matrix_operations.ipynb b/examples/05_matrix_operations.ipynb index 926d1734..1f08951b 100644 --- a/examples/05_matrix_operations.ipynb +++ b/examples/05_matrix_operations.ipynb @@ -7,8 +7,7 @@ "outputs": [], "source": [ "import pyerrors as pe\n", - "import numpy as np\n", - "import scipy" + "import numpy as np" ] }, { @@ -27,8 +26,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[4.10(20)] Obs[-1.00(10)]]\n", - " [Obs[-1.00(10)] Obs[1.000(10)]]]\n" + "[[4.10(20) -1.00(10)]\n", + " [-1.00(10) 1.000(10)]]\n" ] } ], @@ -63,8 +62,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[17.81] Obs[-5.1]]\n", - " [Obs[-5.1] Obs[2.0]]]\n" + "[[17.81 -5.1]\n", + " [-5.1 2.0]]\n" ] } ], @@ -88,8 +87,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[4.1] Obs[-1.0]]\n", - " [Obs[-1.0] Obs[1.0]]]\n" + "[[4.1 -1.0]\n", + " [-1.0 1.0]]\n" ] } ], @@ -113,8 +112,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[78.12099999999998] Obs[-22.909999999999997]]\n", - " [Obs[-22.909999999999997] Obs[7.1]]]\n" + "[[78.12099999999998 -22.909999999999997]\n", + " [-22.909999999999997 7.1]]\n" ] } ], @@ -138,8 +137,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[30.161857460980094] Obs[-1.1752011936438014]]\n", - " [Obs[-1.1752011936438014] Obs[1.1752011936438014]]]\n" + "[[30.16185746098009 -1.1752011936438014]\n", + " [-1.1752011936438014 1.1752011936438014]]\n" ] } ], @@ -163,7 +162,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[Obs[2.00(40)] Obs[1.00(10)]]\n" + "[2.00(40) 1.00(10)]\n" ] } ], @@ -171,8 +170,7 @@ "vec1 = pe.pseudo_Obs(2, 0.4, 'e1')\n", "vec2 = pe.pseudo_Obs(1, 0.1, 'e1')\n", "vector = np.asarray([vec1, vec2])\n", - "for (i), entry in np.ndenumerate(vector):\n", - " entry.gamma_method()\n", + "pe.gm(vector)\n", "print(vector)" ] }, @@ -192,14 +190,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "[Obs[7.2(1.7)] Obs[-1.00(46)]]\n" + "[7.2(1.7) -1.00(45)]\n" ] } ], "source": [ "product = matrix @ vector\n", - "for (i), entry in np.ndenumerate(product):\n", - " entry.gamma_method()\n", + "pe.gm(product)\n", "print(product)" ] }, @@ -245,15 +242,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[2.025(49)] Obs[0.0]]\n", - " [Obs[-0.494(50)] Obs[0.870(29)]]]\n" + "[[2.025(49) 0.0]\n", + " [-0.494(50) 0.870(29)]]\n" ] } ], "source": [ "cholesky = pe.linalg.cholesky(matrix)\n", - "for (i, j), entry in np.ndenumerate(cholesky):\n", - " entry.gamma_method()\n", + "pe.gm(cholesky)\n", "print(cholesky)" ] }, @@ -273,8 +269,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[-8.881784197001252e-16] Obs[0.0]]\n", - " [Obs[0.0] Obs[0.0]]]\n" + "[[-8.881784197001252e-16 0.0]\n", + " [0.0 0.0]]\n" ] } ], @@ -299,18 +295,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Obs[0.494(12)] Obs[0.0]]\n", - " [Obs[0.280(40)] Obs[1.150(39)]]]\n", + "[[0.494(12) 0.0]\n", + " [0.280(40) 1.150(38)]]\n", "Check:\n", - "[[Obs[1.0] Obs[0.0]]\n", - " [Obs[0.0] Obs[1.0]]]\n" + "[[1.0 0.0]\n", + " [0.0 1.0]]\n" ] } ], "source": [ "inv = pe.linalg.inv(cholesky)\n", - "for (i, j), entry in np.ndenumerate(inv):\n", - " entry.gamma_method()\n", + "pe.gm(inv)\n", "print(inv)\n", "print('Check:')\n", "check_inv = cholesky @ inv\n", @@ -335,21 +330,19 @@ "output_type": "stream", "text": [ "Eigenvalues:\n", - "[Obs[0.705(57)] Obs[4.39(19)]]\n", + "[0.705(56) 4.39(20)]\n", "Eigenvectors:\n", - "[[Obs[-0.283(26)] Obs[-0.9592(75)]]\n", - " [Obs[-0.9592(75)] Obs[0.283(26)]]]\n" + "[[-0.283(26) -0.9592(75)]\n", + " [-0.9592(75) 0.283(26)]]\n" ] } ], "source": [ "e, v = pe.linalg.eigh(matrix)\n", - "for (i), entry in np.ndenumerate(e):\n", - " entry.gamma_method()\n", + "pe.gm(e)\n", "print('Eigenvalues:')\n", "print(e)\n", - "for (i, j), entry in np.ndenumerate(v):\n", - " entry.gamma_method()\n", + "pe.gm(v)\n", "print('Eigenvectors:')\n", "print(v)" ] @@ -371,9 +364,9 @@ "output_type": "stream", "text": [ "Check eigenvector 1\n", - "[Obs[-5.551115123125783e-17] Obs[0.0]]\n", + "[-5.551115123125783e-17 0.0]\n", "Check eigenvector 2\n", - "[Obs[0.0] Obs[-2.220446049250313e-16]]\n" + "[0.0 -2.220446049250313e-16]\n" ] } ], @@ -407,7 +400,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.11.0" } }, "nbformat": 4, diff --git a/pyerrors/obs.py b/pyerrors/obs.py index a25dceb8..31ed84af 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1032,6 +1032,22 @@ class CObs: return f"({self.real:{format_type}}{self.imag:+{significance}}j)" +def gamma_method(x, **kwargs): + """Vectorized version of the gamma_method applicable to lists or arrays of Obs. + + See docstring of pe.Obs.gamma_method for details. + """ + return np.vectorize(lambda o: o.gm(**kwargs))(x) + + +def gm(x, **kwargs): + """Short version of the vectorized gamma_method. + + See docstring of pe.Obs.gamma_method for details + """ + return gamma_method(x, **kwargs) + + def _format_uncertainty(value, dvalue, significance=2): """Creates a string of a value and its error in paranthesis notation, e.g., 13.02(45)""" if dvalue == 0.0 or (not np.isfinite(dvalue)): diff --git a/tests/obs_test.py b/tests/obs_test.py index 924d98cd..cc13c845 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1,4 +1,5 @@ -import autograd.numpy as np +import numpy as np +import autograd.numpy as anp import os import copy import matplotlib.pyplot as plt @@ -126,9 +127,9 @@ def test_function_overloading(): fs = [lambda x: x[0] + x[1], lambda x: x[1] + x[0], lambda x: x[0] - x[1], lambda x: x[1] - x[0], lambda x: x[0] * x[1], lambda x: x[1] * x[0], lambda x: x[0] / x[1], lambda x: x[1] / x[0], - lambda x: np.exp(x[0]), lambda x: np.sin(x[0]), lambda x: np.cos(x[0]), lambda x: np.tan(x[0]), - lambda x: np.log(x[0]), lambda x: np.sqrt(np.abs(x[0])), - lambda x: np.sinh(x[0]), lambda x: np.cosh(x[0]), lambda x: np.tanh(x[0])] + lambda x: anp.exp(x[0]), lambda x: anp.sin(x[0]), lambda x: anp.cos(x[0]), lambda x: anp.tan(x[0]), + lambda x: anp.log(x[0]), lambda x: anp.sqrt(anp.abs(x[0])), + lambda x: anp.sinh(x[0]), lambda x: anp.cosh(x[0]), lambda x: anp.tanh(x[0])] for i, f in enumerate(fs): t1 = f([a, b]) @@ -306,9 +307,9 @@ def test_derived_observables(): test_obs = pe.pseudo_Obs(2, 0.1 * (1 + np.random.rand()), 't', int(1000 * (1 + np.random.rand()))) # Check if autograd and numgrad give the same result - d_Obs_ad = pe.derived_observable(lambda x, **kwargs: x[0] * x[1] * np.sin(x[0] * x[1]), [test_obs, test_obs]) + d_Obs_ad = pe.derived_observable(lambda x, **kwargs: x[0] * x[1] * anp.sin(x[0] * x[1]), [test_obs, test_obs]) d_Obs_ad.gamma_method() - d_Obs_fd = pe.derived_observable(lambda x, **kwargs: x[0] * x[1] * np.sin(x[0] * x[1]), [test_obs, test_obs], num_grad=True) + d_Obs_fd = pe.derived_observable(lambda x, **kwargs: x[0] * x[1] * anp.sin(x[0] * x[1]), [test_obs, test_obs], num_grad=True) d_Obs_fd.gamma_method() assert d_Obs_ad == d_Obs_fd @@ -1283,12 +1284,14 @@ def test_format_uncertainty(): pe.obs._format_uncertainty(1, np.NaN) pe.obs._format_uncertainty(np.NaN, np.inf) + def test_format(): o1 = pe.pseudo_Obs(0.348, 0.0123, "test") assert o1.__format__("+3") == '+0.3480(123)' assert o1.__format__("+2") == '+0.348(12)' assert o1.__format__(" 2") == ' 0.348(12)' + def test_f_string_obs(): o1 = pe.pseudo_Obs(0.348, 0.0123, "test") print(f"{o1}") @@ -1297,6 +1300,7 @@ def test_f_string_obs(): print(f"{o1:-1}") print(f"{o1: 8}") + def test_f_string_cobs(): o_real = pe.pseudo_Obs(0.348, 0.0123, "test") o_imag = pe.pseudo_Obs(0.348, 0.0123, "test") @@ -1307,7 +1311,24 @@ def test_f_string_cobs(): print(f"{o1:-1}") print(f"{o1: 8}") + def test_compute_drho_fails(): obs = pe.input.json.load_json("tests/data/compute_drho_fails.json.gz") obs.gm() assert np.isclose(obs.dvalue, 0.0022150779611891094) + + +def test_vec_gm(): + obs = pe.misc.gen_correlated_data(np.arange(3), np.array([[0.0364 , 0.03627262, 0.03615699], + [0.03627262, 0.03688438, 0.03674798], + [0.03615699, 0.03674798, 0.03732882]]), "qq", 3.8, 1000) + pe.gm(obs[0], S=0) + assert obs[0].S["qq"] == 0 + pe.gm(obs, S=1.3) + assert np.all(np.vectorize(lambda x: x.S["qq"])(obs) == 1.3) + aa = np.array([obs, obs, obs]) + pe.gamma_method(aa, S=2.2) + assert np.all(np.vectorize(lambda x: x.S["qq"])(aa) == 2.20) + cc = pe.Corr(obs) + pe.gm(cc, S=4.12) + assert np.all(np.vectorize(lambda x: x.S["qq"])(cc.content) == 4.12) From af28f77ec56f359bb85bcce9870c8eb14b79853b Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 19 Jul 2023 15:06:19 +0100 Subject: [PATCH 045/107] __eq__ method for Corr class (#206) * feat: implemented __eq__ method for Corr class. * feat: __eq__ method now respects None entries in correlators. * feat: Obs can now be compared to None, __ne__ method removed as it is not required. * feat: Corr.__eq__ rewritten to give a per element comparison. * tests: additional test case for correlator comparison added. * feat: comparison now also works for padding. --- pyerrors/correlators.py | 7 +++++++ pyerrors/obs.py | 5 ++--- tests/correlators_test.py | 36 ++++++++++++++++++++++++++++++++++++ tests/obs_test.py | 1 + 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 002d2c87..be5c69a6 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -1056,6 +1056,13 @@ class Corr: __array_priority__ = 10000 + def __eq__(self, y): + if isinstance(y, Corr): + comp = np.asarray(y.content, dtype=object) + else: + comp = np.asarray(y) + return np.asarray(self.content, dtype=object) == comp + def __add__(self, y): if isinstance(y, Corr): if ((self.N != y.N) or (self.T != y.T)): diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 31ed84af..0409a5d4 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -773,11 +773,10 @@ class Obs: return self.value >= other def __eq__(self, other): + if other is None: + return False return (self - other).is_zero() - def __ne__(self, other): - return not (self - other).is_zero() - # Overload math operations def __add__(self, y): if isinstance(y, Obs): diff --git a/tests/correlators_test.py b/tests/correlators_test.py index 5b1c5e62..3d49164a 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -713,3 +713,39 @@ def test_corr_roll(): tt = mcorr.roll(T) - mcorr for el in tt: assert np.all(el == 0) + + +def test_correlator_comparison(): + scorr = pe.Corr([pe.pseudo_Obs(0.3, 0.1, "test") for o in range(4)]) + mcorr = pe.Corr(np.array([[scorr, scorr], [scorr, scorr]])) + for corr in [scorr, mcorr]: + assert (corr == corr).all() + assert np.all(corr == 1 * corr) + assert np.all(corr == (1 + 1e-16) * corr) + assert not np.all(corr == (1 + 1e-5) * corr) + assert np.all(corr == 1 / (1 / corr)) + assert np.all(corr - corr == 0) + assert np.all(corr * 0 == 0) + assert np.all(0 * corr == 0) + assert np.all(0 * corr + scorr[2] == scorr[2]) + assert np.all(-corr == 0 - corr) + assert np.all(corr ** 2 == corr * corr) + acorr = pe.Corr([scorr[0]] * 6) + assert np.all(acorr == scorr[0]) + assert not np.all(acorr == scorr[1]) + + mcorr[1][0, 1] = None + assert not np.all(mcorr == pe.Corr(np.array([[scorr, scorr], [scorr, scorr]]))) + + pcorr = pe.Corr([pe.pseudo_Obs(0.25, 0.1, "test") for o in range(2)], padding=[1, 1]) + assert np.all(pcorr == pcorr) + assert np.all(1 * pcorr == pcorr) + + +def test_corr_item(): + corr_aa = _gen_corr(1) + corr_ab = 0.5 * corr_aa + + corr_mat = pe.Corr(np.array([[corr_aa, corr_ab], [corr_ab, corr_aa]])) + corr_mat.item(0, 0) + assert corr_mat[0].item(0, 1) == corr_mat.item(0, 1)[0] diff --git a/tests/obs_test.py b/tests/obs_test.py index cc13c845..334521f0 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -103,6 +103,7 @@ def test_comparison(): test_obs1 = pe.pseudo_Obs(value1, 0.1, 't') value2 = np.random.normal(0, 100) test_obs2 = pe.pseudo_Obs(value2, 0.1, 't') + assert test_obs1 != None assert (value1 > value2) == (test_obs1 > test_obs2) assert (value1 < value2) == (test_obs1 < test_obs2) assert (value1 >= value2) == (test_obs1 >= test_obs2) From 087a3f92eb0dc51e7d95a03d2dc16098ea033efe Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 20 Jul 2023 11:31:20 +0100 Subject: [PATCH 046/107] feat: short version of vectorized gamma_method simplified. --- pyerrors/obs.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 0409a5d4..e9dc20cb 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1039,12 +1039,7 @@ def gamma_method(x, **kwargs): return np.vectorize(lambda o: o.gm(**kwargs))(x) -def gm(x, **kwargs): - """Short version of the vectorized gamma_method. - - See docstring of pe.Obs.gamma_method for details - """ - return gamma_method(x, **kwargs) +gm = gamma_method def _format_uncertainty(value, dvalue, significance=2): From 07ad89ec6e6032d4210d0c8b15ce42a562b6384a Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 20 Jul 2023 11:41:10 +0100 Subject: [PATCH 047/107] docs: CHANGELOG updated. --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4242799b..006888a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to this project will be documented in this file. +## [2.9.0] - 2023-07-20 +### Added +- Vectorized `gamma_method` added which can be applied to lists or arrays of pyerrors objects. +- Wrapper for numerical integration of `Obs` valued functions with `Obs` valued intervals. +- Bootstrap import and export added. +- `matmul` overloaded for `Corr` class. +- More options for initializing `Corr` objects added. +- General Hadrons hdf5 reader added. +- New variant of second_derivative. +- CObs formatting added. +- Comparisons for `Corr` class added. + +### Changed +- support for python<3.8 was dropped and dependencies were updated. + + ## [2.8.2] - 2023-06-02 ### Fixed - Another bug appearing in an edge case of `_compute_drho` fixed. From d768d355ac1d4e46cb2c86c7fc1b432cc01afb81 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 20 Jul 2023 11:41:50 +0100 Subject: [PATCH 048/107] build: version bumped to 2.9.0 --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 70ba39fa..43ce13db 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.9.0-dev" +__version__ = "2.9.0" From 7f5373d5e433636303b3cf8a7d112b22ea754254 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 20 Jul 2023 11:54:43 +0100 Subject: [PATCH 049/107] build: version bumped to 2.10.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 43ce13db..2e796a5b 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.9.0" +__version__ = "2.10.0-dev" From 01ef97ffdf3a63288ec0c43185d494fd578ef1e5 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 20 Jul 2023 12:20:06 +0100 Subject: [PATCH 050/107] docs: example 5 validation updated. --- examples/05_matrix_operations.ipynb | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/examples/05_matrix_operations.ipynb b/examples/05_matrix_operations.ipynb index 1f08951b..0cd85534 100644 --- a/examples/05_matrix_operations.ipynb +++ b/examples/05_matrix_operations.ipynb @@ -190,7 +190,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[7.2(1.7) -1.00(45)]\n" + "[7.2(1.7) -1.00(46)]\n" ] } ], @@ -243,7 +243,7 @@ "output_type": "stream", "text": [ "[[2.025(49) 0.0]\n", - " [-0.494(50) 0.870(29)]]\n" + " [-0.494(51) 0.870(29)]]\n" ] } ], @@ -296,7 +296,7 @@ "output_type": "stream", "text": [ "[[0.494(12) 0.0]\n", - " [0.280(40) 1.150(38)]]\n", + " [0.280(40) 1.150(39)]]\n", "Check:\n", "[[1.0 0.0]\n", " [0.0 1.0]]\n" @@ -330,10 +330,10 @@ "output_type": "stream", "text": [ "Eigenvalues:\n", - "[0.705(56) 4.39(20)]\n", + "[0.705(57) 4.39(19)]\n", "Eigenvectors:\n", - "[[-0.283(26) -0.9592(75)]\n", - " [-0.9592(75) 0.283(26)]]\n" + "[[-0.283(26) -0.9592(76)]\n", + " [-0.9592(76) 0.283(26)]]\n" ] } ], @@ -363,17 +363,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Check eigenvector 1\n", - "[-5.551115123125783e-17 0.0]\n", - "Check eigenvector 2\n", - "[0.0 -2.220446049250313e-16]\n" + "[[ True True]\n", + " [ True True]]\n" ] } ], "source": [ - "for i in range(2):\n", - " print('Check eigenvector', i + 1)\n", - " print(matrix @ v[:, i] - v[:, i] * e[i])" + "print(matrix @ v == e * v)" ] }, { From 7f8c2ce33b7bc16899f048216639afb10ab96dfb Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 21 Jul 2023 14:15:41 +0100 Subject: [PATCH 051/107] feat: added support for addition and multiplication of complex numbers (#209) to Corr objects. --- pyerrors/correlators.py | 4 ++-- pyerrors/obs.py | 2 ++ tests/correlators_test.py | 10 ++++++++++ tests/obs_test.py | 7 +++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index be5c69a6..5c9e236a 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -1075,7 +1075,7 @@ class Corr: newcontent.append(self.content[t] + y.content[t]) return Corr(newcontent) - elif isinstance(y, (Obs, int, float, CObs)): + elif isinstance(y, (Obs, int, float, CObs, complex)): newcontent = [] for t in range(self.T): if _check_for_none(self, self.content[t]): @@ -1103,7 +1103,7 @@ class Corr: newcontent.append(self.content[t] * y.content[t]) return Corr(newcontent) - elif isinstance(y, (Obs, int, float, CObs)): + elif isinstance(y, (Obs, int, float, CObs, complex)): newcontent = [] for t in range(self.T): if _check_for_none(self, self.content[t]): diff --git a/pyerrors/obs.py b/pyerrors/obs.py index e9dc20cb..df287a10 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -784,6 +784,8 @@ class Obs: else: if isinstance(y, np.ndarray): return np.array([self + o for o in y]) + elif isinstance(y, complex): + return CObs(self, 0) + y elif y.__class__.__name__ in ['Corr', 'CObs']: return NotImplemented else: diff --git a/tests/correlators_test.py b/tests/correlators_test.py index 3d49164a..5f213034 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -749,3 +749,13 @@ def test_corr_item(): corr_mat = pe.Corr(np.array([[corr_aa, corr_ab], [corr_ab, corr_aa]])) corr_mat.item(0, 0) assert corr_mat[0].item(0, 1) == corr_mat.item(0, 1)[0] + + +def test_complex_add_and_mul(): + o = pe.pseudo_Obs(1.0, 0.3, "my_r345sfg16£$%&$%^%$^$", samples=47) + co = pe.CObs(o, 0.341 * o) + for obs in [o, co]: + cc = pe.Corr([obs for _ in range(4)]) + cc += 2j + cc = cc * 4j + cc.real + cc.imag diff --git a/tests/obs_test.py b/tests/obs_test.py index 334521f0..02d539ef 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1333,3 +1333,10 @@ def test_vec_gm(): cc = pe.Corr(obs) pe.gm(cc, S=4.12) assert np.all(np.vectorize(lambda x: x.S["qq"])(cc.content) == 4.12) + +def test_complex_addition(): + o = pe.pseudo_Obs(34.12, 1e-4, "testens") + r = o + 2j + assert r.real == o + r = r * 1j + assert r.imag == o From 2017de0ec70d038cdc262cd89e6a82de70126cb2 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 15 Sep 2023 09:02:08 +0200 Subject: [PATCH 052/107] ci: workflow_dispatch added to pytest and examples workflow. --- .github/workflows/examples.yml | 1 + .github/workflows/pytest.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 473e77d0..cd4f73c9 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -6,6 +6,7 @@ on: - master - develop pull_request: + workflow_dispatch: schedule: - cron: '0 4 1 * *' diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index d4abcfc3..b6a5290f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -6,6 +6,7 @@ on: - master - develop pull_request: + workflow_dispatch: schedule: - cron: '0 4 1 * *' From 957030cba042b9b0f6973c3fb22523423d92e621 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 17 Sep 2023 18:11:26 +0200 Subject: [PATCH 053/107] fix: explicit type check replaced by isinstance in fits.least_squares. --- pyerrors/fits.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 5ec857e0..6518b224 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -168,12 +168,12 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): ''' output = Fit_result() - if (type(x) == dict and type(y) == dict and type(func) == dict): + if (isinstance(x, dict) and isinstance(y, dict) and isinstance(func, dict)): xd = {key: anp.asarray(x[key]) for key in x} yd = y funcd = func output.fit_function = func - elif (type(x) == dict or type(y) == dict or type(func) == dict): + elif (isinstance(x, dict) or isinstance(y, dict) or isinstance(func, dict)): raise TypeError("All arguments have to be dictionaries in order to perform a combined fit.") else: x = np.asarray(x) From 0ef8649031c0890e03b44e25a0e4f71775db1071 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:22:55 +0200 Subject: [PATCH 054/107] Feat/read sfcf multi (#210) * make template * read_sfcf_multi running with compact format * fix append mode, norrmal tests work * improve readability * add simple test for multi_read * simple multi_test works * add first method to check sfcf param hashes * add docstring * simple test for o format working * use benedict to make loops easier * introduce python-benedict as dep * no nice_out, less error prone, found bug in tests * Revert "introduce python-benedict as dep" This reverts commit 9696d68b7a2360a6be5a768a278ec4ed74b5dbb8. * Revert "use benedict to make loops easier" This reverts commit fa3987479bd730d6dd145d048578f5a1f0e9d063. * no nice output after reverts * [build] Added jkuhl-uni as CODEOWNER for sfcf. * refactor: flatten internal dicts * very small test extension * ...flake8 * docu * Delete second sep init --------- Co-authored-by: Fabian Joswig --- .github/CODEOWNERS | 2 + pyerrors/input/sfcf.py | 390 ++++++++++++++++++++++++++++++---------- pyerrors/input/utils.py | 69 ++++++- tests/sfcf_in_test.py | 283 ++++++++++++++++++++++++----- 4 files changed, 606 insertions(+), 138 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c0649da6..5e6baf48 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,3 +2,5 @@ /pyerrors/covobs.py @s-kuberski /pyerrors/input/json.py @s-kuberski /pyerrors/input/dobs.py @s-kuberski +/pyerrors/input/sfcf.py @jkuhl-uni +/tests/sfcf_in_test.py @jkuhl-uni diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index e6cb0b49..82957552 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -4,9 +4,13 @@ import re import numpy as np # Thinly-wrapped numpy from ..obs import Obs from .utils import sort_names, check_idl +import itertools -def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, wf2=0, version="1.0c", cfg_separator="n", silent=False, **kwargs): +sep = "/" + + +def read_sfcf(path, prefix, name, quarks='.*', corr_type="bi", noffset=0, wf=0, wf2=0, version="1.0c", cfg_separator="n", silent=False, **kwargs): """Read sfcf files from given folder structure. Parameters @@ -65,6 +69,76 @@ def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, list of Observables with length T, observable per timeslice. bb-type correlators have length 1. """ + ret = read_sfcf_multi(path, prefix, [name], quarks_list=[quarks], corr_type_list=[corr_type], + noffset_list=[noffset], wf_list=[wf], wf2_list=[wf2], version=version, + cfg_separator=cfg_separator, silent=silent, **kwargs) + return ret[name][quarks][str(noffset)][str(wf)][str(wf2)] + + +def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list=['bi'], noffset_list=[0], wf_list=[0], wf2_list=[0], version="1.0c", cfg_separator="n", silent=False, keyed_out=False, **kwargs): + """Read sfcf files from given folder structure. + + Parameters + ---------- + path : str + Path to the sfcf files. + prefix : str + Prefix of the sfcf files. + name : str + Name of the correlation function to read. + quarks_list : list[str] + Label of the quarks used in the sfcf input file. e.g. "quark quark" + for version 0.0 this does NOT need to be given with the typical " - " + that is present in the output file, + this is done automatically for this version + corr_type_list : list[str] + Type of correlation function to read. Can be + - 'bi' for boundary-inner + - 'bb' for boundary-boundary + - 'bib' for boundary-inner-boundary + noffset_list : list[int] + Offset of the source (only relevant when wavefunctions are used) + wf_list : int + ID of wave function + wf2_list : list[int] + ID of the second wavefunction + (only relevant for boundary-to-boundary correlation functions) + im : bool + if True, read imaginary instead of real part + of the correlation function. + names : list + Alternative labeling for replicas/ensembles. + Has to have the appropriate length + ens_name : str + replaces the name of the ensemble + version: str + version of SFCF, with which the measurement was done. + if the compact output option (-c) was specified, + append a "c" to the version (e.g. "1.0c") + if the append output option (-a) was specified, + append an "a" to the version + cfg_separator : str + String that separates the ensemble identifier from the configuration number (default 'n'). + replica: list + list of replica to be read, default is all + files: list + list of files to be read per replica, default is all. + for non-compact output format, hand the folders to be read here. + check_configs: list[list[int]] + list of list of supposed configs, eg. [range(1,1000)] + for one replicum with 1000 configs + + Returns + ------- + result: dict[list[Obs]] + dict with one of the following properties: + if keyed_out: + dict[key] = list[Obs] + where key has the form name/quarks/offset/wf/wf2 + if not keyed_out: + dict[name][quarks][offset][wf][wf2] = list[Obs] + """ + if kwargs.get('im'): im = 1 part = 'imaginary' @@ -72,16 +146,6 @@ def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, im = 0 part = 'real' - if corr_type == 'bb': - b2b = True - single = True - elif corr_type == 'bib': - b2b = True - single = False - else: - b2b = False - single = False - known_versions = ["0.0", "1.0", "2.0", "1.0c", "2.0c", "1.0a", "2.0a"] if version not in known_versions: @@ -121,7 +185,7 @@ def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, else: replica = len([file.split(".")[-1] for file in ls]) // len(set([file.split(".")[-1] for file in ls])) if not silent: - print('Read', part, 'part of', name, 'from', prefix[:-1], ',', replica, 'replica') + print('Read', part, 'part of', name_list, 'from', prefix[:-1], ',', replica, 'replica') if 'names' in kwargs: new_names = kwargs.get('names') @@ -135,10 +199,38 @@ def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, if not appended: new_names = _get_rep_names(ls, ens_name) else: - new_names = _get_appended_rep_names(ls, prefix, name, ens_name) + new_names = _get_appended_rep_names(ls, prefix, name_list[0], ens_name) new_names = sort_names(new_names) idl = [] + + noffset_list = [str(x) for x in noffset_list] + wf_list = [str(x) for x in wf_list] + wf2_list = [str(x) for x in wf2_list] + + # setup dict structures + intern = {} + for name, corr_type in zip(name_list, corr_type_list): + intern[name] = {} + b2b, single = _extract_corr_type(corr_type) + intern[name]["b2b"] = b2b + intern[name]["single"] = single + intern[name]["spec"] = {} + for quarks in quarks_list: + intern[name]["spec"][quarks] = {} + for off in noffset_list: + intern[name]["spec"][quarks][off] = {} + for w in wf_list: + intern[name]["spec"][quarks][off][w] = {} + for w2 in wf2_list: + intern[name]["spec"][quarks][off][w][w2] = {} + intern[name]["spec"][quarks][off][w][w2]["pattern"] = _make_pattern(version, name, off, w, w2, intern[name]['b2b'], quarks) + + internal_ret_dict = {} + needed_keys = _lists2key(name_list, quarks_list, noffset_list, wf_list, wf2_list) + for key in needed_keys: + internal_ret_dict[key] = [] + if not appended: for i, item in enumerate(ls): rep_path = path + '/' + item @@ -164,67 +256,83 @@ def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, idl.append(rep_idl) # here we have found all the files we need to look into. if i == 0: - # here, we want to find the place within the file, - # where the correlator we need is stored. - # to do so, the pattern needed is put together - # from the input values - if version == "0.0": - file = path + '/' + item + '/' + sub_ls[0] + '/' + name - else: - if compact: - file = path + '/' + item + '/' + sub_ls[0] - else: + if version != "0.0" and compact: + file = path + '/' + item + '/' + sub_ls[0] + for name in name_list: + if version == "0.0" or not compact: file = path + '/' + item + '/' + sub_ls[0] + '/' + name - - pattern = _make_pattern(version, name, noffset, wf, wf2, b2b, quarks) - start_read, T = _find_correlator(file, version, pattern, b2b, silent=silent) - - # preparing the datastructure - # the correlators get parsed into... - deltas = [] - for j in range(T): - deltas.append([]) + for key in _lists2key(quarks_list, noffset_list, wf_list, wf2_list): + specs = _key2specs(key) + quarks = specs[0] + off = specs[1] + w = specs[2] + w2 = specs[3] + # here, we want to find the place within the file, + # where the correlator we need is stored. + # to do so, the pattern needed is put together + # from the input values + start_read, T = _find_correlator(file, version, intern[name]["spec"][quarks][str(off)][str(w)][str(w2)]["pattern"], intern[name]['b2b'], silent=silent) + intern[name]["spec"][quarks][str(off)][str(w)][str(w2)]["start"] = start_read + intern[name]["T"] = T + # preparing the datastructure + # the correlators get parsed into... + deltas = [] + for j in range(intern[name]["T"]): + deltas.append([]) + internal_ret_dict[sep.join([name, key])] = deltas if compact: - rep_deltas = _read_compact_rep(path, item, sub_ls, start_read, T, b2b, name, im) - - for t in range(T): - deltas[t].append(rep_deltas[t]) + rep_deltas = _read_compact_rep(path, item, sub_ls, intern, needed_keys, im) + for key in needed_keys: + name = _key2specs(key)[0] + for t in range(intern[name]["T"]): + internal_ret_dict[key][t].append(rep_deltas[key][t]) else: - for t in range(T): - deltas[t].append(np.zeros(no_cfg)) - for cnfg, subitem in enumerate(sub_ls): - with open(path + '/' + item + '/' + subitem + '/' + name) as fp: - for k, line in enumerate(fp): - if (k >= start_read and k < start_read + T): - floats = list(map(float, line.split())) - if version == "0.0": - deltas[k - start_read][i][cnfg] = floats[im - single] - else: - deltas[k - start_read][i][cnfg] = floats[1 + im - single] - + for key in needed_keys: + rep_data = [] + name = _key2specs(key)[0] + for subitem in sub_ls: + cfg_path = path + '/' + item + '/' + subitem + file_data = _read_o_file(cfg_path, name, needed_keys, intern, version, im) + rep_data.append(file_data) + print(rep_data) + for t in range(intern[name]["T"]): + internal_ret_dict[key][t].append([]) + for cfg in range(no_cfg): + internal_ret_dict[key][t][i].append(rep_data[cfg][key][t]) else: - if "files" in kwargs: - ls = kwargs.get("files") - else: - for exc in ls: - if not fnmatch.fnmatch(exc, prefix + '*.' + name): - ls = list(set(ls) - set([exc])) - ls = sort_names(ls) - pattern = _make_pattern(version, name, noffset, wf, wf2, b2b, quarks) - deltas = [] - for rep, file in enumerate(ls): - rep_idl = [] - filename = path + '/' + file - T, rep_idl, rep_data = _read_append_rep(filename, pattern, b2b, cfg_separator, im, single) - if rep == 0: - for t in range(T): - deltas.append([]) - for t in range(T): - deltas[t].append(rep_data[t]) - idl.append(rep_idl) + for key in needed_keys: + specs = _key2specs(key) + name = specs[0] + quarks = specs[1] + off = specs[2] + w = specs[3] + w2 = specs[4] + if "files" in kwargs: + ls = kwargs.get("files") + else: + name_ls = ls + for exc in name_ls: + if not fnmatch.fnmatch(exc, prefix + '*.' + name): + name_ls = list(set(name_ls) - set([exc])) + name_ls = sort_names(name_ls) + pattern = intern[name]['spec'][quarks][off][w][w2]['pattern'] + deltas = [] + for rep, file in enumerate(name_ls): + rep_idl = [] + filename = path + '/' + file + T, rep_idl, rep_data = _read_append_rep(filename, pattern, intern[name]['b2b'], cfg_separator, im, intern[name]['single']) + if rep == 0: + intern[name]['T'] = T + for t in range(intern[name]['T']): + deltas.append([]) + for t in range(intern[name]['T']): + deltas[t].append(rep_data[t]) + internal_ret_dict[key] = deltas + if name == name_list[0]: + idl.append(rep_idl) - if "check_configs" in kwargs: + if kwargs.get("check_configs") is True: if not silent: print("Checking for missing configs...") che = kwargs.get("check_configs") @@ -236,10 +344,83 @@ def read_sfcf(path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, check_idl(idl[r], che[r]) if not silent: print("Done") - result = [] - for t in range(T): - result.append(Obs(deltas[t], new_names, idl=idl)) - return result + + result_dict = {} + if keyed_out: + for key in needed_keys: + result = [] + for t in range(intern[name]["T"]): + result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) + result_dict[key] = result + else: + for name in name_list: + result_dict[name] = {} + for quarks in quarks_list: + result_dict[name][quarks] = {} + for off in noffset_list: + result_dict[name][quarks][off] = {} + for w in wf_list: + result_dict[name][quarks][off][w] = {} + for w2 in wf2_list: + key = _specs2key(name, quarks, off, w, w2) + result = [] + for t in range(intern[name]["T"]): + result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) + result_dict[name][quarks][str(off)][str(w)][str(w2)] = result + return result_dict + + +def _lists2key(*lists): + keys = [] + for tup in itertools.product(*lists): + keys.append(sep.join(tup)) + return keys + + +def _key2specs(key): + return key.split(sep) + + +def _specs2key(*specs): + return sep.join(specs) + + +def _read_o_file(cfg_path, name, needed_keys, intern, version, im): + return_vals = {} + for key in needed_keys: + file = cfg_path + '/' + name + specs = _key2specs(key) + if specs[0] == name: + with open(file) as fp: + lines = fp.readlines() + quarks = specs[1] + off = specs[2] + w = specs[3] + w2 = specs[4] + T = intern[name]["T"] + start_read = intern[name]["spec"][quarks][off][w][w2]["start"] + deltas = [] + for line in lines[start_read:start_read + T]: + floats = list(map(float, line.split())) + if version == "0.0": + deltas.append(floats[im - intern[name]["single"]]) + else: + deltas.append(floats[1 + im - intern[name]["single"]]) + return_vals[key] = deltas + return return_vals + + +def _extract_corr_type(corr_type): + if corr_type == 'bb': + b2b = True + single = True + elif corr_type == 'bib': + b2b = True + single = False + else: + b2b = False + single = False + return b2b, single def _find_files(rep_path, prefix, compact, files=[]): @@ -309,38 +490,57 @@ def _find_correlator(file_name, version, pattern, b2b, silent=False): return start_read, T -def _read_compact_file(rep_path, config_file, start_read, T, b2b, name, im): - with open(rep_path + config_file) as fp: +def _read_compact_file(rep_path, cfg_file, intern, needed_keys, im): + return_vals = {} + with open(rep_path + cfg_file) as fp: lines = fp.readlines() - # check, if the correlator is in fact - # printed completely - if (start_read + T + 1 > len(lines)): - raise Exception("EOF before end of correlator data! Maybe " + rep_path + config_file + " is corrupted?") - corr_lines = lines[start_read - 6: start_read + T] - del lines - t_vals = [] + for key in needed_keys: + keys = _key2specs(key) + name = keys[0] + quarks = keys[1] + off = keys[2] + w = keys[3] + w2 = keys[4] - if corr_lines[1 - b2b].strip() != 'name ' + name: - raise Exception('Wrong format in file', config_file) + T = intern[name]["T"] + start_read = intern[name]["spec"][quarks][off][w][w2]["start"] + # check, if the correlator is in fact + # printed completely + if (start_read + T + 1 > len(lines)): + raise Exception("EOF before end of correlator data! Maybe " + rep_path + cfg_file + " is corrupted?") + corr_lines = lines[start_read - 6: start_read + T] + t_vals = [] - for k in range(6, T + 6): - floats = list(map(float, corr_lines[k].split())) - t_vals.append(floats[-2:][im]) - return t_vals + if corr_lines[1 - intern[name]["b2b"]].strip() != 'name ' + name: + raise Exception('Wrong format in file', cfg_file) + + for k in range(6, T + 6): + floats = list(map(float, corr_lines[k].split())) + t_vals.append(floats[-2:][im]) + return_vals[key] = t_vals + return return_vals -def _read_compact_rep(path, rep, sub_ls, start_read, T, b2b, name, im): +def _read_compact_rep(path, rep, sub_ls, intern, needed_keys, im): rep_path = path + '/' + rep + '/' no_cfg = len(sub_ls) - deltas = [] - for t in range(T): - deltas.append(np.zeros(no_cfg)) + + return_vals = {} + for key in needed_keys: + name = _key2specs(key)[0] + deltas = [] + for t in range(intern[name]["T"]): + deltas.append(np.zeros(no_cfg)) + return_vals[key] = deltas + for cfg in range(no_cfg): cfg_file = sub_ls[cfg] - cfg_data = _read_compact_file(rep_path, cfg_file, start_read, T, b2b, name, im) - for t in range(T): - deltas[t][cfg] = cfg_data[t] - return deltas + cfg_data = _read_compact_file(rep_path, cfg_file, intern, needed_keys, im) + for key in needed_keys: + name = _key2specs(key)[0] + for t in range(intern[name]["T"]): + return_vals[key][t][cfg] = cfg_data[key][t] + return return_vals def _read_chunk(chunk, gauge_line, cfg_sep, start_read, T, corr_line, b2b, pattern, im, single): diff --git a/pyerrors/input/utils.py b/pyerrors/input/utils.py index f9eedd68..eaf41f06 100644 --- a/pyerrors/input/utils.py +++ b/pyerrors/input/utils.py @@ -1,6 +1,9 @@ -import re """Utilities for the input""" +import re +import fnmatch +import os + def sort_names(ll): """Sorts a list of names of replika with searches for `r` and `id` in the replikum string. @@ -17,6 +20,7 @@ def sort_names(ll): ll: list sorted list """ + if len(ll) > 1: sorted = False r_pattern = r'r(\d+)' @@ -63,6 +67,7 @@ def check_idl(idl, che): miss_str : str string with integers of which idls are missing """ + missing = [] for c in che: if c not in idl: @@ -75,3 +80,65 @@ def check_idl(idl, che): miss_str += "," + str(i) print(miss_str) return miss_str + + +def check_params(path, param_hash, prefix, param_prefix="parameters_"): + """ + Check if, for sfcf, the parameter hashes at the end of the parameter files are in fact the expected one. + + Parameters + ---------- + path: str + measurement path, same as for sfcf read method + param_hash: str + expected parameter hash + prefix: str + data prefix to find the appropriate replicum folders in path + param_prefix: str + prefix of the parameter file. Defaults to 'parameters_' + + Returns + ------- + nums: dict + dictionary of faulty parameter files sorted by the replica paths + """ + + ls = [] + for (dirpath, dirnames, filenames) in os.walk(path): + ls.extend(dirnames) + break + if not ls: + raise Exception('Error, directory not found') + # Exclude folders with different names + for exc in ls: + if not fnmatch.fnmatch(exc, prefix + '*'): + ls = list(set(ls) - set([exc])) + + ls = sort_names(ls) + nums = {} + for rep in ls: + rep_path = path + '/' + rep + # files of replicum + sub_ls = [] + for (dirpath, dirnames, filenames) in os.walk(rep_path): + sub_ls.extend(filenames) + + # filter + param_files = [] + for file in sub_ls: + if fnmatch.fnmatch(file, param_prefix + '*'): + param_files.append(file) + + rep_nums = '' + for file in param_files: + with open(rep_path + '/' + file) as fp: + for line in fp: + pass + last_line = line + if last_line.split()[2] != param_hash: + rep_nums += file.split("_")[1] + ',' + nums[rep_path] = rep_nums + + if not len(rep_nums) == 0: + raise Warning("found differing parameter hash in the param files in " + rep_path) + return nums diff --git a/tests/sfcf_in_test.py b/tests/sfcf_in_test.py index 35fb3509..ac05dc04 100644 --- a/tests/sfcf_in_test.py +++ b/tests/sfcf_in_test.py @@ -1,7 +1,6 @@ import os import sys import inspect -import pyerrors as pe import pyerrors.input.sfcf as sfin import shutil import pytest @@ -14,107 +13,299 @@ sys.path.insert(0, parent_dir) def build_test_environment(path, env_type, cfgs, reps): shutil.copytree("tests/data/sfcf_test/data_"+env_type, (path + "/data_" + env_type)) if env_type == "o": - for i in range(2,cfgs+1): + for i in range(2, cfgs+1): shutil.copytree(path + "/data_o/test_r0/cfg1", path + "/data_o/test_r0/cfg"+str(i)) - for i in range(1,reps): + for i in range(1, reps): shutil.copytree(path + "/data_o/test_r0", path + "/data_o/test_r"+str(i)) elif env_type == "c": - for i in range(2,cfgs+1): + for i in range(2, cfgs+1): shutil.copy(path + "/data_c/data_c_r0/data_c_r0_n1", path + "/data_c/data_c_r0/data_c_r0_n"+str(i)) - for i in range(1,reps): + for i in range(1, reps): os.mkdir(path + "/data_c/data_c_r"+str(i)) - for j in range(1,cfgs+1): - shutil.copy(path + "/data_c/data_c_r0/data_c_r0_n1",path + "/data_c/data_c_r"+str(i)+"/data_c_r"+str(i)+"_n"+str(j)) + for j in range(1, cfgs+1): + shutil.copy(path + "/data_c/data_c_r0/data_c_r0_n1", path + "/data_c/data_c_r"+str(i)+"/data_c_r"+str(i)+"_n"+str(j)) elif env_type == "a": - for i in range(1,reps): + for i in range(1, reps): for corr in ["f_1", "f_A", "F_V0"]: shutil.copy(path + "/data_a/data_a_r0." + corr, path + "/data_a/data_a_r" + str(i) + "." + corr) def test_o_bb(tmp_path): - build_test_environment(str(tmp_path), "o",5,3) - f_1 = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "f_1",quarks="lquark lquark", wf = 0, wf2=0, version = "2.0", corr_type="bb") + build_test_environment(str(tmp_path), "o", 5, 3) + f_1 = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "f_1", quarks="lquark lquark", wf=0, wf2=0, version="2.0", corr_type="bb") print(f_1) assert len(f_1) == 1 - assert list(f_1[0].shape.keys()) == ["test_|r0","test_|r1","test_|r2"] + assert list(f_1[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] assert f_1[0].value == 351.1941525454502 + def test_o_bi(tmp_path): - build_test_environment(str(tmp_path), "o",5,3) - f_A = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "f_A",quarks="lquark lquark", wf = 0, version = "2.0") + build_test_environment(str(tmp_path), "o", 5, 3) + f_A = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "f_A", quarks="lquark lquark", wf=0, version="2.0") print(f_A) assert len(f_A) == 3 - assert list(f_A[0].shape.keys()) == ["test_|r0","test_|r1","test_|r2"] + assert list(f_A[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] assert f_A[0].value == 65.4711887279723 assert f_A[1].value == 1.0447210336915187 assert f_A[2].value == -41.025094911185185 + def test_o_bib(tmp_path): - build_test_environment(str(tmp_path), "o",5,3) - f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "F_V0",quarks="lquark lquark", wf = 0, wf2 = 0, version = "2.0", corr_type="bib") + build_test_environment(str(tmp_path), "o", 5, 3) + f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "F_V0", quarks="lquark lquark", wf=0, wf2=0, version="2.0", corr_type="bib") print(f_V0) assert len(f_V0) == 3 - assert list(f_V0[0].shape.keys()) == ["test_|r0","test_|r1","test_|r2"] + assert list(f_V0[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] assert f_V0[0] == 683.6776090085115 assert f_V0[1] == 661.3188585582334 assert f_V0[2] == 683.6776090081005 + +def test_simple_multi_o(tmp_path): + build_test_environment(str(tmp_path), "o", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_o", "test", ["F_V0"], quarks_list=["lquark lquark"], wf1_list=[0], wf2_list=[0], version="2.0", corr_type_list=["bib"]) + f_V0 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + assert len(f_V0) == 3 + assert list(f_V0[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] + assert f_V0[0] == 683.6776090085115 + assert f_V0[1] == 661.3188585582334 + assert f_V0[2] == 683.6776090081005 + + +def test_dict_multi_o(tmp_path): + build_test_environment(str(tmp_path), "o", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_o", "test", + ["F_V0", "f_A", "f_1"], quarks_list=["lquark lquark"], + wf_list=[0], wf2_list=[0], version="2.0", + corr_type_list=["bib", "bi", "bb"], nice_output=False) + print(corrs) + f_1 = corrs["f_1"]['lquark lquark']['0']['0']['0'] + f_A = corrs["f_A"]['lquark lquark']['0']['0']['0'] + f_V0 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + + assert len(f_1) == 1 + assert list(f_1[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] + assert f_1[0].value == 351.1941525454502 + + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + assert len(f_V0) == 3 + assert list(f_V0[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] + assert f_V0[0] == 683.6776090085115 + assert f_V0[1] == 661.3188585582334 + assert f_V0[2] == 683.6776090081005 + + def test_c_bb(tmp_path): - build_test_environment(str(tmp_path), "c",5,3) - f_1 = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "f_1", quarks="lquark lquark", wf = 0, wf2=0, version = "2.0c", corr_type="bb") + build_test_environment(str(tmp_path), "c", 5, 3) + f_1 = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "f_1", quarks="lquark lquark", wf=0, wf2=0, version="2.0c", corr_type="bb") print(f_1) assert len(f_1) == 1 - assert list(f_1[0].shape.keys()) == ["data_c_|r0","data_c_|r1","data_c_|r2"] + assert list(f_1[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] assert f_1[0].value == 351.1941525454502 + def test_c_bi(tmp_path): - build_test_environment(str(tmp_path), "c",5,3) - f_A = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "f_A", quarks="lquark lquark", wf = 0, version = "2.0c") + build_test_environment(str(tmp_path), "c", 5, 3) + f_A = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "f_A", quarks="lquark lquark", wf=0, version="2.0c") print(f_A) assert len(f_A) == 3 - assert list(f_A[0].shape.keys()) == ["data_c_|r0","data_c_|r1","data_c_|r2"] + assert list(f_A[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] assert f_A[0].value == 65.4711887279723 assert f_A[1].value == 1.0447210336915187 assert f_A[2].value == -41.025094911185185 + def test_c_bib(tmp_path): - build_test_environment(str(tmp_path), "c",5,3) - f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "F_V0",quarks="lquark lquark", wf = 0, wf2 = 0, version = "2.0c", corr_type="bib") + build_test_environment(str(tmp_path), "c", 5, 3) + f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "F_V0", quarks="lquark lquark", wf=0, wf2=0, version="2.0c", corr_type="bib") print(f_V0) assert len(f_V0) == 3 - assert list(f_V0[0].shape.keys()) == ["data_c_|r0","data_c_|r1","data_c_|r2"] + assert list(f_V0[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] assert f_V0[0] == 683.6776090085115 assert f_V0[1] == 661.3188585582334 assert f_V0[2] == 683.6776090081005 -def test_a_bb(tmp_path): - build_test_environment(str(tmp_path), "a",5,3) - f_1 = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "f_1", quarks="lquark lquark", wf = 0, wf2=0, version = "2.0a", corr_type="bb") - print(f_1) + +def test_simple_multi_c(tmp_path): + build_test_environment(str(tmp_path), "c", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_c", "data_c", ["F_V0"], quarks_list=["lquark lquark"], wf1_list=[0], wf2_list=[0], version="2.0c", corr_type_list=["bib"]) + f_V0 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + assert len(f_V0) == 3 + assert list(f_V0[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] + assert f_V0[0] == 683.6776090085115 + assert f_V0[1] == 661.3188585582334 + assert f_V0[2] == 683.6776090081005 + + +def test_dict_multi_c(tmp_path): + build_test_environment(str(tmp_path), "c", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_c", "data_c", + ["F_V0", "f_A", "f_1"], quarks_list=["lquark lquark"], + wf_list=[0], wf2_list=[0], version="2.0c", + corr_type_list=["bib", "bi", "bb"], nice_output=False) + print(corrs) + f_1 = corrs["f_1"]['lquark lquark']['0']['0']['0'] + f_A = corrs["f_A"]['lquark lquark']['0']['0']['0'] + f_V0 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + assert len(f_1) == 1 - assert list(f_1[0].shape.keys()) == ["data_a_|r0","data_a_|r1","data_a_|r2"] + assert list(f_1[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] assert f_1[0].value == 351.1941525454502 -def test_a_bi(tmp_path): - build_test_environment(str(tmp_path), "a",5,3) - f_A = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "f_A", quarks="lquark lquark", wf = 0, version = "2.0a") - print(f_A) assert len(f_A) == 3 - assert list(f_A[0].shape.keys()) == ["data_a_|r0","data_a_|r1","data_a_|r2"] + assert list(f_A[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] assert f_A[0].value == 65.4711887279723 assert f_A[1].value == 1.0447210336915187 assert f_A[2].value == -41.025094911185185 -def test_a_bib(tmp_path): - build_test_environment(str(tmp_path), "a",5,3) - f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "F_V0",quarks="lquark lquark", wf = 0, wf2 = 0, version = "2.0a", corr_type="bib") - print(f_V0) assert len(f_V0) == 3 - assert list(f_V0[0].shape.keys()) == ["data_a_|r0","data_a_|r1","data_a_|r2"] + assert list(f_V0[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] assert f_V0[0] == 683.6776090085115 assert f_V0[1] == 661.3188585582334 assert f_V0[2] == 683.6776090081005 + +def test_dict_multi_wf_c(tmp_path): + build_test_environment(str(tmp_path), "c", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_c", "data_c", + ["F_V0", "f_A", "f_1"], quarks_list=["lquark lquark"], + wf_list=[0, 1], wf2_list=[0, 1], version="2.0c", + corr_type_list=["bib", "bi", "bb"], nice_output=False) + rep_names = ["data_c_|r0", "data_c_|r1", "data_c_|r2"] + f_1_00 = corrs["f_1"]['lquark lquark']['0']['0']['0'] + f_1_01 = corrs["f_1"]['lquark lquark']['0']['0']['1'] + f_1_10 = corrs["f_1"]['lquark lquark']['0']['1']['0'] + f_1_11 = corrs["f_1"]['lquark lquark']['0']['1']['1'] + + assert len(f_1_00) == 1 + assert list(f_1_00[0].shape.keys()) == rep_names + assert f_1_00[0].value == 351.1941525454502 + + assert len(f_1_01) == 1 + assert list(f_1_01[0].shape.keys()) == rep_names + assert f_1_01[0].value == 351.20703575855345 + + assert len(f_1_10) == 1 + assert list(f_1_10[0].shape.keys()) == rep_names + assert f_1_10[0].value == 351.20703575855515 + + assert len(f_1_11) == 1 + assert list(f_1_11[0].shape.keys()) == rep_names + assert f_1_11[0].value == 351.22001235609065 + + f_A = corrs["f_A"]['lquark lquark']['0']['0']['0'] + + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == rep_names + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + f_V0_00 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + f_V0_01 = corrs["F_V0"]['lquark lquark']['0']['0']['1'] + f_V0_10 = corrs["F_V0"]['lquark lquark']['0']['1']['0'] + f_V0_11 = corrs["F_V0"]['lquark lquark']['0']['1']['1'] + + assert len(f_V0_00) == 3 + assert list(f_V0_00[0].shape.keys()) == rep_names + assert f_V0_00[0].value == 683.6776090085115 + assert f_V0_00[1].value == 661.3188585582334 + assert f_V0_00[2].value == 683.6776090081005 + + assert len(f_V0_01) == 3 + assert list(f_V0_01[0].shape.keys()) == rep_names + assert f_V0_01[0].value == 683.7028316879306 + assert f_V0_01[1].value == 661.3432563640756 + assert f_V0_01[2].value == 683.7028316875197 + + assert len(f_V0_10) == 3 + assert list(f_V0_10[0].shape.keys()) == rep_names + assert f_V0_10[0].value == 683.7028316879289 + assert f_V0_10[1].value == 661.343256364074 + assert f_V0_10[2].value == 683.702831687518 + + assert len(f_V0_11) == 3 + assert list(f_V0_11[0].shape.keys()) == rep_names + assert f_V0_11[0].value == 683.7280552978792 + assert f_V0_11[1].value == 661.3676550700158 + assert f_V0_11[2].value == 683.7280552974681 + + +def test_a_bb(tmp_path): + build_test_environment(str(tmp_path), "a", 5, 3) + f_1 = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "f_1", quarks="lquark lquark", wf=0, wf2=0, version="2.0a", corr_type="bb") + print(f_1) + assert len(f_1) == 1 + assert list(f_1[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_1[0].value == 351.1941525454502 + + +def test_a_bi(tmp_path): + build_test_environment(str(tmp_path), "a", 5, 3) + f_A = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "f_A", quarks="lquark lquark", wf=0, version="2.0a") + print(f_A) + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + +def test_a_bib(tmp_path): + build_test_environment(str(tmp_path), "a", 5, 3) + f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "F_V0", quarks="lquark lquark", wf=0, wf2=0, version="2.0a", corr_type="bib") + print(f_V0) + assert len(f_V0) == 3 + assert list(f_V0[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_V0[0] == 683.6776090085115 + assert f_V0[1] == 661.3188585582334 + assert f_V0[2] == 683.6776090081005 + + +def test_simple_multi_a(tmp_path): + build_test_environment(str(tmp_path), "a", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_a", "data_a", ["F_V0"], quarks_list=["lquark lquark"], wf1_list=[0], wf2_list=[0], version="2.0a", corr_type_list=["bib"]) + f_V0 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + assert len(f_V0) == 3 + assert list(f_V0[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_V0[0] == 683.6776090085115 + assert f_V0[1] == 661.3188585582334 + assert f_V0[2] == 683.6776090081005 + + +def test_dict_multi_a(tmp_path): + build_test_environment(str(tmp_path), "a", 5, 3) + corrs = sfin.read_sfcf_multi(str(tmp_path) + "/data_a", "data_a", + ["F_V0", "f_A", "f_1"], quarks_list=["lquark lquark"], + wf_list=[0], wf2_list=[0], version="2.0a", + corr_type_list=["bib", "bi", "bb"], nice_output=False) + print(corrs) + f_1 = corrs["f_1"]['lquark lquark']['0']['0']['0'] + f_A = corrs["f_A"]['lquark lquark']['0']['0']['0'] + f_V0 = corrs["F_V0"]['lquark lquark']['0']['0']['0'] + + assert len(f_1) == 1 + assert list(f_1[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_1[0].value == 351.1941525454502 + + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + assert len(f_V0) == 3 + assert list(f_V0[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_V0[0] == 683.6776090085115 + assert f_V0[1] == 661.3188585582334 + assert f_V0[2] == 683.6776090081005 + + def test_find_corr(): pattern = 'name ' + "f_A" + '\nquarks ' + "lquark lquark" + '\noffset ' + str(0) + '\nwf ' + str(0) start_read, T = sfin._find_correlator("tests/data/sfcf_test/data_c/data_c_r0/data_c_r0_n1", "2.0c", pattern, False) @@ -129,7 +320,8 @@ def test_find_corr(): with pytest.raises(ValueError): sfin._find_correlator("tests/data/sfcf_test/broken_data_c/data_c_r0/data_c_r0_n1", "2.0c", pattern, False) -def test_read_compact_file(tmp_path): + +def test_read_compact_file(): rep_path = "tests/data/sfcf_test/broken_data_c/data_c_r0/" config_file = "data_c_r0_n1" start_read = 469 @@ -139,3 +331,10 @@ def test_read_compact_file(tmp_path): im = False with pytest.raises(Exception): sfin._read_compact_file(rep_path, config_file, start_read, T, b2b, name, im) + + +def test_find_correlator(): + file = "tests/data/sfcf_test/data_c/data_c_r0/data_c_r0_n1" + found_start, found_T = sfin._find_correlator(file, "2.0", "name f_A\nquarks lquark lquark\noffset 0\nwf 0", False, False) + assert found_start == 21 + assert found_T == 3 From dc63142f8e72b1645e6bd2a2df655bb985698da3 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Tue, 24 Oct 2023 19:30:52 +0200 Subject: [PATCH 055/107] Fixed bugs for combined fits with multiple independent variables (#211) --- pyerrors/fits.py | 4 ++-- tests/fits_test.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 6518b224..b58f62b6 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -365,7 +365,7 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): raise Exception('The minimization procedure did not converge.') output.chisquare = chisquare - output.dof = x_all.shape[-1] - n_parms + len(loc_priors) + output.dof = y_all.shape[-1] - n_parms + len(loc_priors) output.p_value = 1 - scipy.stats.chi2.cdf(output.chisquare, output.dof) if output.dof > 0: output.chisquare_by_dof = output.chisquare / output.dof @@ -393,7 +393,7 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): hat_vector = prepare_hat_matrix() A = W @ hat_vector P_phi = A @ np.linalg.pinv(A.T @ A) @ A.T - expected_chisquare = np.trace((np.identity(x_all.shape[-1]) - P_phi) @ W @ cov @ W) + expected_chisquare = np.trace((np.identity(y_all.shape[-1]) - P_phi) @ W @ cov @ W) output.chisquare_by_expected_chisquare = output.chisquare / expected_chisquare if not silent: print('chisquare/expected_chisquare:', output.chisquare_by_expected_chisquare) diff --git a/tests/fits_test.py b/tests/fits_test.py index dbc227dc..3ed8f5fb 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -1142,6 +1142,53 @@ def test_fit_dof(): assert cd[0] != cd[0] # Check for nan assert np.all(np.array(cd[1:]) > 0) + N = 5 + + def fitf(a, x): + return a[0] + 0 * x + + def fitf_multi(a, x): + return a[0] + 0 * x[0] + 0*x[1] + + for priors in [None, [pe.cov_Obs(3, 1, 'p')]]: + if priors is None: + lp = 0 + else: + lp = len(priors) + x = [1. for i in range(N)] + y = [pe.cov_Obs(i, .1, '%d' % (i)) for i in range(N)] + [o.gm() for o in y] + res = pe.fits.least_squares(x, y, fitf, expected_chisquare=True, priors=priors) + assert(res.dof == N - 1 + lp) + if priors is None: + assert(np.isclose(res.chisquare_by_expected_chisquare, res.chisquare_by_dof)) + + kl = ['a', 'b'] + x = {k: [1. for i in range(N)] for k in kl} + y = {k: [pe.cov_Obs(i, .1, '%d%s' % (i, k)) for i in range(N)] for k in kl} + [[o.gm() for o in y[k]] for k in y] + res = pe.fits.least_squares(x, y, {k: fitf for k in kl}, expected_chisquare=True, priors=priors) + assert(res.dof == 2 * N - 1 + lp) + if priors is None: + assert(np.isclose(res.chisquare_by_expected_chisquare, res.chisquare_by_dof)) + + x = np.array([[1., 2.] for i in range(N)]).T + y = [pe.cov_Obs(i, .1, '%d' % (i)) for i in range(N)] + [o.gm() for o in y] + res = pe.fits.least_squares(x, y, fitf_multi, expected_chisquare=True, priors=priors) + assert(res.dof == N - 1 + lp) + if priors is None: + assert(np.isclose(res.chisquare_by_expected_chisquare, res.chisquare_by_dof)) + + x = {k: np.array([[1., 2.] for i in range(N)]).T for k in kl} + y = {k: [pe.cov_Obs(i, .1, '%d%s' % (i, k)) for i in range(N)] for k in kl} + [[o.gm() for o in y[k]] for k in y] + res = pe.fits.least_squares(x, y, {k: fitf_multi for k in kl}, expected_chisquare=True, priors=priors) + + assert(res.dof == 2 * N - 1 + lp) + if priors is None: + assert(np.isclose(res.chisquare_by_expected_chisquare, res.chisquare_by_dof)) + def test_combined_fit_constant_shape(): N1 = 16 From e032412ebd46a7582fa73e20b8cf3c805be2bb3d Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 27 Oct 2023 08:39:33 +0200 Subject: [PATCH 056/107] [ci] Added python 3.12 to pytest and examples workflows. (#212) --- .github/workflows/examples.yml | 2 +- .github/workflows/pytest.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index cd4f73c9..dd2fc4cf 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.10"] + python-version: ["3.8", "3.10", "3.12"] steps: - name: Checkout source diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index b6a5290f..b76dba9f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] include: - os: macos-latest python-version: "3.10" From eb83ff20914c56fd3a07547d55982a0937450983 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 10 Nov 2023 14:21:35 +0100 Subject: [PATCH 057/107] [ci] removed failing pytest 3.12 test for now. --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index b76dba9f..b6a5290f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11"] include: - os: macos-latest python-version: "3.10" From d689959b1f34a359f37f3cb6c39e1c6985e2135f Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Fri, 10 Nov 2023 19:13:11 +0100 Subject: [PATCH 058/107] fix: Check if configs appears multiple times when creating an obs (#216) --- pyerrors/obs.py | 4 +++- tests/obs_test.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index df287a10..3f86a6b3 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -104,7 +104,9 @@ class Obs: elif isinstance(idx, (list, np.ndarray)): dc = np.unique(np.diff(idx)) if np.any(dc < 0): - raise ValueError("Unsorted idx for idl[%s]" % (name)) + raise ValueError("Unsorted idx for idl[%s] at position %s" % (name, ' '.join(['%s' % (pos + 1) for pos in np.where(np.diff(idx) < 0)[0]]))) + elif np.any(dc == 0): + raise ValueError("Duplicate entries in idx for idl[%s] at position %s" % (name, ' '.join(['%s' % (pos + 1) for pos in np.where(np.diff(idx) == 0)[0]]))) if len(dc) == 1: self.idl[name] = range(idx[0], idx[-1] + dc[0], dc[0]) else: diff --git a/tests/obs_test.py b/tests/obs_test.py index 02d539ef..0631effe 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -40,6 +40,10 @@ def test_Obs_exceptions(): pe.Obs([np.random.rand(4)], ['name']) with pytest.raises(ValueError): pe.Obs([np.random.rand(5)], ['1'], idl=[[5, 3, 2 ,4 ,1]]) + with pytest.raises(ValueError): + pe.Obs([np.random.rand(5)], ['1'], idl=[[1, 2, 3, 3, 5]]) + with pytest.raises(ValueError): + pe.Obs([np.random.rand(5)], ['1'], idl=[[1, 1, 3, 1, 5]]) with pytest.raises(TypeError): pe.Obs([np.random.rand(5)], ['1'], idl=['t']) with pytest.raises(ValueError): From a8d16631d8aa72315de778f75a8a550d325bd52c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 14 Nov 2023 20:28:09 +0100 Subject: [PATCH 059/107] [doc] Added Programming Language :: Python :: 3.12 classifier to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index ab038e8d..da240d16 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ setup(name='pyerrors', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Scientific/Engineering :: Physics' ], ) From e1a4d0c2189410dff45e825d986e3671c5eb4dd6 Mon Sep 17 00:00:00 2001 From: JanNeuendorf <75676159+JanNeuendorf@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:57:18 +0100 Subject: [PATCH 060/107] Additional GEVP method with errors (#195) * Added the function error_gevp() to compute the gevp with statistical errors. * Changed method name from error_gevp to error_GEVP and removed automatic gamma method * added auto_gamma to error_GEVP * Specified exceptions in Corr.error_GEVP * Fixed a wrong path. It should be np.linalg.LinAlgError * Added a test for error_GEVP * The tests of error_gevp loads a test matrix * Incorporated eigenvectors with uncertainties in GEVP routine * Cleaned up GEVP routines * Cleaned up breaking change from merge * Released tolerance in test of GEVP * Repaired broken GEVP test --------- Co-authored-by: Simon Kuberski --- pyerrors/correlators.py | 122 ++++++++++++++++++++++++---- pyerrors/linalg.py | 6 ++ tests/correlators_test.py | 32 ++++++-- tests/data/test_matrix_corr.json.gz | Bin 0 -> 184942 bytes tests/linalg_test.py | 4 + 5 files changed, 141 insertions(+), 23 deletions(-) create mode 100644 tests/data/test_matrix_corr.json.gz diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 5c9e236a..de1addfd 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -8,6 +8,7 @@ from .obs import Obs, reweight, correlate, CObs from .misc import dump_object, _assert_equal_properties from .fits import least_squares from .roots import find_root +from . import linalg class Corr: @@ -298,7 +299,7 @@ class Corr: transposed = [None if _check_for_none(self, G) else G.T for G in self.content] return 0.5 * (Corr(transposed) + self) - def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs): + def GEVP(self, t0, ts=None, sort="Eigenvalue", vector_obs=False, **kwargs): r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors. The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the @@ -317,14 +318,21 @@ class Corr: If sort="Eigenvector" it gives a reference point for the sorting method. sort : string If this argument is set, a list of self.T vectors per state is returned. If it is set to None, only one vector is returned. - - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice. + - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice. (default) - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state. The reference state is identified by its eigenvalue at $t=t_s$. + - None: The GEVP is solved only at ts, no sorting is necessary + vector_obs : bool + If True, uncertainties are propagated in the eigenvector computation (default False). Other Parameters ---------------- state : int Returns only the vector(s) for a specified state. The lowest state is zero. + method : str + Method used to solve the GEVP. + - "eigh": Use scipy.linalg.eigh to solve the GEVP. (default for vector_obs=False) + - "cholesky": Use manually implemented solution via the Cholesky decomposition. Automatically chosen if vector_obs==True. ''' if self.N == 1: @@ -342,16 +350,34 @@ class Corr: else: symmetric_corr = self.matrix_symmetric() - G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0]) - np.linalg.cholesky(G0) # Check if matrix G0 is positive-semidefinite. + def _get_mat_at_t(t, vector_obs=vector_obs): + if vector_obs: + return symmetric_corr[t] + else: + return np.vectorize(lambda x: x.value)(symmetric_corr[t]) + G0 = _get_mat_at_t(t0) + + method = kwargs.get('method', 'eigh') + if vector_obs: + chol = linalg.cholesky(G0) + chol_inv = linalg.inv(chol) + method = 'cholesky' + else: + chol = np.linalg.cholesky(_get_mat_at_t(t0, vector_obs=False)) # Check if matrix G0 is positive-semidefinite. + if method == 'cholesky': + chol_inv = np.linalg.inv(chol) + else: + chol_inv = None if sort is None: if (ts is None): raise Exception("ts is required if sort=None.") if (self.content[t0] is None) or (self.content[ts] is None): raise Exception("Corr not defined at t0/ts.") - Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts]) - reordered_vecs = _GEVP_solver(Gt, G0) + Gt = _get_mat_at_t(ts) + reordered_vecs = _GEVP_solver(Gt, G0, method=method, chol_inv=chol_inv) + if kwargs.get('auto_gamma', False) and vector_obs: + [[o.gm() for o in ev if isinstance(o, Obs)] for ev in reordered_vecs] elif sort in ["Eigenvalue", "Eigenvector"]: if sort == "Eigenvalue" and ts is not None: @@ -359,8 +385,8 @@ class Corr: all_vecs = [None] * (t0 + 1) for t in range(t0 + 1, self.T): try: - Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t]) - all_vecs.append(_GEVP_solver(Gt, G0)) + Gt = _get_mat_at_t(t) + all_vecs.append(_GEVP_solver(Gt, G0, method=method, chol_inv=chol_inv)) except Exception: all_vecs.append(None) if sort == "Eigenvector": @@ -369,15 +395,17 @@ class Corr: all_vecs = _sort_vectors(all_vecs, ts) reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)] + if kwargs.get('auto_gamma', False) and vector_obs: + [[[o.gm() for o in evn] for evn in ev if evn is not None] for ev in reordered_vecs] else: - raise Exception("Unkown value for 'sort'.") + raise Exception("Unknown value for 'sort'. Choose 'Eigenvalue', 'Eigenvector' or None.") if "state" in kwargs: return reordered_vecs[kwargs.get("state")] else: return reordered_vecs - def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"): + def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue", **kwargs): """Determines the eigenvalue of the GEVP by solving and projecting the correlator Parameters @@ -387,7 +415,7 @@ class Corr: All other parameters are identical to the ones of Corr.GEVP. """ - vec = self.GEVP(t0, ts=ts, sort=sort)[state] + vec = self.GEVP(t0, ts=ts, sort=sort, **kwargs)[state] return self.projected(vec) def Hankel(self, N, periodic=False): @@ -1386,8 +1414,13 @@ class Corr: return Corr(newcontent) -def _sort_vectors(vec_set, ts): +def _sort_vectors(vec_set_in, ts): """Helper function used to find a set of Eigenvectors consistent over all timeslices""" + + if isinstance(vec_set_in[ts][0][0], Obs): + vec_set = [anp.vectorize(lambda x: float(x))(vi) if vi is not None else vi for vi in vec_set_in] + else: + vec_set = vec_set_in reference_sorting = np.array(vec_set[ts]) N = reference_sorting.shape[0] sorted_vec_set = [] @@ -1406,9 +1439,9 @@ def _sort_vectors(vec_set, ts): if current_score > best_score: best_score = current_score best_perm = perm - sorted_vec_set.append([vec_set[t][k] for k in best_perm]) + sorted_vec_set.append([vec_set_in[t][k] for k in best_perm]) else: - sorted_vec_set.append(vec_set[t]) + sorted_vec_set.append(vec_set_in[t]) return sorted_vec_set @@ -1418,10 +1451,63 @@ def _check_for_none(corr, entry): return len(list(filter(None, np.asarray(entry).flatten()))) < corr.N ** 2 -def _GEVP_solver(Gt, G0): - """Helper function for solving the GEVP and sorting the eigenvectors. +def _GEVP_solver(Gt, G0, method='eigh', chol_inv=None): + r"""Helper function for solving the GEVP and sorting the eigenvectors. + + Solves $G(t)v_i=\lambda_i G(t_0)v_i$ and returns the eigenvectors v_i The helper function assumes that both provided matrices are symmetric and only processes the lower triangular part of both matrices. In case the matrices - are not symmetric the upper triangular parts are effectively discarded.""" - return scipy.linalg.eigh(Gt, G0, lower=True)[1].T[::-1] + are not symmetric the upper triangular parts are effectively discarded. + + Parameters + ---------- + Gt : array + The correlator at time t for the left hand side of the GEVP + G0 : array + The correlator at time t0 for the right hand side of the GEVP + Method used to solve the GEVP. + - "eigh": Use scipy.linalg.eigh to solve the GEVP. + - "cholesky": Use manually implemented solution via the Cholesky decomposition. + chol_inv : array, optional + Inverse of the Cholesky decomposition of G0. May be provided to + speed up the computation in the case of method=='cholesky' + + """ + if isinstance(G0[0][0], Obs): + vector_obs = True + else: + vector_obs = False + + if method == 'cholesky': + if vector_obs: + cholesky = linalg.cholesky + inv = linalg.inv + eigv = linalg.eigv + matmul = linalg.matmul + else: + cholesky = np.linalg.cholesky + inv = np.linalg.inv + + def eigv(x, **kwargs): + return np.linalg.eigh(x)[1] + + def matmul(*operands): + return np.linalg.multi_dot(operands) + N = Gt.shape[0] + output = [[] for j in range(N)] + if chol_inv is None: + chol = cholesky(G0) # This will automatically report if the matrix is not pos-def + chol_inv = inv(chol) + + try: + new_matrix = matmul(chol_inv, Gt, chol_inv.T) + ev = eigv(new_matrix) + ev = matmul(chol_inv.T, ev) + output = np.flip(ev, axis=1).T + except (np.linalg.LinAlgError, TypeError, ValueError): # The above code can fail because of linalg-errors or because the entry of the corr is None + for s in range(N): + output[s] = None + return output + elif method == 'eigh': + return scipy.linalg.eigh(Gt, G0, lower=True)[1].T[::-1] diff --git a/pyerrors/linalg.py b/pyerrors/linalg.py index 73c93169..5a489e26 100644 --- a/pyerrors/linalg.py +++ b/pyerrors/linalg.py @@ -271,6 +271,12 @@ def eig(obs, **kwargs): return w +def eigv(obs, **kwargs): + """Computes the eigenvectors of a given hermitian matrix of Obs according to np.linalg.eigh.""" + v = derived_observable(lambda x, **kwargs: anp.linalg.eigh(x)[1], obs) + return v + + def pinv(obs, **kwargs): """Computes the Moore-Penrose pseudoinverse of a matrix of Obs.""" return derived_observable(lambda x, **kwargs: anp.linalg.pinv(x), obs) diff --git a/tests/correlators_test.py b/tests/correlators_test.py index 5f213034..d6d2012c 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -200,17 +200,17 @@ def test_padded_correlator(): def test_corr_exceptions(): obs_a = pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test']) - obs_b= pe.Obs([np.random.normal(0.1, 0.1, 99)], ['test']) + obs_b = pe.Obs([np.random.normal(0.1, 0.1, 99)], ['test']) with pytest.raises(Exception): pe.Corr([obs_a, obs_b]) obs_a = pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test']) - obs_b= pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test'], idl=[range(1, 200, 2)]) + obs_b = pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test'], idl=[range(1, 200, 2)]) with pytest.raises(Exception): pe.Corr([obs_a, obs_b]) obs_a = pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test']) - obs_b= pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test2']) + obs_b = pe.Obs([np.random.normal(0.1, 0.1, 100)], ['test2']) with pytest.raises(Exception): pe.Corr([obs_a, obs_b]) @@ -436,6 +436,7 @@ def test_GEVP_solver(): sp_vecs = [v / np.sqrt((v.T @ mat2 @ v)) for v in sp_vecs] assert np.allclose(sp_vecs, pe.correlators._GEVP_solver(mat1, mat2), atol=1e-14) + assert np.allclose(np.abs(sp_vecs), np.abs(pe.correlators._GEVP_solver(mat1, mat2, method='cholesky'))) def test_GEVP_none_entries(): @@ -552,7 +553,7 @@ def test_corr_no_filtering(): li = [-pe.pseudo_Obs(.2, .1, 'a', samples=10) for i in range(96)] for i in range(len(li)): li[i].idl['a'] = range(1, 21, 2) - c= pe.Corr(li) + c = pe.Corr(li) b = pe.pseudo_Obs(1, 1e-11, 'a', samples=30) c *= b assert np.all([c[0].idl == o.idl for o in c]) @@ -572,6 +573,28 @@ def test_corr_symmetric(): assert scorr[0] == corr[0] +def test_error_GEVP(): + corr = pe.input.json.load_json("tests/data/test_matrix_corr.json.gz") + t0, ts, state = 3, 6, 0 + vec_regular = corr.GEVP(t0=t0)[state][ts] + vec_errors = corr.GEVP(t0=t0, vector_obs=True, auto_gamma=True)[state][ts] + vec_regular_chol = corr.GEVP(t0=t0, method='cholesky')[state][ts] + print(vec_errors) + print(type(vec_errors[0])) + assert(np.isclose(np.asarray([e.value for e in vec_errors]), vec_regular).all()) + assert(all([e.dvalue > 0. for e in vec_errors])) + + projected_regular = corr.projected(vec_regular).content[ts + 1][0] + projected_errors = corr.projected(vec_errors).content[ts + 1][0] + projected_regular.gamma_method() + projected_errors.gamma_method() + assert(projected_errors.dvalue > projected_regular.dvalue) + assert(corr.GEVP(t0, vector_obs=True)[state][42] is None) + + assert(np.isclose(vec_regular_chol, vec_regular).all()) + assert(np.isclose(corr.GEVP(t0=t0, state=state)[ts], vec_regular).all()) + + def test_corr_array_ndim1_init(): y = [pe.pseudo_Obs(2 + np.random.normal(0.0, 0.1), .1, 't') for i in np.arange(5)] cc1 = pe.Corr(y) @@ -688,7 +711,6 @@ def test_matrix_trace(): for el in corr.trace(): assert el == 0 - with pytest.raises(ValueError): corr.item(0, 0).trace() diff --git a/tests/data/test_matrix_corr.json.gz b/tests/data/test_matrix_corr.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..d61b3bcff44e8b15510ded8277cb06921bd09e81 GIT binary patch literal 184942 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%0AW9 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 z z0p<$xm&)Jz^g9>NtnbYVGBAxq86m;fv79@JpW}}E;-Vb~l8RU0>e<}~73CNY@(Urh z9tq_}=6DvNj9o?!ZVNv4+{iI%`)?$Hw{L%~7C|cRx=!1@;Bc&Rx@93?9M=z*mW@l? z^xTT~I=aX;R!Y05uk4FMtf6vCz-mD|X-{7mAqr2?pp<(7%^2C*Aohi*ca7qYTur%l zk~6Mvz1fmBTf*qee&?vN&n$H$&~+lgK$orSKl>V)F7s3{dDIlBxl|nH(JQYh0{Nqo zdfyvUrGoNiU}#SaC0CrR9zRa|^u-pSE!|N8S;l5ahz$=TOJ|BeTw#mZf6$!lzl2*4 zDEM2rv@4YPq~K)i+1N6nA)2pJk^9g3`v^Wx(UQu*wX{iC(-v30(4}T%4m)U2E(jZy zk2L`9Z{o@N=v7nzJ>-#K&W}xigtbL2QKAv0n#V zSIo3N(nenxvI7PFWsTQETkrqShK`tp7=5mJ<-R|628m&xR-b`Tx`#>Eqes?)9rTrr z6^k^@7VB$i>gRQ}sEd6JLj2B5FxYKLK|!TMc&P6>$~jz6`at9{KSJB9_-Ykwr(ZT2 zu0#gJgJ30+-(;9RP4?Dj5^28ylA4k~4sITVCs{;4J{HdM@qGz2KPO-6k<&?Mq^EX| z+iK*w-y?vilk4v}wpkjCOU==o0@AvVE9`WcZHNbVPt1Kww0%tyA zH>eAWrU`UwHYLU3+eb;bwKdt%;#ixfQ)ANdtc-xPylywCsiDGaiLnW4_9%+?pSEv| zQG@wKwUV|=tO)@T&cvl7;;xr1_c-y zl_gCG$aV-$QKhMkJO3Fnb+U5H<(BH&goO1~S&eP|i2v{0cMs@VLajYqT76o#P4qgaRear6;Pu9IHa@X@(pDZ4L;kHI-{Da;_#ET-|ADBlK{Vu*(bz_0SBr?T?e-BopjeeRO@411f}O@ zdD+ph*m#bfeB~LYr`zo9*jlw-8|+3xb#gVm$1vK2b^oV8{^?OvV&HEdNAfE}N{zQ; zf587o*}W#a%410w9?`c0gd|}86KkH-#ixFDMORd`w-3e`bXhC&P4<)Ip;`JVuguq} zkmP*Hjcre?Ir9sIo&=}CRr0>SD;iPycR7XyZc)%Mcy!_(wTL6e%8?Ldm=7GAcK@x< zrhi$1S(?#~<)$9%q!?G#|3OdxfwJkzy*+J#KD7L!k)U5Bd}zcdwsk@oQD9-HTE9nX z!o1RKjda;|{TpK^AMF;XV7EkeLN2_qX<9zvnD-IFea%IZGKe4}`4FXTh8QqZ0U|>F zIqHOwefOJ{_y`bx$9$Tr!<53uX-#*Re^_Knk^}~7$J}+l4OmSGBW$@*<{hX~79Boay#Ri^H!K93o*oxBj8L3lrHfufMXA!_e z{k*|jX-U4JER`AUXY-A_`Mse(^A8pm-{vLhZk20n97e%O88?X`Jas=z*|*=Y4jk>S39m3(Tel0pf5{ zy`dCLI#}w{w}jqrd8Fb~7c~q@0WO~2FZk-Kjwc&%8H?!Thnz4j;~T|`lR8wJ#_O=t zB=U6D35uHE_S=L9d0`5Mgjwfe$TR?PO3!CvrlT|6iA~quc^0XR4*H2_pKWFy1rnr@ z4f$4zI<;+D+Z%2AVL5eVu|0`ikW5AIkI`ztsoG4qEhE*OpYU0iCXF(U5KY4}-|ZG` zQbj2~i7(p*n;F3|ZaOLYQ`YIrt@sv`eFU$+ZJ&;G2ue0ax>32fX5!)ztBl9A)_wkI zK(efcqj?06J&<0HUqs?->Z;EokKNJQlo$p;7$<_we$V1iYbUDHb(j_eZnF%w1`g!xk6_RURW zt%Yi$v-4lIB?EgwGTDS4n2| z`do!<{!Fzzsf!zhhPd#C;b%=&_JPm_A`O!ychA=!oC0=*BYXc^_VE>0We}=fe#_Zs z6TO)65T)GwPu<|R_$mFwmw>3JZ=_d*Ps4?z@>n-}?0sT<;IK2w-3zyNM&T`NgKzw) z>r8K!3{>5gw4L!XzDq(^BnKyecYa~!xXcMrQDs>-JO#$1BT6O9);`%_VR)*HD@dyf zE#{fV%56J^ZFB?!J(+3*Vr0A2f%m&nWoasdICQGzYhO3u5gSCChO?gg`shc1)sk_P z@gNPSB!vAFov1-?!^+4sd$=LCulM@co1hSsEo8fMvC7U=ksmP^?--az8UxCpJ3OZD zbrdromox+A6}0Lms@>FdEfSr8isv8hzXaoW(8apc{r*J$979eaf^T2tGXoVTjH16C z&tRA_&2pBwuTu$~P_jeJbD@s0+N+cOgoS1iWx$obcQ1N+~CEY(^p z6ayKOQjSg?4UO$}KR2=MwhXf=phNGaoIf&Kjo{1f)h)OASMpaa^laeO%v{$lHEUj#MmujAt z;MJovSxhpJDf|34QQ);L(pt>POyEzdiJJz8oGj_92c)y$U})py)CpewZiMGYkAtSG zrlG5)7&N|=;8*p3P3`PK>X_^i4Yi0_fyPQ}AlTTmlS3s>bF;_NEwxV3U4I6Je1y#* z2iGfkkZzT}kCWQ2fz}GvJUw66$EHB1#}E8}u()jRl`2nV%)z+|`}G}s9wNjych0qW zqLdAG1J3}$ozr@AE8_B94`5JPC>TneBl#isWO`wovno`bks~mxYk&=Pu`}R5T#@T_ z>QiQxlUVD`jWf>1W2X7Uf`AY<|6tu(!m`mPuE)8-Z^U?M92uvswpe1{?UKpHnMyF-*Nf(zI*?A7BY+lS|DD~m!&S=nWtYJI5}{U zZ%oKl7s38Y@hCAXu75!Bp>*zA!!@nKmCA>h)9oBAW*spKCU1V% zllu5v%rj;|p(2dcb)61}$`uG8`E^R!>+(D?#Hitx8f)nws@=?{95XMCds}ET>vPB! z)BH0iPJQwh{dVwZJ%NCmZT8|);n8RfXS((AWu zxYXkZ?TZN2S~*Ed!CXy92Fmt#pNK`c)^#=nT%AUV>Iv6!QnwOtrb8<>g;Ut)8XsP<=ouI>T7r++6FkdC@RYhocR`iilz_8(DAUcBO{Xy>D?FLlKT z#iW2+c}_OitIW}ZF#DFqxfh1RQ7tw8QQGB!)*`zgbCrSUF;NoKH+GE1Ty%~O%c#WE zXRf)qEHzy)F)0%ycJTe=Eggll{IdnZt`J~=jQGE2N2)_IspLI z7Tat9IU9i48w+UPM-N#9;EKv83x-w6lMOF@BiQQ+D?0;jm*Qbz{SCK2NSZ~~cYE|Z z)tG;dRMM8^EGQ7}8+!&v5$HPj>9NkVifE;|vzWF~tD6<4auaVNf8{$4(?ZY6up>%s zeJ{&0`K>U{brV&>t_?6lGckVQI)}VFE<&O?63^A5Xc_OnZ05={>~Wl+ZM|+11+c&% zxfaPy*^Hb{v_N4sTHh-0ww|~&wvUM%yCAt6X}u}D^uB;v?mVz95||XP+FbZ*4}LQo z%w~!+<6N$6HlGCgrQ_MMMnpW(0JMyG>b$}=GgD1kyH!c6n`ef^*_?k5vbb&#*?lA; z$VBu8y8ncL*w>j16iV3n8`%tRiMXdEQz7SE4q+zO4_LwwQF!U+wemusb0zZ zq_mpHA<3kt({mdjobfvDmm8jzP9oWcy;)br&T2h-rbsg3WCvVj<2n0&pZ$8pgsssM zwLYq+6ZO_?%Gmbo)%&y2UoHIK3>V)&sEb2pr}`wjHb{ec7?}bAjT{oO$K`C3Crp^8 zIuM}8eEt68mXdvy^zwYIHkLtp3PCt24D}89YJ}+WOKp9wcd^?pMgR#@+PLd55!gfs z3`<8SaBnEsoho~lx!;`pETWnAc2DKd$lUi;Q@%JxePGlQ+9+KqALCP>JoxMIs^)ws zDa}v%R2w1y#y0wy_X?M5zT$~@6lHwdZ;d9HjfbWU1m!)$u;?wd{HcKE>vEd|1M1SMBIy~o zLWUtFQl9N7bS81^h_R=~R23Tf1fWU9AU8aMu-_y#(lb&32KF;;B!KEQT$G_l$=&ExT3J^YEj5M}RQ)~}-EQs=yX$gvWx`jsll zd;i|f8ds8!mLlW8sA)~xY#OS~3F}jNfs%)SWiu{vKxbVytE7$OD$g%l(K!KR1p$m* z>j1BD*Wqis1xsBQnS`B3A2d6Sv+=TE`J!lW1{hu5b%}c!`OSpH!5f6W^?+|(Dke^5 z%n+j_7Kh0hs91??qOI(NQQJ4m^z6|mckPZr@CtnL8dKI>GX*Kqf`Yl9^9O4Cp3NHd zGksxx-0=q`xYcLkHflN646w^E}$Fc>taXm)b?>>-nL%XZ3_RTCByJXOM7BxJd+)t`q!eU zVI@8Wf@eHieFPs6nvv-w6|Btf8%mNEjWSo6Y^bGkqgND)rPlo{LfLaBn-VjedHD)0^?-qN3G-F{$(k`p!jBmJ`bNNoL_~nEj{}_D_0m* zq%2akkHrrQpX}QY9`fT#1kVFdhFT`E;7|y=%KCu!FBB%TC<#@0*7+#*-P*HvAA?V? z6Fg&gfupa9dbaicnk3P05hoDkdN*whLPlJ;?Q7qlxzvixDWujtF|LDVme`GpXh{C5 zNwkkrNRr4wjU_B9T=iKJssWN~lzy|*u@6+w$m+ijVP4A3=R z2>}G$Ez5dNA@Hr?_)mf_TWo+U+xI)t7iOyERKJPk-pqbr$nSQrzi>lHTlQ_oBBj|o z>%19z-wktX+Q&G?3CKInb&%IRLmZ{H8B^fA*!le9!+->)JaC zWi(kb#-o?DSsRv^@BX_sRiY*!O9`Dh9FVDa4FX1q; zBdDmK>r!UJ(_8=+)r<&glr=GfdY_9&q9sQ8&k{i!^*`JWQJZoSL;Ah4)G0BQ(D}D; z&c$+eDMG?NnQ69Vv-Kh!qRfK-+QOc%y1Uev=?T*TC zlS`z~lsm!Dt!xODs=uh?Lpt|0X&Xs)$d(H@qAjRekDU=inh7MG?7Tg8+#?S)Ze1zs zC}#$-S4|nccD($C2+M$~7yJ(zOc7&bqX?ZmH#H`B6OtEwg6A&l{Xhl;ld{c@TZKbH1G#M&=06n zAd>dNKdcTe%C}qXo&=B2kFRBMn{3jnUhfZwbLZA3h+%)b*Ef^)rZ!|*aQ$xBmU-Dm z`xuFq>q>W^rI_z2jq5s$dQ>%FX)qjhai2Pw|HG{Ux_IU$Tq?&N5>m}`@#6?l|AN@u zF`S$moMiV$YJwrr>f}vnhv)0wcci*y(WBVHbb|5OzSZ!xZsr%)Mk9qrtNiFxX9UVe zpr7?E``0ScNo<6Z@N{oPT6oRE)Q&hP@AbJ!VrDgt4g1-Yv~HP^4W^RKkCH|xJQw&h z+4sU$W#O$_s_}E)b#t7gMA=w;XB9;Lq7LDqjvRO%=X=x55cDV}?96)q-6h{L>_|qa zBlYzGZ$Q>s#6u{X)shmbX7j+C`%G1T@y)0c(2an%D3p zy;-qyak@N6R{e#rxg8tONGq+P!MoFSQ~x{14(Pi#-z4^$g`Xb=Nk@~6I3?4kE~wY5 zL=o{6HvId8xw-i0roujdH#1rN6I17RFD(oY1bv)Nhj0}>-iVw)kE=Ayew_8AJr@55{tmyfVAj3n0!%so1D%4Ag5^@oSr*0WC#N?Q51)jB`+)Tf6t^Scbky4Nj+colYhWj1Ic^Qr2T4R;`@R1csrwB-caRP#oc!3cZQut1>aHc2L$p%ycp{q}KiFIk zszb!Tug%p*8#mrivQW$Dv+h+G3Y5(hQBqwioN*ld4z-_Gn&17-3Gf7QO2mDwn=qz- zL(~-R^*Xv)GgK2;x!jVPeOQKH|Ce`WPd*BOhi*lm>Aw0~ zCo;;n(m4M;^*ih(FdVIe4I!>neRRf;2E?;=7wUmTm6nA(n48MhlL4l@oph-F+ZIxq zJOf)iGjn@LP<{)0*$o?>{O50>Y#{u$CGVgpWgkX#bOryc^76x@y>{#(tQjl&f|4<+ zsOo?T3as4-Wfrs~W?7m4zSX6IX3Pfu`Uc!}b?nz~WiN`g=UktQ{%}J7MQiy()43g+ z^YE?u27TMJYP1+q_a4~hjYzDA=6y`|6-Ctduq}T@S=WUkMiQ)KX^-4|x9#%xNdby- zER1oQ$^&~9h<@LCVWAQ8btj$}y>XE1oADBzPNsCuop{zUsCzn>Ry6}{rg3rx`)8AK z&a(?;)~No1h?n&R(Mv>2_3ZNptt?H=JK>&JNK^x2-UZ;2F}~M8gXPOP;95z?GBv3p$u7PdLiE3m)o}Eh86AEZ^hiqc| z2v44mxk3|5$`pC`EMt_QdFgNE+RR%CJ;|0%r(wWjj$qJEcdZT&z`{&G0xlxvzTLnh za#j+M<{#Xg5Hc4n@;Ddt==K#&~E(vg>+EAYjAvr}4`S-C9fDVEAS_&P2Vn zMxL4e!kw{EL&%n>%}|RYfcGm7u~g->SMFpi@++NF)CK8!XFulBjt1mORdt8(H{eKp z=ut$%xm*Km(#%0&Ak^fsj;`ICn7a~5W=gB46`bX;)h)V5utHP<6PRODkC)_a8RMQ* z{(X(>jth|uA<wXGi<$p7FxPSK?)_Xa`rjrmBkEp1(F z&d@g$LtpJ8Vb5`eervKJ|9rB6jZEPsj=v&{iQ+Fj9+!$s_U(C+{J7uUjINv5sw7Ur zc{H)TY}Z5lFpy>xK}RB-v>(Dgx!LmJp`-eZTZGy^cDAo)U=pNp0vc!Q?q?&ZW07S`3(>@GN^kJV2ANRrj|H`mRiRO++>UxYb+M zk={ju&qM!YA0U=(p}Uw?{k$M4B6Doo?*Y-V>;Q3=EzF%kA@Du70o4*|imd;hA$UMB zZfA0ttSU`x28C~d^u{iTaP)n5{}QDB83oeQMZ9tMYEspofR(;0{Aa16lBgdFXat-S zodQA1ky5Aeor5~K@o%Kk(3b)6$KVS0?!o?6T{3CEwYbRVY^ZVpK$iF`t@Xik-35q* zAWGk*|7KprX1pa8_9M}j_i)*Cp6tJbg!}1y>^X2?)(L^Y1%~8w6_>75ZzIez^jyzh zHc`f^+ywCT--l46&r9KLcOGU~ zAm!gLZ|8f!3gz6YM@Xgd8bK1+Vs|h*S~(VVkwY_Dlmt2|S$ZDZlfZCrN9OWpGM?n1 z*IQ&UqU!K{k-B|9j=g@|jM+LXC4D!t^}leebg1ai&P(Q;y~X1U-T06;tW$^oW z7~b))k>lGKKviE5gu`8{+y=h){Rqc+u&A+=oWEfwrS0!>5g%3E(6bD9*Qu}p6LPu; zRU1bQz0leacP@0Aai}Nf{iKjTeJ-JpdHMV0{viFv@MvxEQno3KrL8Ku5Ov`@z~7r6 zoXg!+3C(&E$WSKDJ-Tftk!gM+YU&=jvo(o3gTb|=Dv)urje*dYg$prP<|Eu|fi8Tv zdHV}RQ={xgpe$yS3`vhHir_=PM<5t7EfK4&#Ny@}eUAsNpH8ExgsQz}!&dW5hk5qk zP;1+B===?$8UzKpo6w1YIMXjVG)o~+2RiJi(X(H=Z}%j4Db*JH&~F&dzoV!_tRl*K z=DUmofkfmHrdXhmRK=7VRO@(2LTy^4S}_A(c57HeFWd2W39gN_&JL4?kc%^;9>#09sc@cd;H9^U~zt9LBGD=d;r?C@y>*PoRc4R#wkU2kznab^0ILi zEEzG(0B{AK+ruTuvk}}{sx?mw1Z#W-zzaj1ZZFxq*P!ZcWgvgPPZ@+Wm>-aQC4h4g z#N7UgvO9=I2Kem^my6Qv8=1#vh$92xaLTqTeX+Vm(cD0(DZ4mhgdoi*eGCra;E4q? z4V%mpc`jI)Qqgw?>F_4ksLj{ki~%_DJayw|{V&5L^Swc#C)aHr#M4azoh-|ZLQBDq z>_~co7IFRk18WV|1NFrEZk*U3#OY0WM+5dQ&X}2ulGWB3JnY9>OPnYFwu!jy!)W1> zy2b-{{0^UJ02NMJrgHXuLX=}-mi-u}jrlKx@IG$lHzmmaH#I=Oq4b1snb&-86OtfG z)E-M^a=!jrSOXkiyee1O$NsZldk5I?3D#p>1ZEtHEe)C2$j<4X{6@XUcpos&c=`WD zKI7$rzJptd>*Sip{)Yc22v$n}7I49ta(s4&y7oa9QqW9fnfakPj?4x*1`~e z8DZqc%f#>B2!ZU7Os0A6D!&9fjTGrgqTW2Z$=e{0mek(1cx6yuw2fN5%4-YimmIWR2N>pa6*F z5kSAJyAtL5KJz@=Uy4vq9KVUR8Rs*Agb-M%e5|kaYCB&Fetaz#1ssMhMFHdtBhP*;g0BCzA5O2JW6OiZ*xTu;y#(@ZnESnWXvK=cP;KuIoP#Gt&eKXp?fx}1V z%nitBkTb{%4h_+oHrC=@33d}GXPx~n0-T=_84&Cr^Xt4f60MIC$H zS4Il$;$1xn$l@cho(rLA#(#fv6Okkc*R`l(nIyzaAA%!}Ys1g5GoT%kSUiGnORR?9 zk3cL#+DV^y) z;;wA8V8(NQPTmF&KQ)L|X_$0%dB9MaoY(ig&xHg7sW4vH5wz#p=lP9P=3~p#GoHv& zM_w@dYKUO(!g%qbxRFaVh^_oEVFy5xZ&g6!??HIF1D?~WN1ti=$=eWM5`It+SR9=D z%*i`iGA7BE|ACFwVEwW8$$siQ{(sq>+NYDJkta?5+1Q~PPeHK<4J?Wv%S=fm2HV!& zMIo~Vc><3O5?~>}EY=ccai~0U=FUIVzpyAQ)dMm*+Wvzy<|_$n1d9_I=lyom(#KD9 zkiNf}4WM?3MdG(;xi<|gY3jx}x~N{(sb26O>e42(cagoU71x(A$;P(t-LLl==PCEO_=ant%p}UC?2#>6+bvr$b$!rEkCb)fSu#u zhu+g3L##~iaU+`lj>AK3bx+Q7EudAP^cq)&&Zlks*pu}_4Un;%`zXX8*-XY1b+^Sy zE2?saY(xJ*$&E}WPpuKyfblLIT2|4tWJv)Q!rnLEtd|&IFA+RtT?WoA{La{!EY;6_ zAhF}8=uh34F$N6In4#9>N}hj(^-!K+4I{4AGZ5L4+h>|3N;Z9fV~14e^&Rj%IMUxl z!rXnK#=j)FOl@#btBy&1Qom{_<)tj_S%Xv3<$H!ws4j|`fKQcE zWY-oa%%o)@ZB&UG=B|KUCwxp$}JSC)lk;ptLD$DDlkjii35s zyDpd67>oIufpNV5n|Q0v22G;M)Hz-j2wP@p^jp@2B6^K=i9X^Jqd-dVbwS?D^PInL zaxw0I5MX-v3dI1g8#1PlNTx1t-Uj7|NwbZ9l4W!D4Dy4vM(!yxsS`Uy*}f=gZbXC; zc>DqGDRX0+9XZ$48AjJ!`P_0ZLKrye{@g%f4KY;6wXN4J+{lO^p&mY{$tufAAT3%p zNPWTaenK2vfdVdcX7@7=!}NE{TJ}8NrEVsfoHENlDT_%~_j&?{o?PnUp?UXN-luwS zQm=xPxQ1NcB93=6!n)cwQ0)%b;n9ma-+2^O_TU3#Giz#%cN5}*RlP|H>;W-O#Mt#@ z_uHfCHIh$g81M`QxKv*n&T%$lge(z(X%`&VI?&uiM~liTFkbIA>)(3Ex>Z#Dgniw_ zOEtB!a3<#IBwMf!Nrc*P>|2wIS4Bfv6%#N0IBROQ_Q;-4i@JsGBD;_e;2A38G)Ht~ z%4bL5)@3lFBKD=+xKba(4fqkj)X%MP@2m!Hnv`}I+B1jfGNay+kq_1VkZRM%-d2h0o#Sqbck3w`D#skM` z+jHj!P@UJGzkqlYFK!mJ&GdT`I6z_squS=d&-5&yzpY4Hm;l#MDJFgXTYwnKHx3TE zIH^8LN!R(qppjhN%yteRW6^cb;pkwm-~z=w_>JdKXKyO(PmC4CXG zbgO`3}#*f-2zs%i$6bXoETv#+RM!t4t5Mc0W>kq<& zt4hyP<1oDT`{h2Ls)a52f%Ek@4)F%%!#*%xeR&cX^^B!<&KrS-(DzM#hr7HT3nNw% zg(i01#%<8~2S%(ShYMoLdYf zNpX|P-i(jYw0)0@EaV9`dn3*GW`B{*PoE#a(gHAni$W=JFXYyH1Tk6?nYb8N74!{N z1XJ-!-D$GgDBOa1B_Mj-YBlSx)b&l^uf^Yb{Y-H6B?equ>|B5`xFGxVI3&I?piO|; ze^0;~ur3C+agoacaz=0eM5uQD{&1;6n0cGk+@yU425h3yEo#x>Eesg3I$xk5phRrX z*L(7ni|I=g%f^ODU;oB6ZUrfx=Xm`AFnQ=2E&WnsnldwvbaT;@Zd09DGUWq!p%SZO z8%)1zjQy(2Z}JMRB#MmR>lKG{r(tCN+Qha;FU+RP$-L`zcqfYnj(&l5*MgOCS3HCy z=LoJ{P%rglXeU2V7MA1XuJ4~3y*l~)vaT{HnMRpz!E-FS*%eh|nl~2#dE1w{i4_a7 zloT%nym;<{#3D8gYoTq58Gi*Z^dk!;metyZVTPWZo`z*-Ld>eloU{$=#9D#|nsBp+ z{9O@GjLKPlAHZ--G@SBZP%eXF7C&>?xe$LFONNwbQD_z|0}Iv0LI4NtNI!72a3;ynA=ku&1!jsO`Q1L1?)3cOt7vOorg{&s+9bWf9nqPbNyus&@x>)L;*P(^ zvR@maMqbB8#f^2Ad$Z?61;ykf38pO)nR2xb(ymOwl5f~EB~uxMd>z0$nD&u(=C(6E=!)CQStuOC9}tf2>~$`b;&%`U^Fw5hXQ zE#+Ak)Z*>nF~BYv$j(s|`<4~-&g#Ul5^dw#==kN^L_&Wr2pl;dZ!%6`SqL<_xjn%b zmar`glq4Abu}50r&}}}?B&-=g#iGQ6DGa>M!+-)O%UH>sfxz2}e8hEA^M ziT?T4O*yl^(mII^`B+~{_5Pxk#n?IPdfh#Xib4RQIb0`JY@lcANP6ZRJ0^xKv`uyF zPNDh+^e`D;B}^oyj4MI#`*JpWYV;fhuyLlNn8gcrVgeE-TxDbCYUs*b{S<+HgVerL z6p+CtZW}Y-uL0o!=3r1Id^V5mz9M>)pstqO_Nl%>S%t1kBtHOJ5v1Y0NMI@Zn^km$ zP>U}j77L4ZPTB+Mokf1D;hMHdgdb@{UcLf+Y%{URDtRQr_j{iUv%nDb=cZoND-CR; zg+TZ0s#`j}%9B+|CfbwM_BAM+$UGotk}bxEhI1B=C01^~=h2lZANCuppq?Ubn@Iu* z{2=@xat;1yESAEy!Dgp54Vdqms4yF}CpPAR5G!+46il&|*qPP#K$LlW3>7^Xv2&xy zB65oyD3P1bVwdq(oGJaiy#!z}7-9s(oB-XPx4r9YJ@M~wQy#it`$h{f2Od79sj|M+ zAu?D`Kv#7G&Aw^n3Wbx!x>avByewC=R-du2_)`;K`t6=liTR0CzdSBGCwyy;>2!TQ5&CaoeyKm5q^+c zZC{>bWxG1|-3x%0S~iN58;YuM9h|i_2`wQP1U@x!Z_KC$k{B`8cfFI^5fLah;sR>% z*dQslkYv_Tf`+2aV5k@YPUBo8@^D0v80eSYkj>W^mU_r2;lsbzuSQ1xN2vwM||XnVv_eJigjRP2u#SRr~|;EUiUx9cH;@2E6FlJ1#CKx0Ygn6E!Kg&o zhD87|=yOY4r3P~8U4*ebewq{-plm@1Ls^y(n{K zRUv&h0gYdBYJ>YZ&qp?t>#XioEEwK>KH`wiMD<{WmU|tCJk27GaOS$(CWT!#RV{(y z;TnxUq^D=^)zX4!aUUj?g zIg`z|+xGX+zmfZ{&EL|D0E$Rf$~)fwVuL(^@0b#DkI&43OK|(0G)&;D-|x^08VETbQZX>VraYx2(qJmQI4 z8c=KF*pyH@baxy;Pg$=gch}TUjC(HtR|*~KZNuMvCZMK|fuSv_jdw)Qlcw? z6H$wd^GN{BHQmT+zCCmF8M7bl?9tPt&zDkdiw&G|{YiPQy@ROL6t-)WNqvH<{)?H4 zLW+2;MGcwq9qGE(au|NJZA;N>B1{Fg7eaFjlwe`B1+4t2FSvx{p zC%TM|_DGPp{;BQ3bYD{lSZD~ZiqK$wvPO;l9E(#5lJotc0g_<1FmLAjufJfF?ZV<0 zoy;+>e~Tbp5@X`A8D*;R*)8@fsP5l4uF3Ma6I4(V{@9aJ6=gK%T^I)Q{GFzZTg_lV z*XQ}?3n%6#ibCn!F*0sGfNkZOWluAEkqVrh2|zVr;jN zAGU-8P-fN2{rhd$Jg4g;bzpe|EiIpUj5je_AUvnlfbzhEoRJvO%_<8e=WSBgj`aWN z*^?(SF=~cc7+Dqx181OUp`UH%H;RFbBf=l%`{k~Gqr@O{5LY?P^*rd8`i!Nb0^2fq zU#7kj{GC1e-nMdkQD~OOBz141mk=-GAT5^G$ew>Up16|Os>{I zzNDb9h<EGN#3LLV#*L7mDpj zYb$&U(x4&8yX8S*^K19Ff4&hdAxxvrqLtqW!(fhly!}mX?CT2lX^?v zbgt7XNm`Yk>Nq#%3qHC;&ZNB9%7&bn1nKpAa0LsFmOisvgbXAyk>Bgu*WY^K3WV<; zKFK!_ykK-v=oyjzrC0>jkFE=lvC66d^X9-vha*vE%6;cUf3`Z#+Fv!h5rfDyoT(aN9gzgVzSrB=rmT zQiauLYoIo>fm?QFCu``H{qNfsd?|9}mT*?xs-c6!M4!jDhw%wnNx}_6+deIVpeGgdAb_Ce(miCWqR+3zmK$`pUwG7 z+dH-^0hPapC94oAw&sPF%QHln>IJX5s&K_}4t`mfz28H1gf?_0Q*?3SK(2sAC_Ik3 ziDc1~y6vB+{rbl11?pjUZzOnsw_D9DN+wO?ya94COxPnT{V138JrEc2RAAT4b^01<=CdH?RkE1WpB$E=rrpWNcGh{%w>;@o59!E;(oHN-qu|DnSg zCU*LlNB!a1(i+9HvSE4ZZ}irg(Vk$4+~~tDkGK(lT<9qG5Ba5_dJIYMTYsPH=foLT zhDf1fLj^t6kV>LJ`ufJ%wy|{+@m+&;H)4-smLy4yTy+y28Go2k)JZq=lnmCE{#57m zJKVpghN7K%m4V^@klQU)yNuE*c zz{}(=)zNX3jl^g1&qv3Pp@3aBP!p#fN``u0;pEzDnoLw_)QTRin;9#5Hu=a`N}lUV z@awowp6`ojw`~qCyi7?b9W2qgCqkV!lnggjTa?-8PGWMk-ei{iU5J~M-=tRuAbkA< z&Rrs>IlGkqrT#K?hkbGoSnS21e6_eilwj(}P$4n0FTbK7`j@K~vO|$!Z#6#%kUjY8 zZ&J%bqknqAqDKiDJgBis87Km z@uDUW<0>0c9S7YRLj?iHe%-)Xg8NdrS`0N)TM5<8Db1t1BjacQEhx{XO9zMsRtxD( z>bSP9SwH|fsIcy>E5i_zr<(h>EI#(-(Z7il*3(rMw+xM6nFbW(7D2Hfn9leiJfakHg-)s!e&@4iq3%hxQ#WUWj*>K4Lw?0sB7a%wSz3`D=vMzJ2bFMOM&J0GN32p+A;6-vjMU6{xxWDw zRx-q!?+HpDk0G82(JJTr@&9ZSPhliMBHHa@Slkvebg}4H-!1o-;)~h(42=%mF8PVt z@)JZxAo-x)pYn~~`Pz4FRe#OE_b4Vx!uw0K{1akc+DG3gji`94{FY-3aW_1pv$1Z} zCHiha6)Y`uD+#+LUjw!8j~S7bAwDmAIO!3cT=t4HH(={%1z)=4&>P4NzD0zf24IRD z*A&bC_50SWs*9E5mba=jgt-)JdeSi(;8{ zYV=ykk^qAe4KawG(>KVMp$@!lc?k!?QXWLUz#JahBPgx!Udup~)X8s(fJy!sKYD#gLK zW`aav#t(>6-3FRaR0Ti6KpK9ho%=-xE33B_gY);}p&89@oOme6ZkY>Rdj=!X{fW=` z1fQiJYZ;*dr}J`;xva-+ao9ffkb5%P!<&EOvf`FCna)^b*Jl)gO_9SBNa3dIkWOjK zZU1IzeVO2UR9h%Z4<**CG5h@(M`XZU=>n$zB+>(n1~7DIjuqgl6l1Xe&N5|E21qlX zTTQ)-zUmm#vyc=GOb=IbjG=ErOB#tW*~0S&y=u(H&`Bn3)d0LRXb_~nWCF<0v~WbV zd{eRu@4xVxV}v^IIB;rI6ayyNL8_BxW_oC8o+oD|DuqAo!ZGhX-T(FZ~x&Ulz>|FU_`PuW$azuSyvsoWXDZbTmf8neKW>+Dcm#g{1( z*LspNjnsk;$h6x%2DWZEZ1xH+*T5CN+Wl;<-!Y#UtvHt~fWI9TCG_Z#T~txhKZCs- zl*GUt>KRkAz|$yYT56o%lJ7|r|>SvFG!j^7h9k|uTK+dyxun;D; zvun)@@p-f{M^yWFP19od_8*~}XZz@u#{4-r!ouZ+gm?Fs&GkY;Jqe}1A>eON&swHG zYBRTQoBXviz5GjI;x8p-pgRZy_uz9ZWLjG6{wtyJFBH1)T?5GamXoZn`Iow;cQ&hm zg59ZNsI1+Qq((#-EdPBta!n9RfMQ}BkaXNGZGg{G4nE&u8Tp%R)T)mt>S`0p@oo2=FR+}1f2YImUTkY??kOPimN0g7{q0B^vz z;1?{JH3P;gq2iJXoDZy6Zq!FA+Gd~8l2r3#j|y_o+)z31xW=S-l|?vH-inz-bDJ9P zoRuy&<{SOlcN;8jn~D%~4}Ssj!FpZ$(Y$40*GF9Kc~2Y||Dy+O0nxIaxr4uG;Us-a z@xQ62?oc#*)`o>+lFTrb&*+K-NkrIc5;3UV8wj>s3CSBzSyG9*Ms|h;<+>o)jI#d^W6q-8C2jh@(>63 z4D2Xbi$^%2>t)osVL5y?e%n60m7UDu`w0Jg62*uG32N-Q?$C;UJU8o;FtCh9Nf14k z?L&&6M~p_Q^#vy*+~RQoluPjB`A+2wJiM(2=JeS&TTM9#I;f64{tx3-;v^-Tyq@*V zbl^#7`#HQf^9#U~hKBwcYZF&rrtXbZttAlfj-IR>Gp^&UkC2iwTTbWEr`wA2e1cN04t!5N+l5aup4 zBdWC8Bg9T#hQ5Pev>+;&*d{M7$wky5h3K_C`zVK6jDh48Ari9GFfH01C=x~++!rQ| z%__lQb{RqI4Bbl&yIZljkceITFh?l+0U)38q?h4z020$@mmV`1fkOh;69Hhctxy=W z)Znv4up4&0E!4aCU?2ytK;Xoqx5*77Ni*ZdYu^XzXGzaICaQY2HTT4)5!`ZnpMj%Z z*E-J46xpSUYbkFmTv!S`oo_@c$*RqTPe0)fA}#OHAjF9 zqQB?+S(Iz2qJ(mKA(IMcx%&=jVIQ*W0c{I$TEecI7D z!SB-c>41`f&RxLI_1%{(xk9n|8kOq&2e|u$L@_UoWbug(<8!dVmpGhsw|U)&f;0oa zdDWV;Z(^C`fX*eXlRl7Tu{g4<4F~!+?`|5JMw;Zw_Y&K+j#B<84TRo);}xm(Vy!9E zcWZcIP+_XAETxv%?-x?6PNaH77|s(Rt#lEn(cyPVzIv$<;IiS+n2gV42rVmwi{t}zD$ z+_qxl`FoiLX;;^~Ib^al`WA{>U_T5PLp&n*MA9C~?ny@oiRc}#zwqABbfi!ehT~J4@-m?? zZ+$!VceSECDcRbfcoWQ0z-ru*vNc#pZe%<%qi?0@L3eB)TNn+ymY`$Ci zJ@3DCt#1WO00`Ff3_9sh?kL@9W@%!NdvhpC6k)zM3buV(FFz)n{9)2)vnd(tAeC8D ztE1lEa-H63(Zs${i=6-4ugg4J?6F};sFD-tph1J?AFLG3laFFOGs(UGa4K8h0#uSr zyZLel0F7`{Ce3vml@Ey^B?XEnx8XChT{BAuDg2JHa2~(veX{Dl$xYg)N}O;cw5Je5 zPEuD=<=wNa)L9*`Yf-4OhE*p`-+iD)VkGjN?sX|fp1ddNw`~E=;`M#AV3vfr z52NA|tp))6`_2=(zrj<6D{bPTLbW+&j!;v1?E!U_#L6d$vJ+{vco^oF;&)b!Qu_}1 zFeL{oIbNcKEMDs)M=o_6Xv2gf^o0|99XPSD;#S_pX6xA)Kv%Mn%x`Md&=L@L=K7b- zLOTZR1e^m=WnCB$3tZ&5XakCi@4y-^Jpopk#fi%{;QKbjwkoV`zTGZPNE*eFK*j6X zKs5mX-;;ji);)emFVpQMfF4SyEk>6Dcv6v`kSXqc7{=>FO?*pe6|4D&&J`yVSAWYQ ze!l*#d1|ERM92M^U^Ytw*7$c=w%dZuU2*e;jXBVd<7dJZtRnpkX>Zxb5I1u(LLjrb zm9DG{^qNnxB0`GFz0h%&rm(r$I(_}7R-iF5*t6f*wFCd<0n`dhzOZzRb#Rz*drBOD z7SG{zO$dPQi->k1sLv*&Wk1HeuYh0n;iYZ!upC!%1lyvQQ9oZP(^D8s=9p9?hc#>H zP);&h=J+E{)9=SiDER|~I$aq2yu{k>XZ zFI0cZwUBJSu005tO5jvwQ_bvjqG(h=x$EZ4>9nv#a#V$eGpS}J_S^y1yIOfRcFYs` z#(i>G%HI25D8$5b*P}cu3%_>KSJbfDuEFP;#>snx_GvYjYoDlb`rEma;Y(!Na5rFl zYZn<6Wz*4gRgy~F11_IDvW7v&LW_x4Hmt1^Gwq@TN&jDb+_f)$a+zOn zk2}VNFQ7pM-jMf{5sM~3ep_8dDcTl%ZbeU=TtsjqcqgK>>D z9O4c3qDIVhtzSL8*EW!zc;NMiWkay5N$*B6IDY@)%tcZGW3yT}BA(>{dcCpaKl@At zR?E8)-hD4F*f)XRJ+UROMASx$7xsP8%1)^{E8=k+NHHP70a=rjb%p4yo-=kKi^$xZ zLl9-3B_}#+srmLRsh7h{p++*_9SY^CjbjYz$HIs)tvBjtc51%Fw(kg%7s+FaG(GEu zYvx3=1)C+TV(v{f(~f?wDS&pwvrAR{k@nf4kQR*bC55{`)wa^!ASHLLi-S_KPp6ikoVGVa{p6aVWPI0Aq zJ&1YZU%Vq#oxIPc;Q9T*6dmxrwb=XPn-!!htZM$Rx?aBrqGv=Rv%~!OX#{rIUenZl zWyz!S@dQG4gp)8{jYm>>&qDrL47P7LOd6Y`-({>aCX*LGg@>;}A5NOscW@B8N6r!i z&-@*E(@zEKh_VX|p zjyHrY3oNZL!_{D@3~JjG=k~XvA|mV?W9Ip!=0#r)$F!blx%Hy8dc$yq^S#t z^xo7kg8q_Lfam_0pWG#%%XdfqJwkl1)6=TZpCGi7(%r&qy=mLh7iRzQ&lh8^rPXeu zE?3VnKG?6&hm%8jP#s1CnG77@n@xQ`6B=;D%%U`o$xi#u@rtpxX%qJi0AWC$zk~w_ z1)bK@4(xU!z;4_V3L$s%H@RQyFeNhukD?5|`LcnW6f)}!E17#8MF~tX^bGZS@!h^( zXo5q(4|oUstC`iq)FM|uufC~o&{p7U))9FC2P;efw5F1_5D@-Ctg#`sgfG8W5R?n~ zR;-$sXfC9nzMx>=)O1>uF!c?(QQ#fypG<8n?Zk7F(I~)eSup|@9gEpPVdmDmCG_kY zfyNd}-`1`WQ6|9cJ5GEKVbUimY?Hja6)m)(t6z1a(U1%L9?6m@$F&Xw(r@AIR7qzx zGEJ208{!Y*_ePXSUJ49MAVWW|e^_dvg zmi7rsgaYq1tvegnv;SVlc)E!vxqHgQZ^>S{4wX`6$fr$0na-HbAt;s$ zYZsIKO6_9<;;}?4qN14ZzUhNh+;pA3mE4a$7Yw0#fJfzd*Js|uz*oYtdYA;`8-3S% z;QPazIIm4UJ7qkSH00`_=S_rh#D-9QCH3RM8B3C%3JJk=TUalZO^ZHRL6NpUn|t=; zOWK`9_qai3uO2^cx5=jc-mltr4V1QAsrdnElk_wGNs8a=+<-P}!|Zx&;OFsmR1dp? z@5$nGTckRgF1xs#t=izVfMOvc>K&w*van?LUy`8|zcXfBo2#a5k8h!s7BJSpeO-6b z#zl#yxU){@4wG5Tfhef9ufjL1NmqV{ZTjvDdlqEC#Y;FKDU-D(8#1~ayy&*EXYXHn zDzfUKiTkxq=UXlsE)dcsrQL(;rAcBpPPr$bvM%-;=}LiXRSG=IxiDZl__`6#@y0_s zsbS()l^4Gc|Hid@vd65g62<*!xF*VHve{^dwV^_7>(@e|l~AlkUUJFmzfQa`C^kvvC0HrRdU{0g|*ar_`@cE#mPv%g&HM89%U7f^_QO>@qUS&bUE<4@pn$ElO`(VpI zxgY6nFKin)ST1MqTF=`z0`I&MQ5d;<3%GM&*wDDrz2^^n?uqcyP$W>nQmftPm#%-V0wyGefkt6l;Im7D7GAO8`5nZl3HH~09chkFfY$qyzf}Wd)SU=|wl4om zZ)(L3f7muS%oCTeJxkgAD1*7dNKf4t+(5ZD{LNiS5OcP7G<3(&b?!)%VkS$eXt1|R z3L=O<$vLPK)#dc*LV$zpZGOBp5?31-Hu=E^ny<4XE0WyjdhF@|C^ZcT#t>EN{eDVm zQcnxrGlkC~@Ha)>78gdzt8b85l5I4#I5nR2g{`Tm+v*sd+8`*^!GFTVwEj3lJ)YO? z7+%2^5qp_Hw={s2slHVrkbHgC_4!`u^4Aq~j}*Gc(G6=HurFuI0KKL0`7M;S+#*Lz z&uo3GGhq>8X3p?W2jh&6!Gr5_k96@T`0M-0Yl5A7n6=OR!(*7aliBs6pkDi-%Tj{e z5V_*o|Da2<)cg!WHv5|K$t)dmO(A+qV)`Z+&C-nhNNU80`I%QDxe!PCt&vfT3vnit zPF9=J-?dR^mwdGA6`Q1mJ_%y4J{6{qU+qo~#{9wra^PV{7~&JCWux!N-=a!oepB5p zf>fy8M=k8xe6sQv5fyw##CyNfVLm=03cgtB+UBC2Br+hJ+8m{>&!s@M7MH(~HrwF6 zVd-5{V>rlA3x6gIyafMO#%(^@c!0!gvRaYv`dXOKgB<%^vU})z+FBS}@E46>dcH|B zLst-e9&b)ZHk<$8(ph1%RI{VAXbZd+J}JPnyXc*OuQp&w7VGe2Ay3}J8;J@f{M@i| z*9}pF=@Ij5Z#m!H$qJJ0Xq_;ymXqlQQ$uuI{pNL@%EKN4RH0BfU}?_;sCRc}3C`g9 zhxH~yIQY88qIZ>bBq5aKErw5R;(4U_uaqFO20RljGLc>I1ZQ_Q#J6|tkD#bL#a&uh zw9WkKao$lms~#_2+IPPJg0&=l4ea9doJUiT;?GRp>IWB6y5%b0! z6-b*f1Kt^1`=_?WP!oSbJy_ET3M6bC{BT2EPrxpLoV8*3*L_#t&2#bA^>%qH^x7}E zXIj;Rxz?hCJBY5YXE0Io{u_uybVc^6SJ`JnKy?RtlW3!L98HG!z+T$w^GE%Y4k-o&b|BQfvW z`6!MBwmHCP1Q$4#^bzl7c|B1gJR@D)FS9vnyhPG}(*iIDPFz|{NFHaiLk9moYH2}+ zpUH4au**1(rl_Kpz-xwr@pj3w+?CnvDK^i;tHtoZ@u1oq&a)(Wv9{MKJdf0K=2b}G z%ALTuC!&`{SF^gfY-{@fKE7i=DtzNnkpmIl=dS-~SM|O7NSe&VoJsuBB*mYzfBs>} z+`_~R*PBjc&#L*bx`0qYa69Wpff>Lp;9d6mW1JBU(BBjsI=eT?{}=zK@aY9oh7S=cYi6yV75ZsPUXXWHVS=4Wmd#g zTjqx<``%I-Ox({r8CVHAGT>hpx!>EranYg)>M@kag%1lrl#n6Q%~{u%#iKz>YOX*; zY=cdv1E%r$-O0x>4{w{Kw2s;fhxe?5*tUPSWrCZ|bbtrPZ^~>#JsVXWygw_YR0+Dj$e&nLVZXT=V_kJV~}(F>LjP?tPf7 zjEs3AJYK`&WTL%4pL-j1XAqJ8gNy0bsSdq34Z|mv*FA9pD9!-J%FnoMZ$A}U?hlpu zf(T&#sm{uFmxw&07PKW(*T2Q7{zE`8Se8WJ9`xP!TrB$$(N$}&*Zc3=i9tk@vF!AF zlO~`?govisu)n+D((m3k^r{?|bqULWov6_ihtCy~3B`18Y;{+Dk7 zPQxWh(YWK+A?AtO$#AXCEaj(UQupybV4Ci8e?>l9GN@t2bD@hpspkuzx;vFcfka zW{ioXg*-ZQ7YT#-aipqtv7i8ihw?8qE{XSKc4k#BkM(*|PkLfSaQdHV706FZgm4eK z%a0#seM{Z<*vwyTLC~&9*l6YZBC434btM>!ENsfrfBY>%c=emkAHV)VjqDV*uT@SqCou`w}8Uq;#Ib+jd1C=R_i{EUO*U;#`Ll=8&tg>3wm z{dWE1)2U+{9M66`jbM`*EaRweD9)AiFtqm?Hvgj8j0|^zZ!G_#Z)jA4*!);eSFZlS zp^%}>w)%~F<>L=mp1W4gA#Hm~-RM5S2RbWcOBQvD@IvY!a+VmBFK8iKl6J~H6ld@P z`;Vxy`I(Q@7uwM_eaE%OKA{r( zzQ+1!TG%J5=Ql-}AJ$C$?IO-&hb+I>N!&R$44k$8z+(!^FxnFN+5(S_Ld0R(Jgf;@ zd44daFun9~!wK9xxFfoY1dRxF{xQ#}#^@nY9+T2Fe$kitEf{aLhkKE)2JPQXCk4J= z`zcV%SL1U@?R^$SNfS=MVW((+e_za~cO+9Mftm{%g*RjrzT(vFgADe4-3tEn9lHh0 zys>hM>U{ix72oxr-`Lvn5PCx<*(XWN#9y+ogx6&h8=p%xSno!NLFn z?#CvBWzT+cEZECmciUt7nz5md4){=^;-%d@?$#~t5UcyFBu+Rqbfgvl8 zg=1^8ws3him@j!zp40P3DASqhT%cf;KlGQylcwMiI!|p z)6aXc5ZQ{;K(itZ+-lsK^nXgzNyY2Gd(fpKVUhy?TDN{E&$;3*S=;q){+YQ|phr3b zW&N<>6yFgOYn;5tCP4Afjl^GYivFYP-9gFF%fA1Bt&BrSW{|CL{9dQaqzLPYB)ise zG|PEZk2G>x-D4PzdW!d=u(L@?D)W28QCnnl=I;4344k+`I1b1o?9q_m+u(OYnD4rJH+ zC%5o6Kp$_w}^*^INtW%!;u$r^s5gFQs-1+)elW zwox~bF(D`2+<1GMBN^RQ=8o3>moXR0hHa9D?%s&FF}}I`gc}W;W&eZlhHKIZ>Tz7d zHl%IZBr{Xd{L3?g!qC8Lfb7RTbgn=P)(38&|6SQ85C*9iqc@uE7l)$4Cp_8Ag*Zf!L; zjeidsEgUsltFz_`UKuV)rW27mw*2J`_y5s*YsWb%KMm#1kMo*S2XF@e2pZ{5jPLV~ zG;=^%-h`3&K1<}q+-HC+&-BmKWUq5JSauB0eM5?R;>|(R7*d-m*i7}otdXbH`uGiD z{`;BQy8lFJl{CcMZufNkos<*QjUbG!#2L;zE!ua1zJEPB4GFb-W0I^1iv@78e#1Rd zcV46xlrx_Jg#6LNGxh*oT_7vw@>6z$3uyBpvTWI8s>Jwe2R#$~I;P*iyM2>9^u2^v zNfKUpB=S-qieXQfb&CQ};Xn^!$g7W+-RmCFZ-Q%%rbexSZ{%w=XB={v)OK+W&SvjkpgQ!O8HWC18lE$;m{azb(;|$|Z zE=_FAc&rm{lb?w?gqlmllZrpjb#zkfe}X|aDl&OL}? zD2WFOu~{m=AN=9>F%W=M>c1Pyszd7}CD{@0K0*3m- z)7-Lg!pg}B`rq%y`qMzh546{nir6jh0Rzs|afNF;LCA_rvnWqzRN$0DZxpr0K;vN% zx_dVoiIh+e3WrkQ(qzhy2gSuYMzWepvYo@B-NyVz-1iuul)!nhV()Tw-G#3D_jq36 z=QvtPnAi>8VEqvJZ@_ck$+56N(zs64GT%s~mTbl|N}}(!_SfUN8d`)JDblU`U6T(C z`$T#T!bcX?v#Oxb=Koi}AY;xgsmKrh7u0Rd`_Q#W-J0IScf*?WFqFQBPt4!QJb z-&Di$Jxt`mhI~KcUkv$Kz3>i5W$3&^6fM5RH)sf}ysweXVR#Ns^m_)}j+cCBn(l)d z{ks~6Vdl#5;=J?ZABwH7@#V4ixAbL;932j!UD{gB(T+hxGkXDalq*SwL+fHaL$VP- zqrKWnv0Fb|fbb=^ zgVZ?%8tj0=d&sSAzhRtI}9t7T#Vw3J0JbIq%f;pmZg zC4jUgI_+sxMUAboJ4&r|uY__~-+boq~eZWJ_FsQxrF%2=3& z9#Tgx)crU9F(-Z<%2+?hAje&b3y|X2=wXr3XDRJdEDEN0xs-!T7UYj76%}%PpkT@G z0W&f_gE^HDX8ChuCU;)?uE6)>$UDurHuz|7&NTFoHjri|VzT6x)J3q0I-SVcZh5>d zEgsH>EBb^%LvsT87C<%6c-w&>2Omw^G!I9?J3>^x!D!&epeYrJKw#WDlnCEClUYl{ z`&$22kxUKU;QmgaSxnCy^ht6a)U-a_)B;IUq8yjt$-k_2J-YZ_v|7?QHE}5-~ zmM6|E<`oSxg_ds+yx+1AZGWQ3(E}}#BRrq4_a2$MxElXpNd({2(hBHl6$V=UXK>CNc7-1l#^YmigpPw0$H}Z~lQH7t!iT(WE2= za6-tUpf~D$OY8Tn>y)DL`=)(CB|5X4_D1tfY^@~G`^1pB>qp9LeTUK(<}Q!h=Vk#0XS|_n&7CaMAbcZxZ!x zg|Z{guX#I2P6)lCMt;1pVp2~VS)Me2#rH}p%CGH6ylcn3=xRk=lNzVM(YtPr+7%BI z4Oh30-oPu5?(kulkk9qG;^kjfR)&-^v0n%$9u~n#dCd0N@Wde=rIm7Ktr}{q+F}`LL)e3raU}X4*kO%Qv~z#&{9UsRKUDwY_gl81c`yXYsx2 z`7h>-`mvBTT}jZITr?DNqWY48T{2V$&&*!VOs$Of21!w_Nf2!57zPA+m&N^RJ zjN@${_%n{jcdWea)von=63T2cZhPlp!fraw*&7q@)%JCGBKM-A z!An>l93!d5KC2z2(WI7ri@rxgf)X?(=y`e2-T~Gxa>V33jrWU_w@XY*KtpOBhICe` zP@VEXk{MJt%p94sfwdh!hMH8eJB#D{8e;j9#x*m)_8R9zOmk(LHc@nMGktEXiK^mZ zpkpQM;9jYH5xDZ$0zPU2^#yNZU3sn`QSZ@H+}D$6R{OUmsb{|*%r)7*h-Qms=4LK3 zPLE)i&)kK5SRa*?xF>`*JwL!#Qma%qa`(*dC0-$Gi+dHyhC7I?^QsAT4a@rTM|BpgPq z{1{{G_^wA-qevRO?en{0F`iNVC6+Ji?LptV;~~Y2IzpdWr`ZyLmd&1UnTQpto*IYA{Cfw&`#kF!2ld?3 zmFdZRynk0mPqvxlC{dpNV`MRhTnS>l;uoDDIdb?UUMtt%b$wlZCj^<-`ICQ*(|6xn z7l?ayG4^?)!dS$VM7v(&r zsM4QFbQ^$*J2EUkxd4=Fa2Y0C$6`R?h zB7U4PEyN;w4Jf(nZy*3KC>RsO{cfyU3G2kx*CO=n>-e1eN0RsCd0g{zUq1mDt+II! zBW9kCh5Wa|ITT{YFE9xBiFk{Xecrl#fz`f&=DVwGAhKqh5WjT1t&B>X!HdclqC^Ws z;|((RirbT7LMDMo$#Z=v%z&&V`KYp$7_VC~qB62iKoeY(Fy=|(xqhq*)wefdEK;ye zM$KaSZbX{6tM5ikgdT$6glP4w67JM{fLrQD>?wiRx7$|pJO6lb2q?F%gx1W$aar@8 z{BtB^oZs#D(Ls^K=|D?+FYFmP$f=k_p8sxKnS3-WD1fUgJEn;%D^Z|65nyF{yGD7d zAL(_{8ejF+b|Qze>&C6=2{Ts_yB+16 zADi^*P)A)(d+tR&JK{J>?ZHD+e~_`8<^bzbN9vq7G>dBGo|-(%_YRD1E)LB_0A^I< z^;{o7Hm@LXYi;&xAD@i)85B(2rMgd4#XtTS_>)0zocY@)c@%efTv+$|%6`Q}_I&r- zfF+LdnmN!kk4cfS^_}npSvR@|Qr7!|jDb^k^(JPSyDr+7_BY73Or^o~8AmUMXM_`_ zmNC>%LYfiL!b!hxz$&x0i=j}p{lQOb>*doxo_+P(2h?tnXz``3d?j<1MH9RK7%_Sp zqv$N}{s*?4RwhRx{{8LNkGINJfmlIh(z zcGLx7^PP?W6$Ja<^K|rqRh;Br(qs1V_7G!5ZxUl-$}VP`k;k5!*hMXUQ`?fFaL^K; zxhV!X3#p@pAfx8_K8ukizairf3Qo-^FfW>F3T`t8S;|TikXK()DiS;3? zwS>-&Z>831fzMEGi9HWa5zy z5`<<=I?oUwy#-g?5>&rQYqZX|r_{uClEAW&^LpKt()Br zbIZHFzgag7`0I*ptj*U!YNU8TX1*K@6DZ~q7+pL&R2=u>jE#U@YrkTN+ZIpkjI2kC zTiJ8c)(m!HzjdJMt3OmXAIyr8!jCE_NZm<9GfvoiDQi6G;Y>0amDp`L|3tsL7w_7VkGRT%pzPF)wNF~r3dK9g; z{jn4#ywf2PT&pjeX_2ov&m@Z?;(4iB#SC_V;`{ z{j;*pW5EZsG2YE?-qRN{3I`i7FlXT202@aa7ZZxoHXhXp)xiWdV zCC)+M?MbB47hf(+G=%aeu|0JK3z(GTO#-Oh_ojQ@vB$_*!M@|@*xSi%TPY9BemdsD*5q|rk zu4x=`i&;0fXVKUoMc?!d6c+~r^Bx3-C#m3fP{b3Vh8PEvAxOxQ@y9qbmFtSjIy)1$ zGcjSeMTtT`%1qzTvP1FtvGmrmc=xw`(?YDkp;dCa4W1V*T1UJoW_ za=GZy?>kVi{+4xV6bgu>2|$iTK1+ZI!8ivSn$F!ey-GSVn1C6yEUMUlBRq>f@D7#v zUZrJ(TRt>S2Lg}#y8hv-9eipv zGSI6oUfOFh)NJ#OFXg=!{!H3&wJ_G=>7{Mnn;b3%iu3J&<2DtWC-#kN1o7oS3Z$Hb=%bdgYK@cE{nw?^XHt1H|!Ggh3~lr8`Qrt7>M)y z+npCHPt*yrbx_o@3nNgfKvePY_A+JSl_IMrwK?QEUcx%#vEWM6(~&Blpy55!*^ z)!E4d`&ynCSfno;gL|w*FXR|7$P~_=1wg}?|1fZl(73+=`0D$-(21#%nCsSIO4zV` z!)g>IO|5&E*SJ^hDxM%s$Lq`u!+L*ZkGx*LEvqlL5QI%#Tr`3MlG&ts4*c0UHj;fp zwYpRUxfZ9bvQdc*Jg{YMTAYZD0CD_cO4!<%>Sy43=bn ztWu_R=ChWe_ax|(79}5T09wRovF2$Se)Dux)WTW15*Gm8x!7U-%U2cAFA>P+ zB7x~U1uX?3)dm@EXD}~=r&q=Wm1}8>t@auAB{01y zrv}+M%5ODwJ*o&@=81*lwS~mhpJuUGXDLk~Uef-3-Jo?6%cKaoh!zLx7|(e54b_gw z`YQNX=lW8oHWUpsE^vBald7gaQSXFD-oa{YY_An$f+g!cqMarLD@8>UuJ+;47RwCHf&;;(w1xM1wapV2@vaA=j?tPY{izBzrxZ zeg<}Bb)zA4r490oIbS~@>!#Fdf}a{|6O6QrU7bVn-Cse*wu+ini@nSD1FW4a_@4$z zpR!dIo^xovKKdlWi*vxajsbG*!`O$|W$>@-O5E8uG#aA5ZoW2P?Fkjo>>l*%kGy7R z$2P&$8?}##O#7&2-hPW%ps>*g^u<8{1?R`DBhKhKM~1$uE)t?|JgY1aM+veBz(`81dJ6vgKXcR%djEzh5RLFNLFi zM_&t5hV>CPmES+S^1(&}ey4`Flrc$73>+hYmO3Ta`))`Xjkh)zu#QFPcwHB#CEGy* z_`2p{{N_3@Y-(1XscJrBam5a#N~X#j3hgsm!@3$|N@M-KE`AEE{gFmGqKr1Oj?;f_ z4xDDozZ`6|>rbGT)?!v;xt;3>IJ43yA+m*HKHg0as0$lbTYzsB8I3}68qckyq?FpE zoYNd`RT^S06jC2trwROfQt?fI#rK1#f%U|!ZM>}0)~#_T{+rbZ?r2!A0c;y25<#&J zz&ysAfTlDIElbN{i{0l8Z|od4l7oB zxXod**LmJtzVm&G8hWn z_*?$QQB=j6TUUiR;7xP8&Q%B;Mli z)g*lX1z{$%(_$Od;nx0H;E5To$Re6D@=%1*Yl$p4szd7$^7mhuD`~@8)&$=_!@p3JW`>(xHx^ z#9s%NaKhbem9nsHgS<9=6fK;C&sppA-RwAKjyG}heZTi(%^j&7vmRQM?2CVf{vnd# zVIRtN=MMqs2{g+>;FeGi$D{J- zfEkGG-t4Z{iNcV40+v7b{&i0n6M_Ua(3k9)jkby=oJ}wFBg~2Y#Zq(mvRDz2T=s#g zt};WbQTDvo?`54-GRRVLz5DgIMvLAeQopXcUgs=_daI}w-^QPXl}9^m8Ee~dFU}FT zYw7|t@891XI4QY^H4Iwkrp%H1pRl8S3-p|I!@U<^{vwZ-dC4(YxW!cUZaL* zJgeF>ad^*$ZGFiV(G_Kb`we3KIZ;PD!eYJ`?I}<#89T2`)^Cjhs$wt!Ea#Y_KNyyt zp5~cnQm*>oFHXSr{uo(l@_Mpjo~hSjUWE?)ys6#6>F%Eq`-v|Bx&Ucz9!a8o zG+*@Gyrzq=rVc?M)39CZdNpOxns7D2NEL_@AL_k@ z|M;qV(2FjC!Zw7%9&oeuUxK69NvRNa^Dob>d!KHs|doK08sM|L9Ty2$yJxGb4K z?mY%y!C%moFh^rqj6Z1q5>Kcz;bHyt&;`cA4t;6?{q9XVxF`5EhKL_rP=6do0P^{!)N=eL7V-=1OV8C?HsVY1Qmlo@T&=XdE>Wo@q_r$4U;9l^@EwYqN1$o@ z>O6xd7Yq9Hd%H;pIDpHBe{hjoOYXEcfxP|vpMO~&*guPqH7GqTZ2t_gXFz%1K7EGs z5|Q64pz@yIRu{+6FFYzSjcf5FlmQtRGS)t6T}mP{wm-_!V#fhtu$&$6Qy3qSvP2Q@Hx}((;R! z2E7u3{k{KQ7Y~6TA=ag|w6-Eo;<-_rXw0X>Muc6kg?+98$oP8=riYd!)tc;@5kg2` z5C#J@@!z;Kf`1`rYBhZj-n)^|``=idi|*=%V?}rg&C+LYMd6vxO#+7yE@Ptng)U=< zVkZc!tJSr`V4x3Q$};!ZL*H|_YeOlRXK#im zWk@XF`2NRk0y!__Z{tq6)!_FgZNF=30l+mknyVz*|?=?Hz0ntxGoHyylek2&l~G zH@vBt<0K({A_MvQ7#=h8CHTXms8q@`1TdYpM{%j1XG%wRfCE%JF^;4^jH3)SBD zwhBTP+=&|;EVL`j3TwezjXe86iCgNpaI<}l=%o^HESM!ebUVrfxzWv?VoRXD$0bjd zS-yQAQ~{ONiv%a;5IH1M>B2H+OMfPo*c)*lh1h>1rDI%G+p!CO?oOyp+S=S`G%TkE zaGh8G1pB~F)VTTHDXtH9>`hczIBEh%U)iVV_c~3(!D~*AM9Pqt?kyIlaVZSj@0yK? zzLE(orcJWX`-f9EV`smr&-8}cXeEWW;#G%7b6$ck+ZfN(8B)Q^zFv8xr1e&wae}sE zth$6vWopz_ITEn*}wTg$z{*&QhP_`i`;2*^}_oKpz0g6m)I7C&udz zCny=^Nd|Q_&(EgSK8oK`*{Fod@@1e@o3$0_i-NE*W8o@cs!0+2^MUhXS$%JsBmLL= zW9UV}Qgn}BqTh^Jzw~cqcahs0{l*1=k+Y7?FLBExdJ}sDs%gy++{P<^lYkug5b5^G zx+Gdi2@>pwcZkZK71*qO1?0v!Vxi;CW@Y`-FEt2VLGi;3>4yMg@(H9=dm;>#NLAz^ zhsM@_c&Ti!?2yY8N{Ppi2b$a^+*pqROFegR;x`76sNW9>7E59x2u!~*OYB8Wa?oI;u?NXn zVK1<_1KLxY7wBbFI0mOwLF`KrMe@;o4K`B$@42-tku0?@CaOQA>EycSc#8Xp_x*%2 zi>2V5R$8<23#l-X(-9QO7Kc;lBu0f094?WRZY#+>b;+XAl3zJR+m#jOY@sDUgi5SOe;LvpH%*r$Cr~y&6QW z$v0qdR3!aplpUr2lv^7Jxi7E&DBhDL5A#-tX`qIdPyJA`9e^17Z}}&MrIy7Fu!NNj zl@_wmXvUC2owqL3qwC;8TbTBx1@Bs92f-8qjhul(@>36rTGJOUK?y40_{NqEeQO{B zjGr8mro~AtaqDI3w0QJ%^`GZY8|3(j$t&?IB0(o}rk{x=qr4{m!;1AjKyuL1kYW2f^PYni2k15^8^ztuP-GsdYPZvFf-6%I3<=A=Y{5vRN>SyN5(5Y4~5k zdWDK{fcRF9&Gq<3gBXEHp? z@VgkrY8hJVemCDrO8pt3rOmwe#_B5?^-q#j(DXhcdAw$_jBHLm7!mb7zxk7g_tYh~ zCVyN!g^T{JlnZU03Tgc}N-qRMrUEdrcWhnk9|C&05&F)c*hqnDjZG#J*w}U+j2N}a z;~5IZurp;in+u)@NNrXu%vww!uN|gxfg*+Kh5vN%(nymGD^jkRd8=+rLO>gk#$96f z8l11^1yr_VJmhG+`6=|VG?~7jY%}Fzm}S$K9YMyqR%7Y1Pnn>!e;_w!!d4a{^<%;Y777|{35ljFt*YcCfZ*kFpGQEKlAE1o>8A{rA4Sr2HS?Fi5< zVfFcr0MP!SCl{`XWz0AhuiwEm;*Z{C}Px9jVDf$3v#2<2DS#U94Lh=yDtMi== zDe$^egq!~w`j4a2#)a|QYo_e+tbB=Q8dNlaNva_&)&Ie5`xEZ9)j0uu+Ya_M|qawn9`G>PlYkmI{fBek}+7^Y( z%w|SfU153~#!m%V5~L7F$j9P948%Pzn>37M!Czb*7_xz`Q0Z z#m-Jjl5JpA!{@BJqn1jn;A&srY|^fs<`ro3J^5VfXo9w2`xGZPF705W)+W@kq^mj8 z=4BaeX`RzmPF@si-ivw&FVRHW~O|ycT=${-+KR*$V48kG;*FzS3Bce`JVJ@ zj&&sd%rqM;Ff2*;fixW@E$S0}@4}yDqP~)pc5sW-$*D387a5-ZNsDZ6z8{dFlA6;` z9~*O438@6rlY+nTHw-#lTwC%=FD!xk$C88pJd}iw*xe@di!c~tlS3SAWBj3Vg^~4~ zBUa*^r^7T`B_Aki(keXb^j~h)kO~0-pMCnE+x_#dP^#KI&K0zsw$Gz6xsDQdP~Y1~ zN?`K+uFXWNNU9H;urllhuBj2myTd?O=joY=uTK7k9(2TWI>91 zJ|ko=lQqVVlPSqab`Ch!I2+}2f<4^qR=$m--* zt)cgr9?S_=gZ7iB`mnq_^DKFf(0-MmS=O$56Uc)4H8US^Qf{sqQL&#&B1QmY!*(zh z@em{jU)%b!=>AX9AgSlT!j&PbdLj#c66pH=#XaM?|b=%kS% z`AA|B4T#P8tg{cF0OsYy(gY#-{Q;^~?khe;28MwM!?F*nPF<|71HJI@(g-vp;{%#6 z$60gH18a?yb(CQ(R6mC9DC_Iq0suc(I3?8y6mtivZc`8kB>Bv<>6gu~60{eE)w~LR zM5zcMnz9nlYrQ?DTT4!~fl_`rH{l3Yi)SX_1%MNsm91|>b*s^n=7eDUawX-U+_B%6 zX|PLf>Ps+l4*uKt0=#9c(>C+h^X}K>qHvfizy1Jf^bT+Ak#xpogS}d!AvjY20e`%> zmKq&HG2v*w*x;c-gOV92q#e)xR7ka`D-|=#+cseCG+wkgMXrbtNBn_#wU1DVTl|jo z!Jx!X+LR^DxZM1s2~|%n*QB(m-fr~S7vPC&pp)@!gj#`>xxwxF2+@9MufPqGZt|r~ zwa{wEz=Vpn{T7Lhkx_SmV!97J*Y^X2;kv-X+wF~KcZH%y-^DK;V959fPl`TSL)d;C z-|zT+_1B;1Gh3JMM{aw3T~fz$$PYHFi!MlQ3$!O`sROO1g49ebPuQdDTj95W{YbJn z+9H1GHs8mH@fKI}?xPEgyP|U^#=kaq?oNK<6u z#^*;MLvfFInS7$H%h?xV;U3lGlpWWPG!|b&xDD3;$z_>BC#`q#Bz*c=$FGTKG>@{) zWc^awf=mwKBFW#eh-f!NxhGm$U}@KWS*wHCLG@-_ic}IRKj%l*BRe%?REsC2bO;!OY zP`i`J_56XLgOS%-Ki9V+JMb{^JLpZ{_m9KIjd6`+w>sc&W3^+9tARyI#dtO&Od72u zojX|`=Z4Gi&>>47sgq11+UH!RJf_=wEZDrX%&NDbCQD6PI`->kF>62-)B2VTN8 z%Ib+G-8^T*kEFW4FKH~rrs*rf?)kcE&2i?0SOpg@t&Uypkqpi2SdccX#GoY4gs)%o zbNyl*Pw+g4>onPC#YADO#UivDsQdjPSU-uTz2nurrpxop=-Xu7ZLrrrq8_v}H7J!0 zlZ%Z5kp88uS0P%*QLfe!zqNwv`z(Oh8Epw&Z!{ni)YaF_W>4Fd+7bgl0By36OI&== z!1G+J)72k9*bKP$zGODsdbaEx?c68t%idvG-{Fov>qF%nCg2m3?EU>#rHx(KoiBGx z(3`%R0&9Y^4chyBv?R!&bZkp|HvUaAc)N$KFl*zHv?J>xp+6LD)>Vs!7BPftAPeVx z7nCfPLInV$w$AJ1v)Sbt7HsB1lH2UDPwBZT(Inn#y?sdao&FI{t*Wj z^BJXPM#R!KxeLqT4tUPFwr^9$XaOog0yWHiZdzGQe<4(+{kwhzmWI$saN)POD|PQPYBw8n%X1}oz0 z4c8JuCPhhk;%f_^7Cac_u-csU%=rVfrFA5NdfFN1_j)I{^%^{I*yvHa}P4{J+I9rQ^(fBY$e}s90-4$_gQlxkL<>e z#pLOiXG03SbFw(x$*)a5yUIMeUQN{$GbG-W;_LmO-9#m?UnPW*$?0N3(8gyzc!Eus z|A2z^JhL#*Qe=Eg_(V z=A8%!L-muK*=um^-pR_83zo=_c|OB6O5ae0UYs=hJzHq>f-&%Iduv}g^wkt} z#E}_eEU}bxgH|HYyB9SqaFfViz@_3w9xa)Ep0~mR2GH2QjpN8#kN+M9ZOL#t8{tW$Ljiy9dx|tl(&TRgWI}>H6)%z!u%sc6vk$lZNzp;^GFI+kfc& zh3&%{kXa3V?pl~P4e-9CYXXeV1rGbbqII&aNI%;=`93kf$hy`_rk5XZTTP$&pz;?;@y*XvlwtNLfluDUfnKu~yE2xu zx#^nKzu;jd8Cd?T)->sCF^*fi9WBMzdM{X4@o}ggP^7Yj&4W z^$1+6i~0q+Va8+J_1rBwnPe4o#n|#NvKHOz5-e7d$k$g9--d?)*|kCTCrd2*?FPp$ zIU;@L`Mz~NM{;|zork8c`#_awOKZ*`6C36HgX%=!-c5YNyRcAIFHsp$H)XNO`T{F? zh~!Ed{Kq^@0dIqKvzh&r8Rz?@8P?ajNaN%-c24SA62`Eru4{d0Vr`zV_oBhM?y_)S zMlP^BoTOO6CuZ!X_juEu;mSKDHV^2PnolKNQBiJXr#g$PBX7B=*|jBtGK2 zN6+i`hrY$TG9}JIc9zecsN!E^bwKG8OqQJ(sO~GW^PK3k%yQT)i4oT@-e5LHt}9)0!E@Sn$%JM0AFx?Nq&QH7lB*Tvp$lfdA-J~%aTH(=2 zkZR<3NzaqGHHNNqiZ=SBDX-t>jy8&l%o8{VakgcHwTUd}QjPko5cr@*`QouQ`z1qm3+!0sG_ zb!=*4{E{E|OyZZEgXc*%D{{eW;n3*MzLp1G4RxN)WA$O1s*^kUU@2tvcLkEtf1e|R z-ntAAt%~^W^G$1DP|t4iT+J4U^n20A`q+84$TyiMYp6|A38615{KxL6em_Xs<*xkb zod+)Ao+MeuSP4P>38=@Wu#qr-W(lR)7a3!N##D&e0kY5wlGPucKj}@Sx)u|_dRaUF zO+26c*K2XZgn9r{uz#f^e6RW$O#{Y762R&LhkaRx_o=Zz>w`0KXAvec!j{79`cCB6 zhnYn>6RzUm$COF-QiiBs*8S&vIWxM>OSSH)&xDn+#WdBnGivyHZ}J9FQTAISO?Zh+jU?GUB$5Y{D02+U&f*#CqsZ^Fs#`k1=;4;O2NXi#< z(rl_00=;UOy$10s^{q20!F3c@+Pf~9Cyn$7r|)V%T$y&EYEcPLYrZZyt510{f=Wbj z`xnti43v;lrQ~cz=#n<`ous6vt(S}8PWdF_p{IVE@%AQ#wbwKyRf5AzIdXnmCfnQ1UZOKo~V4W<44+!${lQGd>;lBnY`@9;E@_=sy)!xjNBo z12!e)Tbu4oLt+}IkcM?%pXj3MX0XLi^F{XfF2Y-O2KWP?iik<-87M9ug5>oo>C|hT zI&>l%^Z_Y8l1v7{EO4Zx;^@(5D9usSywlD^1ldo<)6-{uw z5i|GA-H~deOrA{(Tj*zlf);msHcm|^#(K))rY$TO!il_}ctAYr>qE8S6I*cx*)VZi zmR^>UqEPz(ADasHlmvH}o~%(e%Z|Q6nA;uRR$E6gmGEz+o16v5g254jBN1^R<#$~x zy9d-n6ixqj_r`50d{sK95Y8utOZg)5LuA#Ku!ngVJH2F7GX{Qb9Y-g%khLB`RGbT5 zMwJWL14`Ve0rz=oVkc+Gq#g;jDJJbm7&tndWhq2!Tr*vY?%00?r3DSv$@c@O}*f9l-gR!pPf=o#pBo=kC2b!DiTDYy0vw+Y} z4&ibAXT!77<=MzKX0{E9V>v{@?GyPxP7f{^c-R@?xoYb*UniaIV}1+)m|ksD)~Kf0 zJM~nv$Qoyp-WJh~zQve1#wsi2VzK}a=`~*u^#4h|jxEW%PCrHng*8diDGN*rTFBvq ztZy4hB;zFYna$SBBA^8=@ zQiM(mQ3&?&SiHAL_9;H;Ozt;({Y5jo;)s`~Y|F4QWV{oqOy!8Swv5-gDnYVnoKak1T6z^PrO$^vD9n;l27_( z&&*;y^1A}&Y7p&9>oEVB5tDCnE64bm>b^- z155NNO^MiCV^5n$PYYf;pe{_3sP`W(3W#=(@@t#$GZ$Z1o0MzRd7ni~yFs5l?oaeA zi+Oj(r+>@;{d4@6fBwgR_MiX$fByUb r{P+LzZ~r&1&HwS={{6rH@Bj5b{yp#e=YPt-_~-uv`V-#tv`+>AZT99> literal 0 HcmV?d00001 diff --git a/tests/linalg_test.py b/tests/linalg_test.py index ec0591ba..49c2159f 100644 --- a/tests/linalg_test.py +++ b/tests/linalg_test.py @@ -297,6 +297,10 @@ def test_matrix_functions(): for j in range(dim): assert tmp[j].is_zero() + # Check eigv + v2 = pe.linalg.eigv(sym) + assert(np.all(v - v2).is_zero()) + # Check eig function e2 = pe.linalg.eig(sym) assert np.all(np.sort(e) == np.sort(e2)) From fb17a46eb92e8d779e57a10589e9012e9aa5f948 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 22 Nov 2023 17:53:58 +0100 Subject: [PATCH 061/107] [docs] latex detection in example notebooks improved. --- examples/01_basic_example.ipynb | 2 +- examples/02_correlators.ipynb | 2 +- examples/03_pcac_example.ipynb | 2 +- examples/04_fit_example.ipynb | 2 +- examples/06_gevp.ipynb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/01_basic_example.ipynb b/examples/01_basic_example.ipynb index 1fe60b4d..52fa35f0 100644 --- a/examples/01_basic_example.ipynb +++ b/examples/01_basic_example.ipynb @@ -34,7 +34,7 @@ "source": [ "plt.style.use('./base_style.mplstyle')\n", "import shutil\n", - "usetex = shutil.which('latex') != ''\n", + "usetex = shutil.which('latex') not in ('', None)\n", "plt.rc('text', usetex=usetex)" ] }, diff --git a/examples/02_correlators.ipynb b/examples/02_correlators.ipynb index b86cb5b9..6f396ff7 100644 --- a/examples/02_correlators.ipynb +++ b/examples/02_correlators.ipynb @@ -22,7 +22,7 @@ "source": [ "plt.style.use('./base_style.mplstyle')\n", "import shutil\n", - "usetex = shutil.which('latex') != ''\n", + "usetex = shutil.which('latex') not in ('', None)\n", "plt.rc('text', usetex=usetex)" ] }, diff --git a/examples/03_pcac_example.ipynb b/examples/03_pcac_example.ipynb index 87d081be..3fc38020 100644 --- a/examples/03_pcac_example.ipynb +++ b/examples/03_pcac_example.ipynb @@ -20,7 +20,7 @@ "source": [ "plt.style.use('./base_style.mplstyle')\n", "import shutil\n", - "usetex = shutil.which('latex') != ''\n", + "usetex = shutil.which('latex') not in ('', None)\n", "plt.rc('text', usetex=usetex)" ] }, diff --git a/examples/04_fit_example.ipynb b/examples/04_fit_example.ipynb index 868de9bf..628506a5 100644 --- a/examples/04_fit_example.ipynb +++ b/examples/04_fit_example.ipynb @@ -20,7 +20,7 @@ "source": [ "plt.style.use('./base_style.mplstyle')\n", "import shutil\n", - "usetex = shutil.which('latex') != ''\n", + "usetex = shutil.which('latex') not in ('', None)\n", "plt.rc('text', usetex=usetex)" ] }, diff --git a/examples/06_gevp.ipynb b/examples/06_gevp.ipynb index 867237bb..3e6c12d5 100644 --- a/examples/06_gevp.ipynb +++ b/examples/06_gevp.ipynb @@ -21,7 +21,7 @@ "source": [ "plt.style.use('./base_style.mplstyle')\n", "import shutil\n", - "usetex = shutil.which('latex') != ''\n", + "usetex = shutil.which('latex') not in ('', None)\n", "plt.rc('text', usetex=usetex)" ] }, From 728c2e55ee971da3e2675c4bb92c61872714fa9b Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 23 Nov 2023 13:34:07 +0100 Subject: [PATCH 062/107] [fix] fixed how the filestem is extracted in hadrons/read_hdf5. (#218) --- pyerrors/input/hadrons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/input/hadrons.py b/pyerrors/input/hadrons.py index 5b65f3c6..4390e3f0 100644 --- a/pyerrors/input/hadrons.py +++ b/pyerrors/input/hadrons.py @@ -88,7 +88,7 @@ def read_hd5(filestem, ens_id, group, attrs=None, idl=None, part="real"): path_obj = Path(filestem) path = path_obj.parent.as_posix() - filestem = path_obj.stem + filestem = path_obj.name files, idx = _get_files(path, filestem, idl) From d0cfc67a436477500ae99b0156178eca735f4645 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 24 Nov 2023 17:05:11 +0100 Subject: [PATCH 063/107] [release] Version bumped to 2.10.0, CHANGELOG updated. --- CHANGELOG.md | 12 ++++++++++++ pyerrors/version.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 006888a0..34ab567f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. +## [2.10.0] - 2023-11-24 +### Added +- More efficient implementation of read_sfcf +- added support for addition and multiplication of complex numbers to Corr objects +- the Corr.GEVP method can now also propagate the errors for the eigenvectors + +### Fixed +- Fixed bug in combined fit with multiple independent variables +- Check for invalid set of configuration numbers added when initializing an Obs object. +- Fixed a bug in hadrons.read_hdf5 + + ## [2.9.0] - 2023-07-20 ### Added - Vectorized `gamma_method` added which can be applied to lists or arrays of pyerrors objects. diff --git a/pyerrors/version.py b/pyerrors/version.py index 2e796a5b..1c622223 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.10.0-dev" +__version__ = "2.10.0" From 1360942a7b863e056a016c73240675929e0d04c6 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 24 Nov 2023 17:15:12 +0100 Subject: [PATCH 064/107] [build] version bumped to 2.11.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 1c622223..ee4b2658 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.10.0" +__version__ = "2.11.0-dev" From 5122e260ea75dbff9a263bf01f9186cdd8d349f0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 7 Jan 2024 17:22:05 +0100 Subject: [PATCH 065/107] Special function module (#221) * [fix] Fixed intendation for flake8 * [feat] special module with Bessel functions added. * [feat] init adapted. * [tests] First test for special module added. * [tests] Check kn special function derivative explicitly vs numerical derivative of scipy function. * [feat] Added remaining autograd scipy special functions to the special module. * [feat] Imports corrected, docstring added. --- pyerrors/__init__.py | 1 + pyerrors/misc.py | 2 +- pyerrors/special.py | 23 +++++++++++++++++++++++ tests/special_test.py | 12 ++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 pyerrors/special.py create mode 100644 tests/special_test.py diff --git a/pyerrors/__init__.py b/pyerrors/__init__.py index 731c43cf..2bfd688f 100644 --- a/pyerrors/__init__.py +++ b/pyerrors/__init__.py @@ -487,5 +487,6 @@ from . import linalg from . import mpm from . import roots from . import integrate +from . import special from .version import __version__ diff --git a/pyerrors/misc.py b/pyerrors/misc.py index d71a2bb5..94a7d4c2 100644 --- a/pyerrors/misc.py +++ b/pyerrors/misc.py @@ -20,7 +20,7 @@ def print_config(): "pandas": pd.__version__} for key, value in config.items(): - print(f"{key : <10}\t {value}") + print(f"{key: <10}\t {value}") def errorbar(x, y, axes=plt, **kwargs): diff --git a/pyerrors/special.py b/pyerrors/special.py new file mode 100644 index 00000000..8b36e056 --- /dev/null +++ b/pyerrors/special.py @@ -0,0 +1,23 @@ +import scipy +import numpy as np +from autograd.extend import primitive, defvjp +from autograd.scipy.special import j0, y0, j1, y1, jn, yn, i0, i1, iv, ive, beta, betainc, betaln +from autograd.scipy.special import polygamma, psi, digamma, gamma, gammaln, gammainc, gammaincc, gammasgn, rgamma, multigammaln +from autograd.scipy.special import erf, erfc, erfinv, erfcinv, logit, expit, logsumexp + + +__all__ = ["beta", "betainc", "betaln", + "polygamma", "psi", "digamma", "gamma", "gammaln", "gammainc", "gammaincc", "gammasgn", "rgamma", "multigammaln", + "kn", "j0", "y0", "j1", "y1", "jn", "yn", "i0", "i1", "iv", "ive", + "erf", "erfc", "erfinv", "erfcinv", "logit", "expit", "logsumexp"] + + +@primitive +def kn(n, x): + """Modified Bessel function of the second kind of integer order n""" + if int(n) != n: + raise TypeError("The order 'n' needs to be an integer.") + return scipy.special.kn(n, x) + + +defvjp(kn, None, lambda ans, n, x: lambda g: - g * 0.5 * (kn(np.abs(n - 1), x) + kn(n + 1, x))) diff --git a/tests/special_test.py b/tests/special_test.py new file mode 100644 index 00000000..548e61db --- /dev/null +++ b/tests/special_test.py @@ -0,0 +1,12 @@ +import numpy as np +import scipy +import pyerrors as pe +import pytest + +from autograd import jacobian +from numdifftools import Jacobian as num_jacobian + +def test_kn(): + for n in np.arange(0, 10): + for val in np.linspace(0.1, 7.3, 10): + assert np.isclose(num_jacobian(lambda x: scipy.special.kn(n, x))(val), jacobian(lambda x: pe.special.kn(n, x))(val), rtol=1e-10, atol=1e-10) From b5bc687625bd3911ce0063946fdc97f13b96aee2 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 7 Jan 2024 17:24:11 +0100 Subject: [PATCH 066/107] [ci] Removed codeql workflow. --- .github/workflows/codeql.yml | 39 ------------------------------------ 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 33f1b1ee..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: - - master - - develop - pull_request: - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'python' ] - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" From df1873b5ac16e8ec9a4b10a4521cac30343a5979 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:48:51 +0100 Subject: [PATCH 067/107] Throw error when trying to only find idls that are not present (#224) --- pyerrors/input/openQCD.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyerrors/input/openQCD.py b/pyerrors/input/openQCD.py index 36fa21b6..158bcaca 100644 --- a/pyerrors/input/openQCD.py +++ b/pyerrors/input/openQCD.py @@ -1286,7 +1286,9 @@ def read_ms5_xsf(path, prefix, qc, corr, sep="r", **kwargs): imagsamples[repnum][t].append(corrres[1][t]) if 'idl' in kwargs: left_idl = list(left_idl) - if len(left_idl) > 0: + if expected_idl[repnum] == left_idl: + raise ValueError("None of the idls searched for were found in replikum of file " + file) + elif len(left_idl) > 0: warnings.warn('Could not find idls ' + str(left_idl) + ' in replikum of file ' + file, UserWarning) repnum += 1 s = "Read correlator " + corr + " from " + str(repnum) + " replika with idls" + str(realsamples[0][t]) From 1713ea146a2a97e786d565972063b508254bdd5c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 21 Jan 2024 12:41:37 +0100 Subject: [PATCH 068/107] [ci] Install pyarrow in pytest workflow to silence pandas warnings. (#227) --- .github/workflows/pytest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index b6a5290f..e6bfe427 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -41,6 +41,7 @@ jobs: pip install pytest-benchmark pip install hypothesis pip install py + pip install pyarrow pip freeze - name: Run tests From 43383acead845c197df5e1cdeaa6b8775f5213dc Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 6 Feb 2024 17:54:33 +0100 Subject: [PATCH 069/107] [ci] Update actions to use Node.js 20 (#228) --- .github/workflows/docs.yml | 4 ++-- .github/workflows/examples.yml | 4 ++-- .github/workflows/flake8.yml | 4 ++-- .github/workflows/pytest.yml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9b204da6..03ca7c23 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,10 +11,10 @@ jobs: steps: - name: Set up Python environment - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Updated documentation run: | git config --global user.email "${{ github.actor }}@users.noreply.github.com" diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index dd2fc4cf..67636577 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -21,10 +21,10 @@ jobs: steps: - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index d931f241..c6625b37 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -13,9 +13,9 @@ jobs: name: Lint steps: - name: Check out source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: flake8 Lint diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index e6bfe427..1bdd412d 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -24,10 +24,10 @@ jobs: steps: - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From b930fab9c223b3d19042d06df141a0c51fea93ee Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:39:35 +0100 Subject: [PATCH 070/107] Fix keyword files with list of lists (#226) * first impl. handle reps with list of lists * implement explicit type checks --- pyerrors/input/sfcf.py | 19 +++++++++++++--- tests/sfcf_in_test.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index 82957552..94aa1948 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -121,7 +121,7 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= String that separates the ensemble identifier from the configuration number (default 'n'). replica: list list of replica to be read, default is all - files: list + files: list[list[int]] list of files to be read per replica, default is all. for non-compact output format, hand the folders to be read here. check_configs: list[list[int]] @@ -236,6 +236,16 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= rep_path = path + '/' + item if "files" in kwargs: files = kwargs.get("files") + if isinstance(files, list): + if all(isinstance(f, list) for f in files): + files = files[i] + elif all(isinstance(f, str) for f in files): + files = files + else: + raise TypeError("files has to be of type list[list[str]] or list[str]!") + else: + raise TypeError("files has to be of type list[list[str]] or list[str]!") + else: files = [] sub_ls = _find_files(rep_path, prefix, compact, files) @@ -248,7 +258,7 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= else: rep_idl.append(int(cfg[3:])) except Exception: - raise Exception("Couldn't parse idl from directroy, problem with file " + cfg) + raise Exception("Couldn't parse idl from directory, problem with file " + cfg) rep_idl.sort() # maybe there is a better way to print the idls if not silent: @@ -309,7 +319,10 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= w = specs[3] w2 = specs[4] if "files" in kwargs: - ls = kwargs.get("files") + if isinstance(kwargs.get("files"), list) and all(isinstance(f, str) for f in kwargs.get("files")): + name_ls = kwargs.get("files") + else: + raise TypeError("In append mode, files has to be of type list[str]!") else: name_ls = ls for exc in name_ls: diff --git a/tests/sfcf_in_test.py b/tests/sfcf_in_test.py index ac05dc04..f92126f9 100644 --- a/tests/sfcf_in_test.py +++ b/tests/sfcf_in_test.py @@ -50,6 +50,18 @@ def test_o_bi(tmp_path): assert f_A[2].value == -41.025094911185185 +def test_o_bi_files(tmp_path): + build_test_environment(str(tmp_path), "o", 10, 3) + f_A = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "f_A", quarks="lquark lquark", wf=0, version="2.0", + files=[["cfg" + str(i) for i in range(1, 11, 2)], ["cfg" + str(i) for i in range(2, 11, 2)], ["cfg" + str(i) for i in range(1, 11, 2)]]) + print(f_A) + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == ["test_|r0", "test_|r1", "test_|r2"] + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + def test_o_bib(tmp_path): build_test_environment(str(tmp_path), "o", 5, 3) f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_o", "test", "F_V0", quarks="lquark lquark", wf=0, wf2=0, version="2.0", corr_type="bib") @@ -120,6 +132,25 @@ def test_c_bi(tmp_path): assert f_A[2].value == -41.025094911185185 +def test_c_bi_files(tmp_path): + build_test_environment(str(tmp_path), "c", 10, 3) + f_A = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "f_A", quarks="lquark lquark", wf=0, version="2.0c", + files=[["data_c_r0_n" + str(i) for i in range(1, 11, 2)], ["data_c_r1_n" + str(i) for i in range(2, 11, 2)], ["data_c_r2_n" + str(i) for i in range(1, 11, 2)]]) + print(f_A) + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == ["data_c_|r0", "data_c_|r1", "data_c_|r2"] + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + +def test_c_bi_files_int_fail(tmp_path): + build_test_environment(str(tmp_path), "c", 10, 3) + with pytest.raises(TypeError): + sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "f_A", quarks="lquark lquark", wf=0, version="2.0c", + files=[[range(1, 11, 2)], [range(2, 11, 2)], [range(1, 11, 2)]]) + + def test_c_bib(tmp_path): build_test_environment(str(tmp_path), "c", 5, 3) f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_c", "data_c", "F_V0", quarks="lquark lquark", wf=0, wf2=0, version="2.0c", corr_type="bib") @@ -256,6 +287,24 @@ def test_a_bi(tmp_path): assert f_A[2].value == -41.025094911185185 +def test_a_bi_files(tmp_path): + build_test_environment(str(tmp_path), "a", 5, 3) + f_A = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "f_A", quarks="lquark lquark", wf=0, version="2.0a", files=["data_a_r0.f_A", "data_a_r1.f_A", "data_a_r2.f_A"]) + print(f_A) + assert len(f_A) == 3 + assert list(f_A[0].shape.keys()) == ["data_a_|r0", "data_a_|r1", "data_a_|r2"] + assert f_A[0].value == 65.4711887279723 + assert f_A[1].value == 1.0447210336915187 + assert f_A[2].value == -41.025094911185185 + + +def test_a_bi_files_int_fail(tmp_path): + build_test_environment(str(tmp_path), "a", 10, 3) + with pytest.raises(TypeError): + sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "f_A", quarks="lquark lquark", wf=0, version="2.0a", + files=[[range(1, 11, 2)], [range(2, 11, 2)], [range(1, 11, 2)]]) + + def test_a_bib(tmp_path): build_test_environment(str(tmp_path), "a", 5, 3) f_V0 = sfin.read_sfcf(str(tmp_path) + "/data_a", "data_a", "F_V0", quarks="lquark lquark", wf=0, wf2=0, version="2.0a", corr_type="bib") From 0df5882d1f61b28fb2a2f018e3aeab4103d49220 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 1 Mar 2024 07:14:01 +0100 Subject: [PATCH 071/107] [ci] Include python 3.12 in ci after new dateutils release. (#229) --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 1bdd412d..34c18faa 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] include: - os: macos-latest python-version: "3.10" From 254a19f321aac07e3b4a5f1c33e2bc2b3514c53a Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:50:19 +0100 Subject: [PATCH 072/107] Catch error if no replica are found in the geiven directory (#230) --- pyerrors/input/sfcf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index 94aa1948..64ce8899 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -184,6 +184,8 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= else: replica = len([file.split(".")[-1] for file in ls]) // len(set([file.split(".")[-1] for file in ls])) + if replica == 0: + raise Exception('No replica found in directory') if not silent: print('Read', part, 'part of', name_list, 'from', prefix[:-1], ',', replica, 'replica') From 1fce785597bca9e49ff8f18c4c6fbec70b6a2020 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 1 Apr 2024 16:13:48 +0200 Subject: [PATCH 073/107] [Build] Version bumped to 2.11, CHANGELOG updated, numpy<2 requirement added. --- CHANGELOG.md | 7 +++++++ pyerrors/version.py | 2 +- setup.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ab567f..84c31fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [2.11.0] - 2024-04-01 +### Added +- New special function module. + +### Fixed +- Various bug fixes in input module. + ## [2.10.0] - 2023-11-24 ### Added - More efficient implementation of read_sfcf diff --git a/pyerrors/version.py b/pyerrors/version.py index ee4b2658..7f6646a7 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.11.0-dev" +__version__ = "2.11.0" diff --git a/setup.py b/setup.py index da240d16..0d9ed3e7 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ setup(name='pyerrors', license="MIT", packages=find_packages(), python_requires='>=3.8.0', - install_requires=['numpy>=1.24', 'autograd>=1.6.2', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], + install_requires=['numpy>=1.24,<2', 'autograd>=1.6.2', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis', 'nbmake', 'flake8']}, classifiers=[ 'Development Status :: 5 - Production/Stable', From 9f46bf8966a9e81628018ad15cfa7e1a198c0a47 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 1 Apr 2024 16:22:08 +0200 Subject: [PATCH 074/107] [build] Version bumped to v2.12.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 7f6646a7..5f6c80fd 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.11.0" +__version__ = "2.12.0-dev" From 43bd99b6c7432f7436e8727c263b209b23914f9e Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 17 Apr 2024 14:53:34 +0200 Subject: [PATCH 075/107] [fix] Numpy 2 breaking changes in tests (#234) --- tests/linalg_test.py | 2 +- tests/obs_test.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/linalg_test.py b/tests/linalg_test.py index 49c2159f..329becb3 100644 --- a/tests/linalg_test.py +++ b/tests/linalg_test.py @@ -299,7 +299,7 @@ def test_matrix_functions(): # Check eigv v2 = pe.linalg.eigv(sym) - assert(np.all(v - v2).is_zero()) + assert np.sum(v - v2).is_zero() # Check eig function e2 = pe.linalg.eig(sym) diff --git a/tests/obs_test.py b/tests/obs_test.py index 0631effe..eed560af 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -777,11 +777,11 @@ def test_gamma_method_irregular(): carr = gen_autocorrelated_array(arr, .8) o = pe.Obs([carr], ['test']) o.gamma_method() - no = np.NaN * o + no = np.nan * o no.gamma_method() o.idl['test'] = range(1, 1998, 2) o.gamma_method() - no = np.NaN * o + no = np.nan * o no.gamma_method() @@ -839,7 +839,7 @@ def test_covariance_vs_numpy(): data1 = np.random.normal(2.5, 0.2, N) data2 = np.random.normal(0.5, 0.08, N) data3 = np.random.normal(-178, 5, N) - uncorr = np.row_stack([data1, data2, data3]) + uncorr = np.vstack([data1, data2, data3]) corr = np.random.multivariate_normal([0.0, 17, -0.0487], [[1.0, 0.6, -0.22], [0.6, 0.8, 0.01], [-0.22, 0.01, 1.9]], N).T for X in [uncorr, corr]: @@ -1276,7 +1276,7 @@ def test_nan_obs(): no.gamma_method() o.idl['test'] = [1, 5] + list(range(7, 2002, 2)) - no = np.NaN * o + no = np.nan * o no.gamma_method() @@ -1285,9 +1285,9 @@ def test_format_uncertainty(): assert pe.obs._format_uncertainty(0.548, 2.48497, 2) == '0.5(2.5)' assert pe.obs._format_uncertainty(0.548, 2.48497, 4) == '0.548(2.485)' assert pe.obs._format_uncertainty(0.548, 20078.3, 9) == '0.5480(20078.3000)' - pe.obs._format_uncertainty(np.NaN, 1) - pe.obs._format_uncertainty(1, np.NaN) - pe.obs._format_uncertainty(np.NaN, np.inf) + pe.obs._format_uncertainty(np.nan, 1) + pe.obs._format_uncertainty(1, np.nan) + pe.obs._format_uncertainty(np.nan, np.inf) def test_format(): From db612597d25ef2cd252edf83aea6ff97bb52b365 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Thu, 25 Apr 2024 20:45:53 +0200 Subject: [PATCH 076/107] [fix] Handle missing replia (#232) * [fix] First version of a fix to cope with missing replica. * [fix] added test for missing replica * [fix] refactored fix for missing replica, modified tests * [fix] refinement of tests --- pyerrors/obs.py | 37 ++++++++++++++++--- tests/obs_test.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 6 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 3f86a6b3..101f6b1f 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1138,7 +1138,7 @@ def _intersection_idx(idl): return idinter -def _expand_deltas_for_merge(deltas, idx, shape, new_idx): +def _expand_deltas_for_merge(deltas, idx, shape, new_idx, scalefactor): """Expand deltas defined on idx to the list of configs that is defined by new_idx. New, empty entries are filled by 0. If idx and new_idx are of type range, the smallest common divisor of the step sizes is used as new step size. @@ -1154,15 +1154,20 @@ def _expand_deltas_for_merge(deltas, idx, shape, new_idx): Number of configs in idx. new_idx : list List of configs that defines the new range, has to be sorted in ascending order. + scalefactor : float + An additional scaling factor that can be applied to scale the fluctuations, + e.g., when Obs with differing numbers of replica are merged. """ - if type(idx) is range and type(new_idx) is range: if idx == new_idx: - return deltas + if scalefactor == 1: + return deltas + else: + return deltas * scalefactor ret = np.zeros(new_idx[-1] - new_idx[0] + 1) for i in range(shape): ret[idx[i] - new_idx[0]] = deltas[i] - return np.array([ret[new_idx[i] - new_idx[0]] for i in range(len(new_idx))]) * len(new_idx) / len(idx) + return np.array([ret[new_idx[i] - new_idx[0]] for i in range(len(new_idx))]) * len(new_idx) / len(idx) * scalefactor def derived_observable(func, data, array_mode=False, **kwargs): @@ -1243,6 +1248,25 @@ def derived_observable(func, data, array_mode=False, **kwargs): new_r_values[name] = func(tmp_values, **kwargs) new_idl_d[name] = _merge_idx(idl) + def _compute_scalefactor_missing_rep(obs): + """ + Computes the scale factor that is to be multiplied with the deltas + in the case where Obs with different subsets of replica are merged. + Returns a dictionary with the scale factor for each Monte Carlo name. + + Parameters + ---------- + obs : Obs + The observable corresponding to the deltas that are to be scaled + """ + scalef_d = {} + for mc_name in obs.mc_names: + mc_idl_d = [name for name in obs.idl if name.startswith(mc_name + '|')] + new_mc_idl_d = [name for name in new_idl_d if name.startswith(mc_name + '|')] + if len(mc_idl_d) > 0 and len(mc_idl_d) < len(new_mc_idl_d): + scalef_d[mc_name] = sum([len(new_idl_d[name]) for name in new_mc_idl_d]) / sum([len(new_idl_d[name]) for name in mc_idl_d]) + return scalef_d + if 'man_grad' in kwargs: deriv = np.asarray(kwargs.get('man_grad')) if new_values.shape + data.shape != deriv.shape: @@ -1280,7 +1304,7 @@ def derived_observable(func, data, array_mode=False, **kwargs): d_extracted[name] = [] ens_length = len(new_idl_d[name]) for i_dat, dat in enumerate(data): - d_extracted[name].append(np.array([_expand_deltas_for_merge(o.deltas.get(name, np.zeros(ens_length)), o.idl.get(name, new_idl_d[name]), o.shape.get(name, ens_length), new_idl_d[name]) for o in dat.reshape(np.prod(dat.shape))]).reshape(dat.shape + (ens_length, ))) + d_extracted[name].append(np.array([_expand_deltas_for_merge(o.deltas.get(name, np.zeros(ens_length)), o.idl.get(name, new_idl_d[name]), o.shape.get(name, ens_length), new_idl_d[name], _compute_scalefactor_missing_rep(o).get(name.split('|')[0], 1)) for o in dat.reshape(np.prod(dat.shape))]).reshape(dat.shape + (ens_length, ))) for name in new_cov_names: g_extracted[name] = [] zero_grad = _Zero_grad(new_covobs_lengths[name]) @@ -1302,11 +1326,12 @@ def derived_observable(func, data, array_mode=False, **kwargs): new_grad[name] += np.tensordot(deriv[i_val + (i_dat, )], dat) else: for j_obs, obs in np.ndenumerate(data): + scalef_d = _compute_scalefactor_missing_rep(obs) for name in obs.names: if name in obs.cov_names: new_grad[name] = new_grad.get(name, 0) + deriv[i_val + j_obs] * obs.covobs[name].grad else: - new_deltas[name] = new_deltas.get(name, 0) + deriv[i_val + j_obs] * _expand_deltas_for_merge(obs.deltas[name], obs.idl[name], obs.shape[name], new_idl_d[name]) + new_deltas[name] = new_deltas.get(name, 0) + deriv[i_val + j_obs] * _expand_deltas_for_merge(obs.deltas[name], obs.idl[name], obs.shape[name], new_idl_d[name], scalef_d.get(name.split('|')[0], 1)) new_covobs = {name: Covobs(0, allcov[name], name, grad=new_grad[name]) for name in new_grad} diff --git a/tests/obs_test.py b/tests/obs_test.py index eed560af..c6ca1a23 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -5,6 +5,7 @@ import copy import matplotlib.pyplot as plt import pyerrors as pe import pytest +import pyerrors.linalg from hypothesis import given, strategies as st np.random.seed(0) @@ -1338,9 +1339,101 @@ def test_vec_gm(): pe.gm(cc, S=4.12) assert np.all(np.vectorize(lambda x: x.S["qq"])(cc.content) == 4.12) + def test_complex_addition(): o = pe.pseudo_Obs(34.12, 1e-4, "testens") r = o + 2j assert r.real == o r = r * 1j assert r.imag == o + + +def test_missing_replica(): + N1 = 3000 + N2 = 2000 + O1 = np.random.normal(1.0, .1, N1 + N2) + O2 = .5 * O1[:N1] + + w1 = N1 / (N1 + N2) + w2 = N2 / (N1 + N2) + m12 = np.mean(O1[N1:]) + m2 = np.mean(O2) + d12 = np.std(O1[N1:]) / np.sqrt(N2) # error of from second rep + d2 = np.std(O2) / np.sqrt(N1) # error of from first rep + dval = np.sqrt((w2 * d12 / m2)**2 + (w2 * m12 * d2 / m2**2)**2) # complete error of / + + # pyerrors version that should give the same result + O1dobs = pe.Obs([O1[:N1], O1[N1:]], names=['E|1', 'E|2']) + O2dobs = pe.Obs([O2], names=['E|1']) + O1O2 = O1dobs / O2dobs + O1O2.gm(S=0) + + # explicit construction with different ensembles + O1a = pe.Obs([O1[:N1]], names=['E|1']) + O1b = pe.Obs([O1[N1:]], names=['F|2']) + O1O2b = (w1 * O1a + w2 * O1b) / O2dobs + O1O2b.gm(S=0) + + # pyerrors version without replica (missing configs) + O1c = pe.Obs([O1], names=['E|1']) + O1O2c = O1c / O2dobs + O1O2c.gm(S=0) + + for o in [O1O2, O1O2b, O1O2c]: + assert(np.isclose(dval, o.dvalue, atol=0, rtol=5e-2)) + + o = O1O2 * O2dobs - O1dobs + o.gm() + assert(o.is_zero()) + + o = O1dobs / O1O2 - O2dobs + o.gm() + assert(o.is_zero()) + + # bring more randomness and complexity into the game + Nl = [int(np.random.uniform(low=500, high=5000)) for i in range(4)] + wl = np.array(Nl) / sum(Nl) + O1 = np.random.normal(1.0, .1, sum(Nl)) + + # pyerrors replica version + datl = [O1[:Nl[0]], O1[Nl[0]:sum(Nl[:2])], O1[sum(Nl[:2]):sum(Nl[:3])], O1[sum(Nl[:3]):sum(Nl[:4])]] + O1dobs = pe.Obs(datl, names=['E|%d' % (d) for d in range(len(Nl))]) + O2dobs = .5 * pe.Obs([datl[0]], names=['E|0']) + O3dobs = 2. / pe.Obs([datl[1]], names=['E|1']) + O1O2 = O1dobs / O2dobs + O1O2.gm(S=0) + O1O2O3 = O1O2 * np.sinh(O3dobs) + O1O2O3.gm(S=0) + + # explicit construction with different ensembles + charl = ['E', 'F', 'G', 'H'] + Ol = [pe.Obs([datl[i]], names=['%s|%d' % (charl[i], i)]) for i in range(len(Nl))] + O1O2b = sum(np.array(Ol) * wl) / O2dobs + O1O2b.gm(S=0) + i = 1 + O3dobsb = 2. / pe.Obs([datl[i]], names=['%s|%d' % (charl[i], i)]) + O1O2O3b = O1O2b * np.sinh(O3dobsb) + O1O2O3b.gm(S=0) + + for op in [[O1O2, O1O2b], [O1O2O3, O1O2O3b]]: + assert np.isclose(op[0].value, op[1].value) + assert np.isclose(op[0].dvalue, op[1].dvalue, atol=0, rtol=5e-2) + + # perform the same test using the array_mode of derived_observable + O1O2 = pyerrors.linalg.matmul(np.diag(np.diag(np.reshape(4 * [O1dobs], (2, 2)))), np.diag(np.diag(np.reshape(4 * [1. / O2dobs], (2, 2))))) + O1O2O3 = pyerrors.linalg.matmul(O1O2, np.diag(np.diag(np.sinh(np.reshape(4 * [O3dobs], (2, 2)))))) + O1O2 = O1O2[0][0] + O1O2.gm(S=0) + O1O2O3 = O1O2O3[0][0] + O1O2O3.gm(S=0) + + O1O2b = pyerrors.linalg.matmul(np.diag(np.diag(np.reshape(4 * [sum(np.array(Ol) * wl)], (2, 2)))), np.diag(np.diag(np.reshape(4 * [1. / O2dobs], (2, 2))))) + O1O2O3b = pyerrors.linalg.matmul(O1O2b, np.diag(np.diag(np.sinh(np.reshape(4 * [O3dobsb], (2, 2)))))) + O1O2b = O1O2b[0][0] + O1O2b.gm(S=0) + O1O2O3b = O1O2O3b[0][0] + O1O2O3b.gm(S=0) + + for op in [[O1O2, O1O2b], [O1O2O3, O1O2O3b]]: + assert np.isclose(op[1].value, op[0].value) + assert np.isclose(op[1].dvalue, op[0].dvalue, atol=0, rtol=5e-2) \ No newline at end of file From e23373d5ee541ae0edf5e041bba1af545a2d7c6c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 25 Apr 2024 20:50:36 +0200 Subject: [PATCH 077/107] [release] Update changelog and bump version --- CHANGELOG.md | 6 ++++++ pyerrors/version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84c31fbe..da8b2a66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [2.11.1] - 2024-04-25 + +### Fixed +- Fixed a bug in error computation when combining two Obs from the same ensemble and fluctuations on one replicum are not part of one of the Obs. + + ## [2.11.0] - 2024-04-01 ### Added - New special function module. diff --git a/pyerrors/version.py b/pyerrors/version.py index 5f6c80fd..200c2363 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.12.0-dev" +__version__ = "2.11.1" From fce6bcd1f893cf98729b78be840ba2ca11b4171a Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 25 Apr 2024 20:55:35 +0200 Subject: [PATCH 078/107] [build] Bump version to v2.12.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 200c2363..5f6c80fd 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.11.1" +__version__ = "2.12.0-dev" From 0e8d68a1f03dd5b243143ae71ab15e82f976d010 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Mon, 13 May 2024 22:27:17 +0200 Subject: [PATCH 079/107] erase print rep data (#235) --- pyerrors/input/sfcf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index 64ce8899..0a6eed85 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -307,7 +307,6 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= cfg_path = path + '/' + item + '/' + subitem file_data = _read_o_file(cfg_path, name, needed_keys, intern, version, im) rep_data.append(file_data) - print(rep_data) for t in range(intern[name]["T"]): internal_ret_dict[key][t].append([]) for cfg in range(no_cfg): From d17513f0436ef7a5590581ff47ce710b28754957 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:55:30 +0200 Subject: [PATCH 080/107] bugfix: read bb and bib/bi corr in one with keyed_out (#237) --- pyerrors/input/sfcf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index 0a6eed85..739b1752 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -362,6 +362,7 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= result_dict = {} if keyed_out: for key in needed_keys: + name = _key2specs(key)[0] result = [] for t in range(intern[name]["T"]): result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) From 7ca9d4ee4174ec157f073f448b431e8096fba16f Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Thu, 15 Aug 2024 19:00:52 +0200 Subject: [PATCH 081/107] corrected sfcf_read_multi behaviour (#238) --- pyerrors/input/sfcf.py | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index 739b1752..e9f2837e 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -224,12 +224,23 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= intern[name]["spec"][quarks][off] = {} for w in wf_list: intern[name]["spec"][quarks][off][w] = {} - for w2 in wf2_list: - intern[name]["spec"][quarks][off][w][w2] = {} - intern[name]["spec"][quarks][off][w][w2]["pattern"] = _make_pattern(version, name, off, w, w2, intern[name]['b2b'], quarks) + if b2b: + for w2 in wf2_list: + intern[name]["spec"][quarks][off][w][w2] = {} + intern[name]["spec"][quarks][off][w][w2]["pattern"] = _make_pattern(version, name, off, w, w2, intern[name]['b2b'], quarks) + else: + intern[name]["spec"][quarks][off][w]["0"] = {} + intern[name]["spec"][quarks][off][w]["0"]["pattern"] = _make_pattern(version, name, off, w, 0, intern[name]['b2b'], quarks) internal_ret_dict = {} - needed_keys = _lists2key(name_list, quarks_list, noffset_list, wf_list, wf2_list) + needed_keys = [] + for name, corr_type in zip(name_list, corr_type_list): + b2b, single = _extract_corr_type(corr_type) + if b2b: + needed_keys.extend(_lists2key([name], quarks_list, noffset_list, wf_list, wf2_list)) + else: + needed_keys.extend(_lists2key([name], quarks_list, noffset_list, wf_list, ["0"])) + for key in needed_keys: internal_ret_dict[key] = [] @@ -270,10 +281,14 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= if i == 0: if version != "0.0" and compact: file = path + '/' + item + '/' + sub_ls[0] - for name in name_list: + for name_index, name in enumerate(name_list): if version == "0.0" or not compact: file = path + '/' + item + '/' + sub_ls[0] + '/' + name - for key in _lists2key(quarks_list, noffset_list, wf_list, wf2_list): + if corr_type_list[name_index] == 'bi': + name_keys = _lists2key(quarks_list, noffset_list, wf_list, ["0"]) + else: + name_keys = _lists2key(quarks_list, noffset_list, wf_list, wf2_list) + for key in name_keys: specs = _key2specs(key) quarks = specs[0] off = specs[1] @@ -368,7 +383,7 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) result_dict[key] = result else: - for name in name_list: + for name, corr_type in zip(name_list, corr_type_list): result_dict[name] = {} for quarks in quarks_list: result_dict[name][quarks] = {} @@ -376,12 +391,19 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= result_dict[name][quarks][off] = {} for w in wf_list: result_dict[name][quarks][off][w] = {} - for w2 in wf2_list: - key = _specs2key(name, quarks, off, w, w2) + if corr_type != 'bi': + for w2 in wf2_list: + key = _specs2key(name, quarks, off, w, w2) + result = [] + for t in range(intern[name]["T"]): + result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) + result_dict[name][quarks][str(off)][str(w)][str(w2)] = result + else: + key = _specs2key(name, quarks, off, w, "0") result = [] for t in range(intern[name]["T"]): result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) - result_dict[name][quarks][str(off)][str(w)][str(w2)] = result + result_dict[name][quarks][str(off)][str(w)][str(0)] = result return result_dict From 55cd7829092dc97800c5c5eec47a73251f2294d8 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 22 Aug 2024 21:59:07 +0200 Subject: [PATCH 082/107] [Build] Remove python3.8 and add support for numpy 2 (#239) * [build] Remove python 3.8 and bump dependency version. * [Build] Remove python 3.8 from ci and update README python badge. * [ci] Temporarily remove -Werror from pytest workflow. * [ci] Remove python 3.8 from examples workflow. * [Build] Bump further dependency versions. --- .github/workflows/examples.yml | 2 +- .github/workflows/pytest.yml | 4 ++-- README.md | 2 +- setup.py | 5 ++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 67636577..b8220691 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.10", "3.12"] + python-version: ["3.10", "3.12"] steps: - name: Checkout source diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 34c18faa..52ce74c7 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12"] include: - os: macos-latest python-version: "3.10" @@ -45,4 +45,4 @@ jobs: pip freeze - name: Run tests - run: pytest --cov=pyerrors -vv -Werror + run: pytest --cov=pyerrors -vv diff --git a/README.md b/README.md index 93afe409..aa669ad5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![pytest](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml/badge.svg)](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml) [![](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![arXiv](https://img.shields.io/badge/arXiv-2209.14371-b31b1b.svg)](https://arxiv.org/abs/2209.14371) [![DOI](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2023.108750-blue)](https://doi.org/10.1016/j.cpc.2023.108750) +[![pytest](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml/badge.svg)](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml) [![](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![arXiv](https://img.shields.io/badge/arXiv-2209.14371-b31b1b.svg)](https://arxiv.org/abs/2209.14371) [![DOI](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2023.108750-blue)](https://doi.org/10.1016/j.cpc.2023.108750) # pyerrors `pyerrors` is a python framework for error computation and propagation of Markov chain Monte Carlo data from lattice field theory and statistical mechanics simulations. diff --git a/setup.py b/setup.py index 0d9ed3e7..af6a01b7 100644 --- a/setup.py +++ b/setup.py @@ -24,15 +24,14 @@ setup(name='pyerrors', author_email='fabian.joswig@ed.ac.uk', license="MIT", packages=find_packages(), - python_requires='>=3.8.0', - install_requires=['numpy>=1.24,<2', 'autograd>=1.6.2', 'numdifftools>=0.9.41', 'matplotlib>=3.7', 'scipy>=1.10', 'iminuit>=2.21', 'h5py>=3.8', 'lxml>=4.9', 'python-rapidjson>=1.10', 'pandas>=2.0'], + python_requires='>=3.9.0', + install_requires=['numpy>=2.0', 'autograd>=1.7.0', 'numdifftools>=0.9.41', 'matplotlib>=3.9', 'scipy>=1.13', 'iminuit>=2.28', 'h5py>=3.11', 'lxml>=5.0', 'python-rapidjson>=1.20', 'pandas>=2.2'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis', 'nbmake', 'flake8']}, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', From 041d53e5ae4bceaa4ac7f31248ec090c771c610f Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 22 Aug 2024 22:04:54 +0200 Subject: [PATCH 083/107] [Release] Prepare v2.12.0 (#240) * [docs] Changelog updated. * [build] Bump version. --- CHANGELOG.md | 10 ++++++++++ pyerrors/version.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da8b2a66..b59bb577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [2.12.0] - 2024-08-22 + +### Changed +- Support for numpy 2 was added via a new autograd release +- Support for python<3.9 was dropped and dependencies were updated. + +### Fixed +- Minor bug fixes in input.sfcf + + ## [2.11.1] - 2024-04-25 ### Fixed diff --git a/pyerrors/version.py b/pyerrors/version.py index 5f6c80fd..95a6d3a7 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.12.0-dev" +__version__ = "2.12.0" From 3830e3f777402a678441f5161d3e45981c439b38 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 22 Aug 2024 22:08:40 +0200 Subject: [PATCH 084/107] [Build] Bump version to 2.13.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 95a6d3a7..9b9dc340 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.12.0" +__version__ = "2.13.0-dev" From 1d6f7f65c0a3cd7bbff219d4c2e0cae2d1275124 Mon Sep 17 00:00:00 2001 From: Pia Leonie Jones Petrak <73896673+PiaLJP@users.noreply.github.com> Date: Fri, 13 Sep 2024 08:35:10 +0200 Subject: [PATCH 085/107] Feature/corr matrix and inverse cov matrix as input in least squares function for correlated fits (#223) * feat: corr_matrix kwargs as input for least squares fit * feat/tests: inverse covariance matrix and correlation matrix kwargs as input for least squares function * feat/tests/example: reduced new kwargs to 'inv_chol_cov_matrix' and outsourced the inversion & cholesky decomposition of the covariance matrix (function 'invert_corr_cov_cholesky(corr, covdiag)') * tests: added tests for inv_chol_cov_matrix kwarg for the case of combined fits * fix: renamed covdiag to inverrdiag needed for the cholesky decomposition and corrected its documentation * examples: added an example of a correlated combined fit to the least_squares documentation * feat/tests/fix(of typos): added function 'sort_corr()' (and a test of it) to sort correlation matrix according to a list of alphabetically sorted keys * docs: added more elaborate documentation/example of sort_corr(), fixed typos in documentation of invert_corr_cov_cholesky() --- examples/04_fit_example.ipynb | 333 ++++++++++++++++++++++++++++++---- pyerrors/fits.py | 84 +++++++-- pyerrors/obs.py | 86 +++++++++ tests/fits_test.py | 118 ++++++++++++ tests/obs_test.py | 21 +++ 5 files changed, 596 insertions(+), 46 deletions(-) diff --git a/examples/04_fit_example.ipynb b/examples/04_fit_example.ipynb index 628506a5..e8f3e7af 100644 --- a/examples/04_fit_example.ipynb +++ b/examples/04_fit_example.ipynb @@ -9,7 +9,10 @@ "import numpy as np\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", - "import pyerrors as pe" + "import pyerrors as pe\n", + "import scipy.optimize\n", + "from scipy.linalg import cholesky\n", + "from scipy.stats import norm" ] }, { @@ -57,7 +60,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We can now define a custom fit function, in this case a single exponential. __Here we need to use the autograd wrapped version of numpy__ (imported as anp) to use automatic differentiation." + "We can now define a custom fit function, in this case a single exponential. __Here we need to use the autograd wrapped version of np__ (imported as anp) to use automatic differentiation." ] }, { @@ -91,7 +94,8 @@ "Fit with 2 parameters\n", "Method: Levenberg-Marquardt\n", "`xtol` termination condition is satisfied.\n", - "chisquare/d.o.f.: 0.0023324250917749687\n", + "chisquare/d.o.f.: 0.0023324250917750268\n", + "fit parameters [ 0.20362603 16.25660947]\n", "\n", " Goodness of fit:\n", "χ²/d.o.f. = 0.002332\n", @@ -104,14 +108,12 @@ }, { "data": { - "image/png": "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wKJFi2oElyFDhtClSxdWrFjB2rVrq+zr3bs3+++/Pxs3bmTp0qr/OTt06MAhhxwCwL///W927NhRZf+gQYPo1q0b77zzDqtWraqyr0ePHvTr14/NmzezePHiGn/H4cOHA3DHHXcwderUKh8mP/7xj7n55pv5yEc+wrvvvlvlsd27d2fgwIFs376dV199tcbv79BDDyU3N5eSkhI2bdpUZd8BBxxAr169WLt2LStWrKiyr0uXLgwZMgSgxu8eYOjQoXTu3Jm33nqLdeuqLsZaVFREnz592LBhA8uWLauyr1OnThx00EEAvPLKKzXm6RYXF9O1a1emTZvGd77znVp/D+ecc84ef4evv/46W7durbJ/wIABFBQUsHLlSt57770q+/Lz8znwwAPZtm0br732Wo2/62GHHUZOTg5Llizhgw8+qLKvb9++9OzZkzVr1lBaWlpl3z777MPgwYMpLy/n5ZdfrnHegw46iE6dOvHmm2+yfn3VKW99+vTZY4iqCFl7en2XlpayZs2aKvt69erFAQccwKZNmygpKamyLzc3l0MPPRSA1157jW3btlXZP3DgQLp37857773HypUrq+xr7veIqVOn8o1vfKPG3PYpU6YwaNAgli1bxoYNG6o8tq29R+zpy0d5eTkXXngh77//fpt/jwgh1Bk2v/71r3PMMcew3377tfn3iD2FTWBX2Gzr7xH//ve/6xycKC8v58033wRoF+8RL7/8Mtdem8x8rvy5ec0113D55ZdTWFjYIu8ROTk55OXl1ThPrWKMDboB1+5l/3kNPXeazz8mKb/G9iXAxDoe8y0g7u123nnnxdLS0vj000/Xur+0tDSWlpbGESNG1Nh3xx13xNLS0vi9732vxr5TTjkllpaWxkcffbTO587JyYknnnhije3f/OY3Y2lpabz77rtr7Bs+fPiumjp16lRj/9y5c2NpaWm86KKLauy76qqrYmlpaZw5c2aNffvtt9+u8+6333419s+cOTOWlpbGq666qsa+iy66KJaWlsa5c+fW2NepU6dYWloan3rqqT3+Hmo779ixY2NpaWl88cUXa33cq6++GktLS+Mpp5xSY9/3vve9WFpaGu+4444a+0aMGLHr71rbeZ9++ulYWloazzvvvBr7vva1r8XS0tJ433331dg3cODAXeft2bNnjf0PP/zwXn8P99xzT43t3bp1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", + "image/png": "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YPXq1Wzfvr3FtjFjxpCVlcXmzZt5/fXXW2wbOnQo4XCY0aNHt/tzhUIh0tPT+ec//9ni9iOOOILDDz+caDTaKnwMHjyYSZMmAfDCCy+0Ci4TJ05kyJAhbNiwgS1btrTYNnLkSI488kjeeeedFn81ABgwYADHHXccAP/617/YtWtXi+25ubkMGzaMN954g02bNrXYlpWVxZgxY9i+fTurV69u9TOeeOKJANx1110UFxe3eDP54Q9/yO233855553Hm2++2WLf4cOHM378eHbu3MmqVata/f6OP/54UlNTiUQibNu2rcW2o446ihEjRrBlyxY2bNjQYtuQIUOYOHEiQKvfPcCkSZMYPHgwr732GnV1dS225eTkMGrUKLZu3cratWtbbBs0aBDHHnssAC+99FKrcbrhcJihQ4dSUlLCN77xjTZ/DxdddNF+f4evvPJKi+MaYNy4cWRkZLBx40beeuutFtvS09M5+uij2bFjBy+//HKrn/WEE04gJSWF6upq3n333RbbRo8eTXZ2NrW1tdTU1LTYdthhhzFhwgQaGxt58cUXWz3usccey6BBg1i3bl38/16TUaNG7TdENYWs/R3fNTU11NbWttg2YsQIjjrqKLZt29bqNSM1NZXjjz8egJdffpkdO3a02D5+/HiGDx/OW2+9xcaNG1ts6+7XiOLiYr7yla+0GtteVFREbm4ua9euZevWrS327WuvEfv78NHY2Mill17K22+/3edfI0KhULth88tf/jKnnHIKRxxxRJ9/jdhf2ATiYbOvv0b861//arc50djYyLp16wD6xWvEiy++yM033wzQ4n3zxhtv5KqrriIzM7PHXiP2vV+7Yp30ne98Z7/bH3rooc4+dIdUVFTE2io/HA7HSkpK2tzna1/7Wgw44OXiiy+O1dTUxJ5++uk2t9fU1MRqampieXl5rbbdddddsZqamti3vvWtVtvOPvvsWE1NTexPf/pTu8+dkpISO/PMM1vd/tWvfjVWU1MTu+eee1ptO/HEE+M1DRo0qNX2pUuXxmpqamKXXXZZq23XXXddrKamJlZWVtZq2xFHHBF/3COOOKLV9rKyslhNTU3suuuua7Xtsssui9XU1MSWLl3aatugQYNiNTU1saeeemq/v4e2Hjc/Pz9WU1MTW7lyZZv7rVq1KlZTUxM7++yzW2371re+FaupqYnddddd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", "text/plain": [ "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -140,8 +142,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Covariance: 0.009831165592706342\n", - "Normalized covariance: 0.8384671239654656\n" + "Covariance: 0.009831165600263978\n", + "Normalized covariance: 0.8384671240792649\n" ] } ], @@ -191,10 +193,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Fit with 1 parameters\n", + "Fit with 1 parameter\n", "Method: Levenberg-Marquardt\n", "`ftol` termination condition is satisfied.\n", - "chisquare/d.o.f.: 0.13241808096937788\n", + "chisquare/d.o.f.: 0.13241808096938082\n", + "fit parameters [0.20567587]\n", "\n", "Effective mass:\t 0.2057(68)\n", "Fitted mass:\t 0.2036(92)\n" @@ -224,14 +227,12 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlcAAAGLCAYAAADqL7dNAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8/fFQqAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9J0lEQVR4nO3de3Bc12Hn+d8BHwAfAJqgDJESxYgNSZE4XK3cIqVREsfrqOHI6yge7YDizEi1u07KQDT2lu1JFbD0H8vRbpU5YE05cU1UHkCVeFIlzQ5JZBWPEltltKzEduKxCMIqDUPJltBUZFqgIQnoBkAS4ANn/+i+zduNftxu3NvoRn8/VSjyvk4f9OP2D+ece66x1goAAAD+aFrtCgAAAKwlhCsAAAAfEa4AAAB8RLgCAADwEeEKAADAR4QrAAAAHxGuAAAAfLR+tSuwGowxRtItkuZWuy4AAKCutEp6zxaZKLQhw5VSwer8alcCAADUpV2SflFoY6OGqzlJ+vnPf662trbVrgsAAKgDs7Ozuu2226QSPV+NGq4kSW1tbYQrAADgKwa0AwAA+IhwBQAA4CPCFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICPCFcAAAA+IlwBAAD4iHAFAADgI8IVAACAjwhXAAAAPiJcAQAA+IhwhbqTSCRqqhwAANwIV6grAwMDCoVCvpQ1PDyseDzuS1kAADiMtXa161B1xpg2SclkMqm2trbVrk7VHTt2LBNQEomE+vv7PR0jSRMTE5KkoaEhz2UmEgmdOHEic3w8Htezzz67LCQNDAyoq6tLktTR0aGenp6s7cPDw4pGowqHw8vqJUkffvihBgcHy6p3X1/fsnUAAOQzOzur9vZ2SWq31s4W3NFa23A/ktok2WQyaRvN4OCgHRwczCyPjo7a3t7eosf09/dnLff29tpoNOq5zN7eXjsxMVHw+JmZGRuJROzMzIy11trTp0/b1FvzhomJiWX17OnpsUNDQ5nloaGhrLqWqrdTV3fdAQAoJJlMWklWUpstljOKbVyrP40crkKhUCbEOHKDjNvMzIyNRqNZxzjhxwlMpcqMRqNZAWZwcNCGQqHMcm9v77KAMzo6mrXc39+fFdAmJiaspKzHnZmZyazzUm9HJBIp+PsDAODwGq7WB9V0tpbFYjGNjo6W3G/37t36/Oc/n7XumWee0bPPPqvf/u3fLnpsd3e3otFoZnlhYUFHjhwpuN2LeDyuRCKRd8xSLBYrWN7Y2Jji8bgikYgkZbrlEomEpzJzn6tTp05lPdbw8HCmuzAejysajS6rSywWy+ryc8ZKuR/X+f/Y2Jj2799ftN5u4XBY4+Pjmf0AAFgJwlUFLl++7OlKs46OjmXr5ufn9eCDD5Y8/vLly8vWuY/Jt72UQoO3Q6FQwfqEQiHNzMxkrYvFYpJSoWRsbKysMkdGRpRIJHTy5MmsOo2PjyscDiscDquvr08HDx7MBKx4PL7suXQHpdxg5wS0YvV26+7uViwWI1wBAHxBuKrApk2bPF2xtnXr1rzrvBy7adOmZevcx+XbXqmOjg5NT0973v/o0aMaGhoq+nvklukMak8kEjp48GDmWHcLlBNuBgcHtWfPnkw4SiQSywJROBxWNBpVLBbLDHx3wlO59e7o6MgMeAcAYKUIVxXI123lVW43oVctLS3LroTzSznBamBgQIcOHVJvb29ZZYZCocwxw8PD2rZtm86dO5fZvn///qx9E4lEplsxHo/nDXKjo6MaGBjQ9PS0Ojo6MgEsN4iVqnc4HNbx48eL/j4AAHhFuGog+UKHlL9lKJ+RkRF1dXVlBZRSZSYSCR09elSHDx/OBKRoNJoJT4W64kKhkKc5qNyB0+mGdAe1QvV2c8IZAAB+YBLRBhIOhwuGllItcU6XmxNQnMHspcqMx+M6duzYsi5CKRWgnHFWuccnEolMSMq3XUqN08qtY09PT1YrV6F65z6WM78WAKA8U7MLOvOLZMmfqdmF1a5q1dBy1WAOHz6sWCyWCRsjIyNZLTrxeFwjIyNZk4COj49rfHxcPT09mWDiPq5YmZFIRP39/VktXMePH1ckEskEusHBwcw65/hoNJp1lV++cHXw4EENDQ1lyhkaGsqaELRUvd2/s1+zvgNAo3n+x+/q6y+/VXK/Lz58p77cfVcVarT6mKG9QWdod8LOqVOnsrrWRkZGNDAwkBngnUgktGfPnrxX/rnfO8XKTCQSGh4ezixPTExocHAwK9AMDw9nHiPfTOvd3d3LpnSIxWIaHx9XKBTSxMSE+vr6sq4i9FJvKRXS8s0YDwAobWp2QVNzi5Kk60tW3z17Qc+8MqHPf6JLn9y7Q+uajCSps7VZnW0tq1nVFfM6QzvhqgHDVT06duxYVmuXnw4ePJiZGgIAUJmXzkzq6RfPajJ5o/tvZ3uLjjy6V4/s27mKNfOP13DFmCvUhf7+/kDuAXjs2DH19fX5Xi4ANJKXzkzqqefGs4KVJF1ILuip58b10pnJVarZ6iBcoW4cOnRIIyMjvpWXSCT04YcfBtIaBgCN4vqS1dMvnlW+fjBn3dMvntX1pcbpKSNcoW44k4V6maLBi+Hh4cDmDgOARvHquellLVZuVtJkckGvnvM+p2K9q8rVgsaYfkmJ9GLIWnusxP4hSY+nF7skhSV9zlqbcO1TVplYG5yA5Qf3FZEAgMpMzXmbYsHrfmtB4C1X6RAka+2wtXZY0rgxptTgmUFJsfQxA5KmJWVGHFdYJgAA8Flnq7crAL3utxZUo1vwsKTMdfjW2pik4vdOSbVUuZsoJiS5p92upEwAAOCzB/Z0aGd7i0yB7UapqwYf2NM4d8IINFwZY8JKddkl8mwrOIrYWtud0813QFKs0jKNMc3GmDbnR1JrWb8IAADIa12T0ZFH90rSsoDlLB95dG9mvqtGEPSYq0I3rEtICnkpwBjTk9734ArKPCzpiJfH85t7crVi1sLkagCAxjM1u6Bd2zbr8Kfu1vAP4vpg/kpm2/atG9X7sbB2bdusqdmFhvmeW63b30xLKto+6BrUHpJ0Ml9LVRllHpX0Nddyq6TzHuq5YtwWAACwlhX7nvtg/oq++p03JTXW99xqhauSHa/pMDUsScaYXmPMjKQ9lZRprV2UlGk+MqZ6TZNPPLhb3Xtvziy/PTWvLx1/TX986D7d0bk1s76ztblqdQIAwC+533OFNNL3XNDhqtCERKFC29ItVoclHXW1VsXSx0QljZdb5mrqbGvJ2wx6R+dW7bu1fRVqBACAfwp9zzWyQMOVtTZujEkYY8LW2njOtliBw8KS+iUNyTWPVfrfRIVl1oTrS1avn09Ikl4/n9A9O9uqOsAvHo9raGhIw8PD6ujoyLrty8TEhGKxmMLhcNYNkp1jDhw4ICl1U2ZJ6urq0ujoKPfkW0WxWCzz/Hd3dxedA8zLvs5r7dyE23l/TExMaHp6WocOHfJ1njEAWKsCv3GzM9lnej4qZ4B6t7W2L70cltTjvjrQGDOYnt8qsywpaq2930uZHupU9Rs319INLe+//37t379/2b36EomEDh48mBWuurq6dPr0aYVCocxyX1+f+vv7NTAwsKIZzoeHh9Xbmz2DxsDAgOLxOKHNA2OMZmZmNDY2JklFb+NTzr7d3d0Kh8PL3h/u1x4AGlHN3Lg5HZpCxpiedAg6kBOCopJyQ9FRY0y/86NUy9XDZZRZU2rthpYdHfmHp4VCIXV3d2eWY7GYQqFQJlg5+zhfzCu9dYw7xDm6u7t16NChFZXbCMbHxxUOhzOvR7GwVM6+xfT19WlgYKD0jgDQ4KoyoD1nzqqRnG3Dck0Iml6XkFT0djbFyqwlpW5oaZS6oWX33h2rNgdIIpHQ9PS0wuGwIpGIEolEJlAVCmIrNTw8nPcegdxE2Tt36PVz31JluN8fAIDlVutqwYZRzg0tH+raXr2KubhDjhNuYrGYhoaGNDY2pmPHjmXtOzQ0pK6uLvX29ioUCmXG6MTjcYXD4axxOcPDWblZvb29isViGh0dVTwez5Td39+v8fHxTLfgxMSEJGlkZERHjx7NdBVGo1HF4/GsrqtwODX1WbF6uMVisUwLzLPPPqt4PK7p6WmdPn06a0za8ePHdfjwYUUikcyxiURCw8PDmbFpfX19WdudbYlEQvF4XKFQSL29vQXXF+P8Ps7z7nTHjY+Pa2hoKPP8Fftdy9m3lNOnTysSiRCsAKAUa23D/Uhqk2STyaQN2l/+5Lz9lYG/Kvnzlz85H3hdHNFo1EYiETs4OGj7+/ttOBy2p0+fXrbf6OiojUQiWesikYgdHR3NLPf09NiTJ09mle2U5ZTvOHnyZGbffGVba+3p06dtOBxeVo/cdYODg1nLxeqRj1Om+3cJh8PL6ptbx/7+fjsxMZF1zMzMTGb/oaGhzLaJiQk7NDRUcH0xPT09WXWbmJiw0Wg0s5zveSqknH2tTT13vb29meWZmRk7ODhoI5FI5ncFgEaUTCatUu0ibbZIzqjGvQUbWq3e0HL//v3q7+/X4OBgxS0Z8XhcIyMjWccfPHhQQ0NDSiQSGhgY0OHDhzPbjh8/nrcrsJRoNKrp6WmNj9+YhcPdelKsHoV0dHQoHo9ndUM6rUSOSCSyrL7xeFyxWCzrGPfyyZMnlUgkMtv2799fdH0+4+PjisViy+o2PT2d9VhBGhsb0/DwsIaHh3XixAlFo9GsCxsAAIXRLRgw54aWF5ILecddGUk7VvmGln19fZkvfin15e7u6irEGfDu/sKfmJhQPB7X2NjYssHwK7kCsLe3V0NDQxoaGlIsFtPjjz/uqR7F5IapUCikrq6uosc4v4PTvTc9Pa3p6WlJUk9Pj4aGhrRt2zZFIhEdOnRI/f39ikQiedcXMjY2tqxuTn1HR0erMi5t//79JbstAQD5Ea4C5tzQ8qnnxmWkrIBVKze0zP0iHxsb8xSuEomEwuFw1pe98/+RkfKuMXDGSRXS19en+++/PzN+yP2Yxerht/HxcR09elTd3d16/PHHl9V5dHQ00/LktJz19/cXXJ+PO+gCAOoP3YJV8Mi+nfrGkxHtaM/u+tvR3qJvPBmp+jxXxTiX7XuRr9tMSoUD56rDfNsKPW4x4XBYHR0dGhkZWXYFY7F6+CmRSOjhhx/W4cOHM4P5nceIx+OZwfuRSET9/f06ffq0jh8/XnB9Ic6g/VzxeDwzmSsAoHYRrqrkkX079cOB39JXH9snSfrqY/v0w4HfWpVg5XRj5TMwMOA5XEWjUe3fv39ZK9WJEycyV6W5rzRMJBI6ceKEJGWu6pNSocFLS1lfX58+97nPLWuVKlaPcpQKY/F4PBMcHc5zOT4+nrmS0M25QjDf+kIikYii0WhWN6cTPpkhHQBqH92CVbSuyejeXSFJ0r27QlXvCnSmUXCCgDv4OLe/cbrnYrGYBgcHM5fw9/f369ixY4rH45n1vb29Gh0d1cDAgKanpzMtSs5YnZMnT2pgYCAzBcD09HRmWzgcVm9vrwYGBjLTOjhdbu7HdOvt7dXExETeQdXF6pEr3+McO3YsM3u500p29OjRrIH5TsvTwMBAZrJV53c8dOhQpl5OyIvH43r22WczIS93fTFOuU4AnZiY0OnTp5fV33nsQuG00L75bl4+MTGhkZERjY2NZZ4bp4UOAOBd4Le/qUXVvP3N1OyCpuYWM8tvT83rS8df0x8fuk93dG7NrO9sbebGlwAA1DCvt7+h5Spgz//4XX395beWrf/S8deylr/48J36cvddVaoVAAAICuEqYE88uFvde28uuV9na3MVagMAAIJGuApYZ1sL3X0AADQQrhYEAADwEeEKAADAR4QrAAAAHxGuAAAAfES4AgAA8BHhCgAAwEeEKwAAAB8RrgAAAHxEuAIAAPAR4QoAAMBHhCsAAAAfEa4AAAB8RLgCAADwEeEKAADAR+ur8SDGmH5JifRiyFp7zOMxktQlSdbaPte2qKQ+SaOS4pK6JZ2y1o74WG0AAICyBR6unJBkrR1OL0eNMUPusJTnmEFr7YBrecgYM2qt7U6vCkmKSupRKlwNEqwAAEAtqEbL1WFJe5wFa23MGDOqVMvTMsaYkKSIMSZkrU2kVw9JOm2MCVtr4+l1e1zbAQAAakKgY66MMWGlugETebZFixy6X1LYtewEqlCF9Wg2xrQ5P5JaKykHAACglKBbrsIF1idUICilg9i2nNVOEIu71j1ujJmW1CGpy92NmMdhSUdK1BUAAGDFVutqQScUeXVYUp+rBWxcUsxaO5IeyzVhjDlZ5PijktpdP7vKrzIAAEBpVblaMA/PwcoYMyjpuDMgXpJc464cJyQN5YzTkmv/RUmLrjLLrjAAAIAXQbdc5YYgR6jItgxjTI+kidypG9LrM1yBqlA3JAAAQFUEGq7SLUyJ9MD23G2xYsc6A95dUziEjDHh9NWEJ91lptdJHgIbAABAkKox5uqobgxId1qdhl3LYdeEoc66iKSIpPH09rCkXknT6VaqYzldg72SRpiaAQAArDZjrQ3+QVLhyQlDB3ImCO2VNGCt7UovhySdU56rCa21xrVPr2vT9hJXC+bWp01SMplMqq2trazfBQAANKbZ2Vm1t7dLUru1drbQflUJV7WGcAUAAMrlNVyt1tWCAACgiqZmFzQ1t1hyv87WZnW2tVShRmsX4QoAgAbw/I/f1ddffqvkfl98+E59ufuuKtRo7SJcAQDQAJ54cLe6994sSbq+ZPXdsxf0zCsT+vwnuvTJvTu0rik1B2Rna/NqVnNNYMwVY64AAA3kpTOTevrFs5pMLmTW7Wxv0ZFH9+qRfTtXsWa1z+uYq9W6/Q0AAKiyl85M6qnnxrOClSRdSC7oqefG9dKZyVWq2dpCuAIAoAFcX7J6+sWzytdf5ax7+sWzur7UeD1afmPMFQAANSSoq/pePTe9rMXKzUqaTC7o1XPTeqhru+dysRzhCgCAGhLUVX1Tc4WDVSX7oTDCFQAANcR9VZ8kvT01ry8df01/fOg+3dG5NbO+3Kv6Olu9tXJ53Q+FEa4AAKghnW0tme6+60tWr59PSJIuXbmme3a2ZaZMKNcDezq0s71FF5ILecddGUk72lv0wJ6OyiqODAa0AwBQg146M6nfGPyevvLCGUnSV144o98Y/F7FV/StazI68uheSakg5eYsH3l0b8XhDTcQrgAAqDFBTZnwyL6d+saTEe1oz+7629Heom88GWGeK58wiSiTiAJYZUFdHca95OrT9SWr3xj8XsEr+5zuux8O/FZZrUzu90OpGdp5P+THjZsBoE4EdXUY95JLqbeQGdSUCYXeD8+8MqFnXpnILK/190M1EK4AlFRvX071Jqirw4Iqt97UW8gMasqE3PdDIWv9/VANhCsAJdXbl1O9cV8d5nZH51btu7W95sqtN0GFzKD+6AhqyoRC7wf4j3AFrCFBnezr7csJcAsqZAb1RwdTJtQ/whWwhgR1sq+3Lycs554v6fXziRXNl1TP/Hwegvqjw5ky4annxmWkrIDFlAn1gXAFrCH1Nsam3upbry1tL52Z1NMvns0Mkv7KC2f0H773to48urehLr33+3kIarJP6caUCe76SqkWq0Z73epRQ4erubk5GUPyx9qxyUi/0nZj+rqLF1Pv751bTNZ66arm5q5W9BjXl6xeffuCJOnVty9o11ZT8ZdIUPV9f25R789fKbnfR7Zu1EfKCG7f/ME7+sYP3i2531Mf261//Zu3ey4318WLFzP/zs2tbDrC2Jsf6N/8xdll3UvOfElf++d7Fb37prLKDOr5DapcKZjnwV32v/vu2/rlXKruX3nhjL4e+5n+z0/eUXGZkvTrv7JV3/nXB/T/vTap//s7b+v/+tQd+l/u26l1TUZzc3MVl4vKeX3eG3qeqz/90z/V5s2bV7s6aECzV43mrpYOJK0brNo2VPYZXbLSqQ/X64Xzm/TYrss6sP2aVtqLcCaxXi/+olnJqze+8Ns3LOnRWxe1L3RtZYVL+sWlJv2Hn23R/3HXRd26eanickYnN+rlX5b+8n345kV17yz9Ze7Ifd2mFpp0/N1NOrT7sjpbbtS3Vl63JSsNnt2i5FWj5XNyS5JV+wargb0Xy3qMoJ7foMoN6nmQUp+J595xWindB6de/ydvXyj7s1GN9xkqc+nSJf3+7/++xDxXhW3cuJFJRLEqvv+O1bdLN4Dof94t/c7t5X+z/uQDq5NvS4n0988L5zfplSnp4B3SR2+q7Jv6Jx9YPffO8vXJq0167p1N+tzeyst2JEzqi2LLli1qa628rGiz1YFbbixfuCT9p59K//uvSjtcf0+1b2xWW7P37rvcs8WWOSu9K+3Zvkm7V1Bfh9+v288SVsmiDX5GyatGU7ZVd7V7Lz+o5zeocoN6Hpas1V+dLVymJP315Cb909ukpjJ6SQqdH46/uylrudLzAyp37Zq3oNzQ4aqlpYWWK6yKT4aX9MCtqSCxZK3Gf3ldf3Xumn5nz3pFbl6XORGHmo02t5TXLTR24ZqePbt8XFDiivTsWekL923U/h3lffSXrNXIxGUp77VLKX8RN3rotk1lfYnkPsbkB1clXdXk4gbd1bmh4rI2b5bcI1JaktclLej2jhbd3r6uojLzabmaKjd1LllZuUG8bguJa5JKjxFbULM2b/ZedlDPb1DlBvU8vPHhdSWuFJ9ramZR+vlCi+7Z7r3+7vNDMZWcH7Ayly5d8rRfQ4crYLWEWpoUakl9oT7/xhXNLKZOpH917pr+7r3reuKe8r9IpVRAef6N4t0l//nNK1kBzoufTi9l6ljI9ILVT6eXyvoSceQ+D39+9qr+68S1ip+HehPU69be7G1fr/tV05K1Ope8Lkk6l7yu3W1NFYftoJ6HZInPRLn7OZzzA+oXkRdYJWMXrulPXltcFlpmFq3+5LVFjV0ofwxTOSGoHEF9iUjBPA/1JqjX7Vc7mrStRGDoaDH61Y7KvwpyQ9CSD+N4xy5c0x/+zWX9+dlUX96fn72qP/ybyxW/F4J6Huo5vCJYVfmT0BjTLymRXgxZa495PEaSuiTJWtu30jKBciUWlpTwEBhCzUahMprng2qpCCoEBfUlEtTzUG+Cet2ajNET92zUn7xWuEvsX929seLnNogWRyds53LC9hfuU9llB/U8OKGtWDBeaXhFfQr8akEnJDnhxxgTlXQwNyzlHDNorR1wLQ9JCltruystM6f8NknJ3/3d3y055qqzs1OPPfZY1roXXnhBU1NTJR9n//79uv/++zPLV65c0Te/+U0vVdRnPvMZ7dixI7M8MTGhWCxW8rgNGzbo937v97LW/e3f/q3efPPNkseGw2F1d3dnrXvuuecyl4UX85u/+Zu65557MsvT09M6efJkyeMk6YknntDWrTfmNHr99df1ox/9qORx27Zt0+OPP5617q//+q91/vz5ksfee++9euihh7LWDQ0NLdvvrZnreiuR3Vqw9d5PakPHrZnlq9O/0M53Y7pzW+nusL6+1Fv0jQ+va/BU6fuC7b0wqvbFX2rXrl369Kc/nbXtxIkTmpmZyVqXbL5ZZ3dkv4bFyi3l4MGD6ujo0JK1+sO/uayZxSXlvdrKWm28fkmRX/yljKy2bNmiJ598MmuX0dFRxePxiup77wff05aL75XcLxqNqqurK7N84cIF/eW3/qsmmz6if5jbqH/SekU7l96XyTN27LOf/aw2btyYWT59+rTGxsYKPpaV0WTTR/TmtZv0B59+UB+/7cYYsXLPEeW+H6TyzhEfbrpN73Ts15X1WzLrOlqM/tXdqRBUyTkiE4KsldyhJP2dctf739f2yz/PrPZyjrAyGr/1n+nKus3ZZbp0tBj9r9ve1o//W/nniLEL1/SnP0nqsrnR57bx2kXdPj2WVVfJ+zniw0236Wcf+c3UQp46f+G+Zt18dVLf/va3S9ZXunGOcPzoRz/S66+/XvI4r+eIfB566CHde++9meX5+Xk9//zznurrnCMcb7zxhr7//e+XPM7rOSKfu+++Wx//+Mez1v3Zn/2Zrl4tPV1LvnPEt771rZLHSalzxOzsrA4ePCjVwNWChyXtcRastTFjzKikvEHIGBOSFDHGhKy1ifTqIUmnjTFha2283DILuXTpkpaWijez57uacGFhQfPz8yXLX1xc/leSl+MkLavXtWvXPB3r/nJweK3v5cuXl627ePGip2Nzr6BYWlry/LvmBvyrV696Ora5efkl25cvX/b1tdneZLU1PQG5lVGyZYc+WL8xa+D5L95dp++fv6T5ef9bmOauGq2bn8/72ly6dGlZnZvmL2rD9nldXb+l4JdTaKNV04dxzRcZnO5w3oc3/vJfKPhluuO9H+rifOE5YPK9NnNNOwrsnW3uapNsBe/D12eadPqWz+jqhq3aKukfJb13dV47J/9O7XPnipa1uLhY8L2UbN2jyZ2/rqsbtmqTlrfYlHuOKNkCYq02XJ3Pet3KOUc0z7+hu95/U+9v3KGJhS36/KFPZYXBcs8RWS2Oue8zYyRrdS4U0cb338wEWS/niPnNt2QFwHymF6z+8dKGis4R+3es17mLr2hs8orevtSiOzYv6CNXLsjIKrc0r+eI5vk3tHthIfN+cLjD67lz3s7f+RR7H7p5PUfkkxtKrLWBf1fl4/X8vbCw/A+Rixcv6sqV0tNzrOS7qhyBhitjTFipLrtEnm1Ra22hppj9ksKSxtPLTpQNraDMZTZv3lyy5aqlZfmowpaWlqyWlkLyffl7OU6Smpqym5HXr1/v6dgNGzYsW+e1vps2bVq2bsuW4ic6d/3cmpqaPP+uuRO5btiwwdOx+V67TZs2BfLauP/yX6/sgee7QxvV0d5a8jHdvHaftW6w2rp1a97XZvPmzXm/APbMnE79JZ0bgtJ6wtI/vOXtdXW/D/fvWK9/9pEP9O3JzVlfgBuvX0r95W/fl9LPYb73Tb7X5rrHOXpaNyxpi4fX1f0+HLtwTf/5HzcvO8tdXb9F7972yWUtK7mam5vzvic+3HSb3nVaKlzc3VblniOyuq0KhNc9iXG1br3xvFZyjlhanNVPE3Pa057dzVruOaLkGDFjdHVjq5a2hzMtbV7OEQubvd0rb9Hkf21yOecId/f+ZbWo+cr7ujYzreb167S04cZ5pGWd1Lw+9byUc47Yat/XLe99K9NC+i//p4/qsY/uzDzHXs/f+RR6H+Yq5xyRK/d7wxgT+HeV13NEPvm+l7ds2ZK3cSHXSr6ryhFot2C6u27UWmty1s9I+py1dsRjOT2STkraplTwKqtMY0yzJPcnpVXS+ZMnT+qmmyqfPReNodAYEMcX7muuaGqDVDdb8bEa//7jlU1tkDsWxinP+Ut6JZas1d/+/Kr+/OxV/W97N2S1gFRSVhDPQ72V6wjydZOkd5LX9W9/tKB/+9DKpjb4b+9d0398vfSX9h/c26x/ekt5Uxt46R4dOFDe1AYvvHVF35oo3WX0ma4NeuzO0l/Qjtwxme/NL2n4v19R7/+wUbdsvRE6yh2Tidr1wQcf1Ey3YD7Tksq5nfdhSX3W2kSR29UUK/OwpCNlPB4gKbgB10EPNN6/I9Vt6VcIcmsyRnva10m6uqwFpJKygngegpo6IugpKfx+3fJ9+bv/dZT75R/UBQ5BDRD/xG3r9dHO0q9HqMz6vvLza3lD2/B/zz5nlBvaUP9WK1x5DlbGmEFJx621wyso86ikr7mWWyWVHvWMhhfkl+n+Hev1hfsUWEuFnyEoSEE8D0FdfRfklBQOP1+3oL78gwpBQYXtoOaNCiq0of4FHa4KDfsPFdmWke4OnMgJVmWXaa1dlGt6Xm7WDK+C/jKtl5aKoPn9PATVslJv8xoF9eUfZMtr0H90+InJPlFIoO9Sa23cGJNwXeXn3lZ04Hl6vJacYJW+irBjJWXmWlhY8DyVPRpTi4cr6lL7LerSJe83kk0uWiVdu5vrzr9X9bOpGy0N7RvL+6L+boF7kuW2VJR7T7Lc+l5If2zemV6Q+8Kdcuuba2ezTf97VQuXK5889LYWq9DGG/foy2dbs3Rby4IuXfJe36DKzbWwYNP/LujShsrL2Sipc/k1LsstSeWeCve2SZ/bq6x7IUqp37+nS9rbdqWsz0Ru2f/PA1Z/Nyn9v29L//IO6dd3WjWZyssE/JDvSsV8qvEnwFFJUUlOSOpx/p9eDkvqcU8CaoyJSIpIGklvlyT3cUXL9OrKlSuanS04Hg1Qp5HaN2xR8qpR3vmdZNW+warTXFQ5b6XY5Ea9/MvlVyP9p59mLz9886K6d3r/Mrmv1ajrrtJfxq0brGZnvbe2BVXfXBcvNUnaoosXL2rWljcbea7fuWW9nnvHaVZwPyep3/vTOxc0P1d+gAuqXDc/n4cgdW2U+u+RTn24Xi+c36THdl3Wge3X1GRU1uehkJuaUs/DTU0XNT9Xu88DGoeX6R6kKkwiKmUm/XRamQ7kTBDaK2nAWtuVXg5JOqdUN18W9xWCxcr0UJ82Scnz58/nnccKcIu9+YH+zV+clZR922Lnzfi1f75X0bvLu+r0/blFvT9f+kP6ka0b9ZHW5aGm2qpV37OTczr0Zz/R8d/7qPbuLG96i3xib36gf/fdt/XLuRt139HWrIHurrJfs2qU6/D7eQhaUPWtt+cBa9/s7Kx27dol1cLVgjm3phnJ2TYsV6tTev6qbSsp06vW1la1tvKBRXGPHWjVpk0tevrFs5pM3mgS3tHeoiOP7tUj+3aWXWZra6vCpXerGUHVd2p2QVNzN8btTF60mX+3zN5oqehsbVZnW/mDWx470Krfvf92HT/1rr7ywhl99bF9OnRgt9Y1rWxMlN/lBv08BM2p45YtW1Z0Tq335wFrn9cGqdoZGQjUIOdkv2vbZv3HJ+/Xd89e0DOvTOjzn+jSJ/fu0LomozO/SHKyr9DzP35XX3/5rWXrv3T8tazlLz58p77cfVdFj7GuyejeXSFJ0r27QisOVkGUW43nwU+5Iejtqfmsfx3lfi7q7XkACiFcAUUUOtk/88qEnnllIrPMyb4yTzy4W917by65X2cNdI0Gqd6eh6BCUL09D0AhhCugCE72wepsa6HFT/X3PAT1uai35wEohHAFFMHJvj4F1W2FFD4XQHGEKwBrTlDdVoQ2AF5UZSqGWuNMxZBMJpmKAViDckNQIeWGoD8a/Vne0JaLMXjA2jQ7O6v29napxFQMhCvCFQCPggptAOqD13BFtyAAeMRYIwBe1M6dWwEAANYAwhUAAICPCFcAAAA+IlwBAAD4iAHtWBO4igsAUCsIV1gTCk0amYv5hwAAQSNcYU3IvdfZ21Pz+tLx1/THh+7THZ1bM+u5ByAAIGiEK6wJ7vmHri9ZvX4+IUm6dOWa7tnZpnVNZhVrBwBoJMzQzgzta8pLZyb19ItnNZlcyKzb2d6iI4/u1SP7dq5izQAA9c7rDO1cLYg146Uzk3rqufGsYCVJF5ILeuq5cb10ZnKVagYAaCSEK6wJ15esnn7xrPK1wzrrnn7xrK4vNV5LLQCgughXWBNePTe9rMXKzUqaTC7o1XPT1asUAKAhEa6wJkzNFQ5WlewHAEClCFdYEzpbvU0M6nU/AAAqRbjCmvDAng7tbG9RoQkXjFJXDT6wp6Oa1QIANCDCFdaEdU1GRx7dK0nLApazfOTRvcx3BQAIHOEKa8Yj+3bqG09GtKM9u+tvR3uLvvFkhHmuAABVwSSiTCK65lxfsjp+6l195YUz+upj+3TowG5arAAAK+Z1EtGq3P7GGNMvKZFeDFlrj3k4JiTpcUkHrbXdOduikvokjUqKS+qWdMpaO+JjtVFHpmYXNDW3mFnevHF95t83Jm+8/ztbmzO3yQEAIAiBh6t0sJK1dji9HDXGDFlr+4ocE5G0X1JIUr4RyCFJUUk9SoWrQYJVY3v+x+/q6y+/tWz9l46/lrX8xYfv1Je776pSrQAAjSjwbkFjzIykPdbahGudtdaW7KcxxvRIOmytvT/P+pi7zDLrRLfgGpPbclUILVcAgErVRLegMSasVDdgIs+2qLU2FuTjo3F0trUQmgAANSHobsFwgfUJpbr2VuJxY8y0Ut2GXdbagUI7GmOaJTW7VrWu8LEBAADyqsqA9jycUFSpcUmy1sYlyRjTa4w5aa09WGD/w5KOrODxAAAAPFmtcLWiabKdUOVyQtKQMSZvF6Sko5K+5lpulXR+JXVAZRgbBQBY64IOV7khyBEqsq0kY0yP++pAa23CGCOluiHHc/e31i5KWnQdX+lDY4UKXdWXi6v6AAD1KtBwZa2NG2MSxphwbmtTpYPZ0/NfnTTGdLm6BUPpzRUHNlTHEw/uVvfemyWlJvv87tkLeuaVCX3+E1365N4dmck+O1ubixUDAEDNqsbtb44qNSeVpMw0CsOu5bAzF1Yey7oP091+x3LCWq+kkUqnZkD1dLa1aN+t7To/c0l/8NxpPfPKhCTpmVcm9AfPndb5mUvad2s7XYIAgLpVldvfpMOTE4YOuK/sM8b0Shqw1na51oWVmiD0kKSIpGNyzcCebqnqdT3E9mJXC+apD/NcraKXzkzqqefGlfvOczpruQ8gAKAWeZ3ninsLEq6q6vqS1W8Mfk+TyYW8241SN1r+4cBvcT9AAEBN8RquqtEtCGS8em66YLCSJCtpMrmgV89NV69SAAD4iHCFqpqaKxysKtkPAIBaQ7hCVXW2ehuo7nU/AABqDeEKVfXAng7tbG9RodFURtLO9hY9sGdF88wCALBqCFeoqnVNRkce3StJywKWs3zk0b0MZgcA1C3CFarukX079Y0nI9rRnt31t6O9hWkYAAB1j6kYmIph1Vxfsjp+6l195YUz+upj+3TowG5arAAANcvrVAyrdeNmNKjcGzdv3rg+8+8bkzfep9y4GQBQrwhXqKpCN27+0vHXspa5cTMAoF4RrlBV7hs3F8ONmwEA9YpwharqbGuhuw8AsKZxtSAAAICPCFcAAAA+IlwBAAD4iHAFAADgI8IVAACAjwhXAAAAPiJcAQAA+IhwBQAA4CPCFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICPCFcAAAA+IlwBAAD4aH01HsQY0y8pkV4MWWuPeTgmJOlxSQettd1+lAkAABC0wMNVOgTJWjucXo4aY4astX1FjolI2i8pJKnDjzJRnqnZBU3NLZbcr7O1WZ1tLVWoEQAA9cFYa4N9AGNmJO2x1iZc66y11ng4tkfSYWvt/X6Vmd63TVIymUyqra3N2y/SYP5o9Gf6+stvldzviw/fqS9331WFGgEAsLpmZ2fV3t4uSe3W2tlC+wXacmWMCSvVZZfIsy1qrY1Vo0xjTLOkZteq1nIft9E88eBude+9WX//9gca/kFcH8xfyWy7aetG9X4srF+74yZ1tjYXKQUAgMYT9ID2cIH1CaW6/KpV5mFJSdfP+Qofu2F0trXo/MwlHf3Om1nBSpI+nL+io995U+dnLtElCABAjtW6WnBaecZSBVjmUUntrp9dPj/2mnN9yerpF88qX6exs+7pF8/q+lKw3coAANSb1QpXfgeromVaaxettbPOj6S5AB5/TXn13LQmkwsFt1tJk8kFvXpuunqVAgCgDgQdruIF1oeKbFuNMpFjaq5wsKpkPwAAGkWg4cpaG5eUSA9Cz91W9mD2oMrEcp2t3sZSed0PAIBGUY1uwaOSos5CenqFYddy2Jm3Ko9iY6gKlomVe2BPh3a2t6jQ3BZG0s72Fj2wJ4geXgAA6lfg4So9c3rIGNOTDkEHcib7jErKmvzTFbj6JEWMMYPpY72WiRVa12R05NG9krQsYDnLRx7dq3VNnqYWAwCgYQQ+iWgtYhJR7146M6mnXzybNbh9Z3uLjjy6V4/s27mKNQMAoLq8TiJKuCJc5eW+/c31Javvnr2gZ16Z0Oc/0aVP7t2RabHi9jcAgEZBuCqCcFUat78BACBbTdz+BvXLuf1NKdz+BgCAbIQr5NXZ1kJ3HwAAFVitGdoBAADWJMIVAACAjwhXAAAAPiJcAQAA+IhwBQAA4CPCFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICPCFcAAAA+IlwBAAD4iHAFAADgI8IVAACAjwhXAAAAPiJcAQAA+IhwBQAA4CPCFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICP1lfjQYwx/ZIS6cWQtfbYSo4xxkQl9UkalRSX1C3plLV2xMdqAwAAlC3wcJUOSbLWDqeXo8aYIWtt3wqOCUmKSupRKlwNNmqwmppd0NTcoiTp+pLVP7yX1Mylq9q2eYP+yS3tWtdkJEmdrc3qbGtZzaoCANAQjLU22AcwZkbSHmttwrXOWmtNpccYY3okxdzby6xTm6RkMplUW1tbJUXUjD8a/Zm+/vJbJff74sN36svdd1WhRgAArE2zs7Nqb2+XpHZr7Wyh/QIdc2WMCSvVpZfIsy3q1zEe6tFsjGlzfiS1VlJOLXriwd36yqfuLrrPVz51t554cHeVagQAQGMLekB7uMD6hFJdeys55nFjTI8xptcYM1iiHoclJV0/50vsXze2b23WN//+nYLbjaRv/v072r61uWp1AgCgka3W1YLTkjpWcMy4Ut2CI+lxWRPGmJNFjj0qqd31s6vMx65Zr56b1mRyoeB2K2kyuaBXz01Xr1IAADSwqlwtmEe5wSrrGGttPGfbCUlDxpi83YnW2kVJi86yMQWHe9WdqbnCwaqS/QAAwMoE3XKVG4IcoSLbSh6THtCe4QpUhboU16zOVm9XAHrdDwAArEyg4SrdwpRID1LP3Rar5BhjTEjSSff29DqpcDBbsx7Y06Gd7S0q1BZnJO1sb9EDeyppLAQAAOWqxpiro0rNSSUp0+o07FoOO/NaeTkm3Up1LKdrsFfSSKVTM9SzD+cX9dlfu12FJtSwkj77a7frw/nFAnsAAAA/BT7PlZSZFNQJQwestQOubb2SBqy1XWUcE1IqUDm2u7d7qA/zXAEAgLJ4neeqKuGq1qylcMUM7QAAVIfXcLVaVwvCJ51tLVmh6X+8LbR6lQEAAKs2zxUAAMCaRLgCAADwEeEKAADAR4QrAAAAHxGuAAAAfES4AgAA8BHhCgAAwEeEKwAAAB8RrgAAAHxEuAIAAPAR4QoAAMBHhCsAAAAfEa4AAAB8RLgCAADwEeEKAADAR4QrAAAAHxGuAAAAfES4AgAA8BHhCgAAwEeEKwAAAB8RrgAAAHxEuAIAAPAR4QoAAMBHhCsAAAAfEa4AAAB8tL4aD2KM6ZeUSC+GrLXHVnpMJWUCAAAELfCWq3QIkrV22Fo7LGncGDO0kmMqKRMAAKAajLU22AcwZkbSHmttwrXOWmtNpcdUUmZO+W2SkslkUm1tbWX+RgAAoBHNzs6qvb1dktqttbOF9gu05coYE1aqyy6RZ1u0kmMqLLPZGNPm/EhqLesXAQAA8CjobsFwgfUJSaEKj6mkzMOSkq6f8wX2AwAAWJHVulpwWlKHz8cU235UUrvrZ1eZjw0AAOBJVa4WzKPcYOXlmILbrbWLkhadZWM8Dc0CAAAoW9AtV/EC60NFtpU6ppIyAQAAqiLQcGWtjUtKpAeh526LVXJMJWUCAABUSzXGXB2VlLmKzxjTI2nYtRx25q3yeoyH7QAAAKsi8HmupMykn06X3QFr7YBrW6+kAWttl9djvGwvUR/muQIAAGXxOs9VVcJVrSFcAQCActXEJKIAAACNhnAFAADgI8IVAACAjwhXAAAAPiJcAQAA+IhwBQAA4CPCFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICPCFcAAAA+IlwBAAD4iHAFAADgI8IVAACAjwhXAAAAPiJcAQAA+IhwBQAA4CPCFQAAgI/Wr3YFGtH1JatXz01ram5Bna0temBPh9Y1mdWuFgAA8AHhqkqmZhc0Nbeov3/7Aw3/IK4P5q9ktt20daN6PxbWr91xkzpbm9XZ1rKKNQUAACtBuKqS53/8rr7+8lt5t30wf0Vf/c6bkqQvPnynvtx9VzWrBgAAfMSYqyr5Fwdu001bNxbd56atG/UvDtxWpRoBAIAgEK6q5J0PL2V1BebzwfwVvfPhpSrVCAAABIFwVSVTcwu+7gcAAGpT4GOujDH9khLpxZC19thKjjHGRCX1SRqVFJfULemUtXbEx2r7rrPV2yB1r/sBAIDaFGjLVTokyVo7bK0dljRujBla4TEhSVFJQ+mfiVoPVpJ0+/bNnsZc3b59c5VqBAAAghB0t+BhScPOgrU2JqnXh2P2WGuNtbYrHcBq3n859XNPY67+y6mfV6lGAAAgCIF1Cxpjwkp16SXybIumQ9OKj6kXTzy4W917b/Y0zxUAAKhfQY65ChdYn1Cqa28lxzxujJmW1CGpy1o7UKwixphmSe7U0lps/yB0trWos61F+25t1+9/LMwM7QAArFGrMYmoE4oqPWZckqy1cUkyxvQaY05aaw8WOf6wpCPlVjQo65qMHuravtrVAAAAAfAcrowxPZIOedj1qLV2vMj2coNV1jFOqHI5IWnIGJO3O9Gpk6SvuZZbJZ2voB4AAABFeQ5X6SvyyrkqLzcEOUJFtpU8xhjT47460FqbMMZIqS7FvKHOWrsoadFZTu8PAADgu8CuFky3MCXSg9Rzt+UdmF7qGGNMSNJJ9/b0OqlwMAMAAKiaoKdiOKrUnFSSMl2Lw67lsDOvlZdj0t1+x3K6BnsljRTpEgQAAKgaY60N9gFS4ckJQwfcV/YZY3olDVhru8o4JqTsea+2l7paME+d2iQlk8mk2trayjkUAAA0qNnZWbW3t0tSu7V2ttB+gYerWkS4AgAA5fIarrhxMwAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICPCFcAAAA+IlwBAAD4iHAFAADgI8IVAACAjwhXAAAAPiJcAQAA+IhwBQAA4CPCFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOAjwhUAAICP1q92BVbT7OzsalcBAADUCa+5wVhrA65K7THG3Crp/GrXAwAA1KVd1tpfFNrYqOHKSLpF0pyH3VuVCmK7PO6P2sDrVp943eoTr1t94nWrTKuk92yRANWQ3YLpJ6Rg4nRL5TBJ0py1ln7EOsHrVp943eoTr1t94nWrWMnnigHtAAAAPiJcAQAA+IhwVdqipKfT/6J+8LrVJ163+sTrVp943QLSkAPaAQAAgkLLFQAAgI8IVwAAAD4iXAEAAPiIcAUAAOCjhpxE1AtjTL+kRHoxZK09torVgQfGmKikPkmjkuKSuiWdstaOrGrFkMUYE5L0uKSD1truPNv57NWgYq8bn73alv5MSVKXJFlr+/JsT6QX+cz5gHCVh/NGtNYOp5ejxpih3Dckak5IUlRSj1In+EFO7rXFGBORtF+p16ojz3Y+ezWo1OsmPns1yxgzaK0dcC0PGWNGnYDMZy4YTMWQhzFmRtIea23Ctc5aa03ho7DajDE9kmLu1w21Kf1aHbbW3p+zns9eDSvyuvHZq0Hp1saTSrU2JtLrIpJOS+qy1sb5zAWDMVc5jDFhpZpFE3m2RatfI6Ax8NkDArFfUti1HE//G+IzFxy6BZcLF1ifUKrpG7XtcWPMtFJdF13u5nDUPD579Y3PXo1Jh6ZtOaud0BRXKnjlkxCfuRUhXHnnnDRQu8YlyVoblyRjTK8x5qS19uDqVgsrxGev9vHZqx+HJfVZaxPGFOz54zO3QnQLescbrcZZa+POyT3thKSe9LgD1C8+ezWOz159MMYMSjruDF4vgs/cChGulosXWB8qsg01ID2oNsM1jqBQdxNqC5+9OsVnr/alX6OJnGkW+MwFhHCVI/3XVyI90C93W2wVqgQPnKti3K+b669mThJ1gM9efeKzV/ucwemu6RZCxpgwn7ngEK7yO6obg/6cxF+qGRWrKP2X8rGcroleSSNcHl6TCnU78NmrbcteNz57tS099UJE0rgxJpwOUr1KjauS+MwFgnmuCkhPrOacLA5w5UvtS/+13OtatZ3XrbakT+w9kg4pdcI/ppyZvPns1Z5SrxufvdqUfl3OKc+Vf+55rPjM+Y9wBQAA4CO6BQEAAHxEuAIAAPAR4QoAAMBHhCsAAAAfEa4AAAB8RLgCAADwEeEKAADAR4QrAPAofduQ0GrXA0BtI1wBgHeHxc2IAZRAuAIA7yLW2vHVrgSA2ka4AgAPjDFRSaOrXQ8AtY9wBQDeHJQ0UnIvAA2PcAUA3oSttfHVrgSA2rd+tSsAAH4yxkQk7ZfUJemUpJik3vTmhLV2uIIyeySdLLLtgKQJSfH0z7S1NlF25QGsCbRcAVgz0tMkRK21w9baAUnPSjpsrT2W3mWgwqIPSTqR5/F6JXVbawfSoS2kVMjaX+HjAFgDaLkCsJb0uoKUYyL977ikvgrLDeW2RBljwpIGJe1xrU5IkrU2VuHjAFgDCFcA1pLMgPN0+Akp3eKUG3jS23uU6sY7IGko35iqdOvUUJ7HGpIUywld3UqFOAANjHAFYM3ICUdRSfEiY59OWmvvlyRjTEzSy5Luz7PfQWttd571UaWuIHSLKDXGC0ADY8wVgLWqWzlTJzi3rkkPes9IB7BQujUrd/9EbsGu/XJbqZgLCwDhCsDake7Cc/QodbVgZpurFavQgPNIznKhLkFJ2S1l6UlGZa2NGWMiuQEOQOMgXAFYE9LBajD9/x65uufy3Gw5JGk6Z11CUkfOuu58g9PToSruBKh0+X1Kjd+SUlcsMvYKaFCMuQKwVsQkDadD1phSYWfAGCNJHTnzWyW0PEiF5Apc6a6/YpOGHpTUZ4w5LUnW2oPGmJPpxydYAQ3MWGtXuw4AUFXpFqdnnQHt6XUzku53uvqMMYOSjtMCBaBcdAsCaDjpwBRyltPdevGcqw0jBCsAlaBbEECjOphunTql1DxXmWkV0i1bBCsAFaFbEAByGGOGJA1yo2YAlaBbEACW6yBYAagULVcAAAA+ouUKAADAR4QrAAAAHxGuAAAAfES4AgAA8BHhCgAAwEeEKwAAAB8RrgAAAHxEuAIAAPDR/w/ayL41LOgQlgAAAABJRU5ErkJggg==", + "image/png": "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", "text/plain": [ "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -262,11 +263,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "(Obs[0.53(35)], Obs[0.38(25)])\n", - "(Obs[1.73(35)], Obs[0.59(25)])\n", - "(Obs[3.92(35)], Obs[-1.23(25)])\n", - "(Obs[5.73(35)], Obs[-1.18(25)])\n", - "(Obs[7.74(35)], Obs[-0.40(25)])\n" + "(Obs[-0.47(35)], Obs[-0.26(25)])\n", + "(Obs[2.44(35)], Obs[1.15(25)])\n", + "(Obs[3.68(35)], Obs[-1.23(25)])\n", + "(Obs[6.50(35)], Obs[-1.86(25)])\n", + "(Obs[7.91(35)], Obs[-0.32(25)])\n" ] } ], @@ -318,10 +319,10 @@ "Fit with 3 parameters\n", "Method: ODR\n", "Sum of squares convergence\n", - "Residual variance: 0.08780824312692749\n", - "Parameter 1 : 0.06(25)\n", - "Parameter 2 : -0.160(47)\n", - "Parameter 3 : 0.80(18)\n" + "Residual variance: 0.49296554803718634\n", + "Parameter 1 : 0.72(56)\n", + "Parameter 2 : -0.43(16)\n", + "Parameter 3 : 2.33(84)\n" ] } ], @@ -347,14 +348,12 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAk8AAAFyCAYAAADsyz6AAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8/fFQqAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA4h0lEQVR4nO3deXiU1cH+8e9JgAQkk7A4CGiQQKKiKKKCu9YQ9w2KRV5cftoq1VZQaq3Utta2vlTalwqtC9RqraJFqQtqXQB3VESRupdNiYASEbIAJiw5vz/OBELINklmzjMz9+e65goz88zk9k3fzJ3znHMeY61FRERERJonzXcAERERkUSi8iQiIiISBZUnERERkSioPImIiIhEQeVJREREJAoqTyIiIiJRUHkSERERiUI73wHamjHGAL2ACt9ZREREJKFkAWttE5tgJl15whWn1b5DiIiISELaF1jT2AHJWJ4qAL744gtCoZDvLCIiIpIAysvL2W+//aAZZ66SsTwBEAqFVJ5ERESkzWnCuIiIiEgUVJ5EREREoqDyJCIiIhIFlScRERGRKKg8iYiIiERB5UlEREQkCipPIiIiIlFQeRIRERGJgsqTiIiISBRUnkRERESioPIkIiIiEoWkvbadSF0l5ZWUVFQ1eVw4K4NwKDMOiUREJBGpPEnKmLmwmKnzlzV53PjCfK4rKohDIhERSUQqT5IyxgzNpWhAj533l5ds4tpZS7h91CD6hzvvfDycleEjnoiIJAiVJ0kZ4VBmvafj+oc7c0jvbA+JREQkEWnCuIiIiEgUVJ5EREREoqDyJCIiIhIFlScRERGRKKg8iYiIiERB5UlEREQkCipPIiIiIlFQeRIRERGJgsqTiIiISBRUnkRERESioPIkIiIiEgWVJxEREZEoqDyJiIiIREHlSURERCQKKk8iIiIiUVB5EhEREYmCypOIiIhIFFSeRERERKKg8iQiIiISBZUnERERkSioPImIiIhEQeVJREREJAoqTyIiIiJRaOc7gMRfSXklJRVVTR4XzsogHMqMQ6J6bN0CqxdBySfw9adw5h8gvT08NhZWveH+3b4jhHrBUVdAwanuNSYN2jcv88NvF+/8euvwgbH8rxERkSSi8pSCZi4sZur8ZU0eN74wn+uKCuKQqJbtVfDAcPjibajeBukZ0L0ANq+HUE/oP4ySzD7MLO7GmPBnhCs/B7vDvfb9WfDsDbD/CXDgmXDg2ZC1T73fZtr8Zcxc6MrTzIXF9AhlMq4wv8WxS8ormbmwmDFDc/0VThERiQtjrfWdoU0ZY0JAWVlZGaFQyHecQKo78rS8ZBPXzlrC7aMG0T/ceefjcRl52l4FHz4Gy+fCd/8GxsAz10P3fFeCuhdA+u4d/8M1ZZz959d5+prjOaR39q4nNqyEpc/Df5+Fz193j508EU766W6vnzZ/GVPmLt0jyoSighYXqAYziYhIQigvLyc7Oxsg21pb3tixGnlKQeFQZr2lqH+4c/w++CvLYeF0eHsGbC6BfoVQVQGZITjrjy17z655cPRV7vbtRlfKukfK0Op3oXw109YexJR59Y+61RSq1oxAiYhI8lN5kvizFu49zY0UDRrjyk73Ni4sHbvAUd/fdf+TOUx7ZRVTtjf+P3kVKBERaYrKk8TPsnmwz0DI6gFnTIZu/dyE7xZaXrKp2cc+vGkkM7cXN+vYKXOXsq68ktFDcmOSRUREEpvKk8Re+Zfw3I3w8RNQ+Cs44SfQ94RWv+21s5a0+j0aMnNh8c4J5SIiIrWpPElsfTwH5lzjthYYcQ8MHNlmb113gntjHn47ujI0Zmhu1CNPsSxzIiISHCpPEjsbV8Hsy+CAM+CcadCpa5u+fTQT3G8dPpAeocx6V9nV1ZpVdyIikvxUnqTtbVwFod7QpQ9c+TL0OMRtQeBZTSFqrEBNGGRVnEREpFG6PIu0raUvwN3Hw5t/dvf3GRiI4lRjXGE+ExrY+HNCzw8Yt+xyKF8b51QiIpJINPIkbcNaWHA7zLsFCk6HIy+P2bcKZ2UwvjCfcFZGi15f3wjUhKICxn3nDFh3hlsBWL0DMJDWvL8vWptJREQSh8qTtF71Dvj39fDOvXDC9fCdm5pdOloiHMps9WVjxhXms67WJVV2nqrreZj7+vzP3Uab598FaelxySQiIokhLuXJGHMDUBq5m2OtndzE8cOAscBcYCVQBCyy1s6OZU5pIZMGO7bCuX+GwZf4TtNso4fkMnNhcf2r6vYbCv/6gSuGw6fvcYkYERFJXTH/RIgUJ6y1MyL3hxljpltrxzbyshxgGDASV55uU3EKoMpyWPch9DkWzrvDd5q2dcgIVwr/9X2w1TDirypQIiICxGfkaSLQt+aOtXaeMWYubmSpMX2ttaWxDCatUFkGD34XylbD+P9AuySc63Pw+a5Azb4MFh4Bx/7YdyIREQmAmJYnY0we7jRdaT3PDbPWzmuD75EB1P7kzmrte0oTvi2FB0fANyvgkieSszjVGHAuXP78rrlQIiKS8mK9VUFeA4+X4k7NNeZ7xpiRxpgrjTG3NXLcRKCs1m11tCElCt9uhAfOdxf1vXQO9Drcd6LY2/dIt0P6qjfhjb/4TiMiIp752udpA9DYdtOLgXnW2tmRuVIrjDGPNnDsJCC71m3fNk0qu/t2I+zYBpfMSb3RmFWvwws3wbv3+04iIiIe+ZoB2+h1Oqy1K+s89Agw3RizxylAa20VUFVz3wRoQ8aksq0S7A7omgdjX4vpVgSBdcL17iLHT18HXfaHvJN8JxIREQ9i/QlYtwTVyGnkOYwxu109tlZhaug0oMTSju1u1dlDo9xmmKlYnMDtlH7GZOh7IjxyiZvzJSIiKSemn4KREaTSyMTxus/VO1ncGJMDPFr7NZHHoJHCJTFiLTw9Hv77LBzzo0BdasWL9HZwwX3QaxBs2+I7jYiIeBCPIYRJuD2bgJ2jSjNq3c+r2QsKdo4yTa5z6u5KYLa2LvDgpVvhvQfh/DvhgDN8pwmGjl3gkifddft2bItcykVERFJFzOc8WWsnG2NuqHUq7qg6G2TW7CZee9fxSbULFdDNWntBrLNKHV/+B179Awz7NRx2oe80wbNjO9x/DuSdDCff6DuNiIjESVwmjNe5HMvsOs/NoNZIVOSxUnYvU+JDz8Pgsucg92jfSYIpvR3kfQdengT7DYF+p/hOJCIicZCiM3+lUd+sgHfuc//uc4zmOTXmxJ+60vSvH0DZGt9pREQkDlSehIffLt71dcsGmDkS3roTtn3rOVkCSEtz171rl+ku47Jju+9EIiISYypPCaqkvJI/zV1KSXllq95n2vxlzFzoytPMhcVMu3Oqu27d/8yC9h29ZEo4e3WDC/4Ogy+BtHTfaUREJMZUnhJUSUUVU+cvo6SiqumDGzBt/jKmzF2622NT1g9hWt4Mtxmmh0wJa78hcPhF7hRnZZnvNCIiEkMqTymqvuJUY8o7VUybvyzOiZLEgmkw/USoqvCdREREYkTlKQU1VpxqTJm7VAWqJQ46GzZ9Dc9p6wIRkWTl69p20kaWl2yK6viH3y7eOcepKVPmLmVdeSWjh+TGJEtS6poHZ9wGc34M+afBgHN9JxIRkTam8pTgrp21JKbvP3Nh88uWRBx+ESx9zl1AuM9xbkK5iIgkDZWnBHf7qEH0D3du9vHRjDwBjBmaG9XIU6zLXEIwBs6aAgvvgvaZvtOIiEgbU3lKcP3DnTmkd3azj791+EB6hDKbnPMEMKGogHGF+a2Jl7qyerjL2oC7/l16e69xRESk7WjCeAoaV5jPhCGdGj1GxamNfPI03DFU2xeIiCQRjTylGmvBVjNuxHcgu/5Vd8lanErKK3fbg6pmgnvdie7hrAzCoTY63dbzMNhUAi/8Es6d1jbvKSIiXqk8JahwVgbjC/MJZ2VE98I374Blz8NFj+0sSLULVGuKU4szxcnMhcVMrWf7hbrztMYX5nNdUUHbfNOc/aDoFnhmAgwcCX1PbJv3FRERb4y11neGNmWMCQFlZWVlhEIh33GCZfW7cO+pcPRVcOrvdj580+MfMHNhMWOG5nLr8IEeA8ZW3ZGnhrTpyBNAdTX8/UzYvB6uegPadWi79xYRkTZRXl5OdnY2QLa1tryxYzXylCqqNsFjP3CnkQpv3u2p0UNymbmwuNmr6hJVOJTZtqWoudLS3Oq7eTe7uU+d945/BhERaTMqT6niuRuhYh2Mma2VXz70GABjHvWdQkRE2oBW26UCa2GfQ+GsP0K3fr7TpLaVr8Az1/tOISIiraCRp2S3Yzukt4OhV/pOIuBO2y36K/Q7BQ4803caERFpAY08JbPqanjoe/DKZN9JpMZB50D/Inj2Bti6xXcaERFpAZWnZPb2dFgxH3of4TuJ1DDGXTi44it448++04iISAuoPCWrdR/D3Jth6FXQv9B3GqmtWz+3XcRHj7nTqiIiklBUnpLRjm3w+Fjomrfr+moSLCffCFe+7OajiYhIQtFv7mS0Yyv0HgyDL4X2HvY1kqZ12Mt93bAStldB+CC/eUREpNlUnpKNte6D+ZypvpNIc/zrCjcP6vtz3VcREQk8nbZLJtU74MER8NHjvpNIcw37NaxeBB9oA00RkUSh8pRMFt4NK16CrF6+k0hz9T3BbV8w92bYutl3GhERaQaVp2TxzQqY/1sY+kPIHeo7jUSj6Lew+Wt4607fSUREpBk05ykZVFfDnHHQOQyFv/SdRqLVtS+cdwfsN8R3EhERaQaVp2RQWQrV2+DcP+9axSWJ5bBR7mt1NaRpQFhEJMj0WzoZdOoKlz8PeSf5TiKt8dUH8JcjYeMq30lERKQRKk+JzFp4egJ8sUjL3JNB1zyoqoCX/td3EhERaYTKUyJb8hC88zd32k4SX4e93M7j789yo1AiIhJIKk+JqvxLeH4iHDYa8ot8p5G2MvgSd+27eb/2nURERBqg8pSIrIVnJkB6BpymUzxJJb09FP4KSovh242+04iISD202i4RVXwFaxbDWf/nJotLcjnoXDjgLF00WEQkoPTbORGFesI170JGZ99JJBaMccVp3UfuosG9B/tOJCIitei0XaJ54y9QvlbFKRU88xN3s9Z3EhERqUXlKZF88jS8cBN8vsB3EomHkyfC2sWw9DnfSUREpBadtksU3250k8QLzoCBI1v1ViXllZRUVO28v7xk025fa4SzMgiHMlv1vaQV+p4IfY6Hl26F/NO087iISECoPCWK52+CbZVw9pRWb4g5c2ExU+cv2+Pxa2ct2e3++MJ8risqaNX3klYwBk65Ce47Az59Cgac5zuRiIig8pQYNq6CDx6Fs6ZAqFer327M0FyKBvRo8rhwVkarv5e0Up9j4fTbYN+jfCcREZEIY5NsMqoxJgSUlZWVEQqFfMdpOxs+gy776zIsIiIiMVBeXk52djZAtrW2vLFjNYki6D59xi1X79pXxSmV/fdZuO9M2LHddxIRkZSn8hRkn70K//wf+PBfvpOIb1n7wKoF8NHjvpOIiKQ8laeg2roZ5lwDucfCoRf6TiO+9Toc8k+F1/4I1dW+04iIpDSVp6B68XfuMizn/UVL1MU58afw9adu5Z2IiHijT+UgWr8M3roLTvkFdOvnO40ExX5DoO9JbrNUERHxRqvtgmrFi+6DMi3ddxIJki0boGMXLR4QEWljWm2XyNZ97K5l1u8UFSfZU6eurjit+0jXvBMR8UTlKUjWLoG7j9eKKmncmnfhrmNh5Uu+k4iIpCSVp6DYvhWe/BH0GAAHneM7jQRZr8Fu9d0rf/CdREQkJcXl8izGmBuA0sjdHGvt5Fi8JqEtuB1KPoErX4L09r7TSJAZAyfeAP8cDZ8vgP2P851IRCSlxHzkKVKCsNbOsNbOABYbY6a39WsS2tf/hVcmw/HXQc/DfKeRRHDAGdBjILyq0ScRkXiL+Wo7Y8xGoK+1trTWY9Za2+ByoZa8ptZxibfabvtWeOdvcOTl0E4X45Vm+vQZKH4Lhv1aiwtERFopmtV2MS1Pxpg8YEXd0mOMsUCRtXZea19jjMkAajeOLGB1wpSnb0uhY47vFCIiIiktSFsV5DXweCmQ00avmQiU1bqtbm4479Yvh9sHwvI9OqRI82zd4k7drV/mO4mISMrwtdpuA9C1jV4zCciuddu3ddHipLoa5vwYOnVz168TaYm0dHj7Hlgw1XcSEZGU4as8RVucGnyNtbbKWltecwMqWhctThbdA8VvumvXdejkO40kqnYZcPRV8P4sKP/SdxoRkZQQ6/K0soHHcxp5riWvSSwbP4d5v4Yjvw/7H+87jSS6Iy+Hdh3hrTt9JxERSQkxLU/W2pVAaWQSeN3n6p3o05LXJJz2neCwUVB0i+8kkgwyQ3DU5fDOfVBZ5juNiEjSi8dpu0nAsJo7xpiRwIxa9/Nq9nVq7msSWvUO6ByGs/8EGVm+00iyOPpquPBByEiAFaYiIgku5uUpsjN4jjFmZKQEHWWtHVvrkGHA2Chfk5hKi+EvR8Kaxb6TSLLpHIa8k93u47pgsIhITMV8k8x4C+wmmdbCA+e77QmuftOdahFpSzu2wQPD4ZARbh6UiIg0W5D2eZIa7/4dVr4M505VcZLYSG8PnbrCG392p4dFRCQmVJ7iobQYXvgFHH4x9B/W9PEiLXXctbBhJXwyx3cSEZGkpfIUD1UVsO9RcNqtvpNIsus9GPqeCK/frrlPIiIxovIUDz0OhkuegMxs30kkFRw3HjZ85kY8RUSkzak8xVLZGvjHebBxle8kkkr6FcKEj6FLH99JRESSkspTrFgLT42Dr/+rESeJL2MgozNs2QAV63ynERFJOipPsfLOvbB8HpwzDTrm+E4jqaa6GmacDK/83ncSEZGko/IUC9+scKvrjrgMCk71nUZSUVoaHH4RLHkINn/jO42ISFJReYqFDSuhewGc+jvfSSSVHfl99/Wde/3mEBFJMipPsZBfBFe85OadiPiyVzc4bDS8PQO2VfpOIyKSNFSe2tLaJfDEj6BqkzttIuLbMT+CXofDtxt8JxERSRrtfAdIGtsq4fGxkN7B3USCoHs+jHnEdwoRiZOS8kpKKqqaPC6clUE4lBmHRMlJ5amtzP+N25hw7CvQTuVJAsRa+PQZyNoH9j3SdxoRiaGZC4uZOn9Zk8eNL8znuqKCOCRKTipPbeGzV+GtO+DUWyF8kO80Int6dTJ06gYXP+47iYjE0JihuRQN6LHz/vKSTVw7awm3jxpE//CuebjhrAwf8ZKGylNbWPOuu57Y0Vf7TiKyJ2PgmB/DY1fAuo+hxwDfiUQkRsKhzHpPx/UPd+aQ3tqwua1oVnNbOP46uOgxTRKX4Dp4OIR6w5t3+E4iIpLw9GnfGu/cB2/e6f6d3t5vFpHGpLeHoWPhg0d0yRYRkVbSabuW+upDePZncMSlvpOINM/gS6FdpvYfExFpJZWnlti6BWZfDt36Q9FvfacRaZ6OOW70SUREWkWn7Vri+YlQWgwX3AfttU+GJJDqanjmJ/CfWb6TiIgkLJWnaG3fChtXwRm3wd4H+E4jEp20NChbAwtud/s/iYhI1FSeotWug1tZN/gS30lEWuboH0LJx25/MhERiZrKU0ukpbm9c0QSUd+TIDwAFt7tO4mISEJSeRJJNca4ieNLn4OKr3ynERFJOFptJ5KKDh0Fuce6692JiEhUNPIkkorad4S9C9zqu+1bfacREUkoKk8iqWr7VrhzKLxzr+8kIiIJReVJJFW16wD7DIS3p7sRKBERaRaVJ5FUNvQq2LASlr3gO4mISMJQeRJJZfsdBb2PhLfu9J1ERCRhqDyJpLqjr3KXG6os951ERCQhaKsCkVR38HB3S0v3nUREJCFo5Ekk1aWlu9v65fDtRt9pREQCT+VJRKCqAu4+Ht65z3cSEZHAU3kSEcjIgoEj4e2/wo5tvtOIiASaypOIOEdfBRVr4ZM5vpOIiASaypOIOD0Ohv1PgLfu8p1ERCTQVJ5EZJfjxrtdx7dX+U4iIm3g4beLd/sqbUPlSUR2yS+Cs/8E7TJ8JxFJWSXllfxp7lJKyitb9T7T5i9j5kJXmmYuLGba/GXeMyUL7fMkIrvbugXeexAOOgdCPX2nEUk5JRVVTJ2/jKIBPQiHMhs+cNPXsPRZKP0Ctm6CbVugXSaccRvT5i9jytylux1ec39cYX7sMqUIlScR2V31Nph/C2wugVN+4TuNiABUlsHSF2D5XOhxCBw3DsrXwJxxkNUTMkPQvhPs1b3e4lSjNQVKdlF5EpHdZWbDoP9xez6dcD2011+ZIt6sWQwv/x5WvOj+sOl5GPQ+wj23z0D4xbrdTrM3VpxqTJm7FNZ9zLiTc6HX4bFMn7RUnkRkT0PGwtsz4MN/weFjfKcRSS1bNwOwvGQTVH8LG9Ng6GTY/3jo3MMds6as1gvcPKSH3y7eOcepKVPeb8e6j+5ndO8/wWEXQp9jwZgGj19esqlF/ynJylhrfWdoU8aYEFBWVlZGKBTyHUckcc28ACq+hLGvNfpLVUTayNbN8NZdfPjak5xdcaPvNPV6+prjOaR3tu8YMVFeXk52djZAtrW20Sula+RJROp30o1u00wRiS1r3Sjv3F/B5q+h4EfwHtw+ahD9w52b/TbRjDwBjBmay+ghubD6Hfj8VTjuOveHUtUmyNj9+y4v2cS1s5Y0+72TncqTiNRv3yOAI3ynEEl+27bA8zfBvkfCqb+Db7vCe6/TP9w5qlGeW4cPpEcos8k5TwATigp2TRrvXQhDC92/l8+D2ZdD0W9g8KUadW6A9nkSkYaVrYaHLoSNq3wnEUku1dWw6B4oXwsd9oKrFsCFM6Fr31a97bjCfCYUFTR6zG7Fqa5eg+HAc+Cp8fDPMbBlQ6vyJCuVJxFpWMcuUPwGLPqr7yQiyaNiHTxwHjzzE1j6nHtsr+5t9vaNFahGixNAp65w/h1w4cPu//fvOg7WfdRm2ZKFypOINKzDXm7ofvE/dq4AEpFW+Px1mH4CfL0ULnkSjrx8j0PCWRmML8wnnNXynf7rK1BNFqfaDjwTfrgACk6FLvu3SaZkovIkIo0bcgVUVcB/HvadRCSxbf7GrWLtXgBjX4W8k+s9LBzK5Lqiglbv5D2uMJ8xQ3MBNzk86o0xs3vDOVOhw16Eq4q5zj5AeC9NlQaVJxFpSk4uHHg2vHu/7yQiiWl7FezYDnt1g0vmwMVPQFaPuHzr0UNyd/vaYl+9D2/dCY9eCtt0fbuYV0hjzA1AaeRujrV2chPHDwPGAnOBlUARsMhaOzuWOUWkEaf+DjKyfKcQSTxbNsCsi9xu4GfcBvsd5TtRywwcCRkheORimDkSRj+c0r8TYjryFClOWGtnWGtnAIuNMdObeFkOMAyYHrmtUHES8axLHzeRdHuV7yQiiWPjKvhbEZR8AgcP952m9QpOdaNmX74PD492+1OlqFiPPE0Edq67tNbOM8bMxY0sNaavtbY0lsFEJEprFsMDw+EH86F7f99pRIJt/TL4x3mQ3h5+MA+69fOdqG30OQb+39NQWZrSe0DFbOTJGJOHO01XWs9zw9rw+2QYY0I1NyB1xxFFYik8ANLawdtNDR6LCIv+5k5rXfZc8hSnGj0Phb4nunlcr/wBKhu9kklSiuVpu7wGHi/FnZprzPeMMSONMVcaY25r4tiJQFmt2+poQopIM7XPdMuqlzwElWVNHy+SirZucV9P/R1c9iyEevrNE0sbP4c3psFDo3b9d6cIH6vtNgBdG3l+MTDPWjs7Mk9qhTHm0UaOnwRk17rt22ZJRWR3R33fzXt670HfSUSCZ+17MO1wWPMupLdz8wSTWff+MGY2fPkftwpvx3bfieKm2XOejDEjgVHNOHSStXZxI883+r8ma+3KOg89Akw3xtR7CtBaWwXsnMVqUvgcrEjMZe3jJr5++R/fSUSCZd3Hbk5g135uH6dUkTsURv0DZn4Pnv0pnDUlJeZCNbs8RVa8RbPqrW4JqpHTyHMYY0bWXl1nrS2NFKI83KiUiPh03l+gnXYZFtnpmxVucnhoX7hoduot4e8/DM653S0qsdVg0n0nirmYnbaLjCCVRiaO131uXn2vMcbkAI/Wfk3kMWikcIlIHLXLcEuU177nO4mIf9bCv34AHXPg4sfd9SBT0eBLXIFKS3dXJEhysZ7zNAm3ZxOw89TfjFr382r2ggI3ygRMrnPq7kpgtrYuEAmQpc/BjJN1wVARY2DEDHedus57+07j3+cL4PaBbi+oJBbT8hTZTTwnsnJuJHCUtbb2Hk81u4nXNskYc0PNDehmrb0gljlFJEr9h0FWL1h4t+8kIn5sr4K5N7tl+t3zIdTLd6Jg6HU4ZO8Hs8a43dWTVMxX21lrJ0dWzs221v6sznMzrLX96jxWGnlNze1niEiwpLd3K+/ef8Rd7FQklVRXwxNXuT8evlnmO02wdOgEF86Eqk0w+7KkXYGnCwOLSMsccZn7uvjvXmOIxN38W+DDx9zput5H+E4TPDm5cMHf4bPX4NVGL2ebsGJ+YWARSVJ7dYOjr4bMbN9JROJn0d9gwe1w2iQYcJ7vNMGVdxJ896+w31DfSWJC5UlEWm7Yzb4TiMTX1s3uj4ZjrvadJPgO+a77umUD7NgGWT385mlDKk8i0jrla+GTp2Holb6TiMTO9iq3Tcdx43wnSSzWusu3pHeAS+e4rQySgOY8iUjrrFnsdhZe867vJCKxsXUz3FMIi+7xnSTxGONGqIvfgFeSZ/6TypOItM4BZ0BOH1g43XcSkbZnrVtZ981KyD3Wd5rEtP/xcNLP3OTxLxb5TtMmVJ5EpHXS0mHIlW71UcVXvtOItK3X/ggfPwkjpkOPAb7TJK4Trnd7QD15NVTv8J2m1VSeRKT1Dr/IzWl4517fSUTazmevwYu3ulGTg87xnSaxpbeD4dPhnGlJMe9JE8ZFpPU65sBZ/wfhg3wnEWk7PQ+FU34Bx1/nO0ly6J7vbtU7oGw1dOnjO1GLaeRJRNrGoNHQa5DvFCKtt2O7+3DPzIYTr0+KkZJAmfsruO9MqCzznaTFVJ5EpO2seAkeG+sm2Yokqhd/A9NPgqoK30mS09Cxrjg9f5PvJC2m03Yi0nbsDnj/n3DEpdBHK5MkAX36b1gwFYp+CxlZvtNEraS8kpKKqp33l5ds2u1rjXBWBuFQZlyz7ZSTC6f+Bp6+Dg4dBX1P8JOjFYxNsr8QjTEhoKysrIxQKOQ7jkhqsRbuGAJ7HwijHvCdRiQ6G1fB9BOgz/Hu4rbG+E4UtT/NXcrU+U1frHh8YT7XFRXEIVEDqqvh72fCphK46g1o76nI1VJeXk52djZAtrW2vLFjNfIkIm3HGLdtwbM3QOkXkLOf70QizTfnx26e0/l3JGRxAhgzNJeiAU1fBiWclRGHNI1IS4NzpsKqBW6lboLRyJOItK2qTTBlABz9Q/jOz32nEWm+r/8L27a4/Ygkvmouf+ORRp5ExJ+Mzu4aVmFtKCgJ4uulbpR07wN8J0lNr/4Blj4Plz+fMCsbtdpORNper0HQroO7krpIkH27ER4YDv/+qe8kqWv/E2D1Ilh8v+8kzabyJCKx8eadMONkbVsgwWWtW/FVVeF2ERc/co+GQWNg/m9gywbfaZpF5UlEYqPXIFj3ISyf5zuJSP2WPAQfPQ7n3K7FDb4N+7XbefzF3/pO0iwqTyISG7nHQO8j4I1pvpOI7GnzercqdNBFcMgI32mkcxhO+SWktU+I0WpNGBeR2DAGjvkxzL4MvvwP9DzMdyKRXfbqDiPvhT7H+U4iNYZe6TtBs2nkSURi56BzoWs/NxlUJCjWfexGNwpOc6tDJTishbfuhg9m+07SKJUnEYmd9HZw9Ztw1A98JxFxvnwfpp8I78/ynUTqYwysfhueuzHQFw5WeRKR2GqX4TbAW7PYdxJJddur4Imr3H5OBw/3nUYacurvYOsWt/9TQKk8iUjsvf4n+Md5UNnopr0isfXy790u4sPv9r6btTQi1AuOGw8Lp8PGz32nqZfKk4jE3uBLYdu3sPgfvpNIqlq7BBbcDiffCPsM9J1GmnLsj6FjV3jvQd9J6qVr24lIfDx+FXz2KoxfAuntfaeRVLNjOyx50G1NkK6F5gmhtBiy94vbRZqjubadRp5EJD6O+RGUr4aPn/SdRFJN2RpXmI74fypOiSQn1xWnte8Fbu8nlScRiY99DoGhP4TMbN9JJJWsfAWmHgpfaLuMhPTl++4yT5/M8Z1kNypPIhI/Z9wG+UW+U0iqqKqAJ3+0a7d7STw9D4X+RTD3Zti+1XeanVSeRCS+vvoQFkz1nUJSwbxfuwvNnvcXSNPHXcI69bdQugoW3eM7yU76X5OIxNdX78PcX7kl4yKx8vkC92E77Gbosr/vNNIa4YPg8Ivdvk8B2ThT5UlE4uuQkZDVE974s+8kksy6F8BJN8JRV/hOIm3h5BvdhH/is/KuKdqqQETib8FUmP9buPZ9tyGeSFvaVgntM32nkASjrQpEJNiOuAzad4K37vSdRJLN6nfh9oFQ8qnvJJLEtOGFiMRfZghG/g16HOw7iSST7VVudV2oF3Tr7zuNJDGVJxHxo2bLAmvjtoOwJLnX/g++WQZXvqLNMCWmdNpORPxZ8SLceQxs3ew7iSS6rz5w5emEn7gNWUViSOVJRPzpmgfrlwb24p+SSAwMOM+VJ5EYU3kSEX+67A+HfNdtW7Bjm+80kqisdaNNI++Fdhm+00gKUHkSEb+OvxbKvoAPZvtOIolo/TK49zQoLfadRFKIypOI+NXjYCg4HZbP851EEk11NTz5Y9i8Hvba23caSSFajiAi/o34K2Rk+U4hiWbRX+GLt+D//Rvad/SdRlKIRp5ExL/MkNuu4KsP3fwVkaZs/Bzm3QJH/QD2P853GkkxKk8iEgzFC+Hu4+Dz13wnkUSw4TPo1g+G/dp3EklBuradiASDtTD9ROiYA5c+5TuNJAJtsCptKJpr22nOk4gEgzFw0g0w6yJY9Sb0OcZ3IqmjpLySkoqqJo8LZ2UQDsXowrwVX8HLk6DwZujUNTbfQ6QJKk8iEhwHnAU9DoFXboNLnvCdRuqYubCYqfOXNXnc+MJ8risqiE2If18PxW+58iTiicqTiARHWhp85yZYvQiqd0Bauu9EUsuYobkUDeix8/7ykk1cO2sJt48aRP9w552Ph7NitFHlx0/CJ0/BBfdr1Em8UnkSkWA58Ex3k8AJhzLrPR3XP9yZQ3pnx/abb9kAz1wPB57tLsMi4pFW24lI8GyrhJdvgzWLfSeRoPjsFajeDmf+UZPExbuYjzwZY3KA7wEXWGuLmvmaG4DSyN0ca+3k2KQTkUBKbw8fzoa1i+F/Zu3xdCAmLkt8HTwc+p0CmTEe4RJphpiWJ2PMYOBIIAdo1gnqSHHCWjsjcn+YMWa6tXZsrHKKSMCkpcOJP4XHroC170Gvw3d7OhATlyU+qjbBkofgyMtVnCQwYlqerLWLgcXGmJFRvGwi0LfWe8wzxswFVJ5EUsnBI+Dl38Mrf4DRD+32lPeJyxI/L/4W3r0fCk6DLn18pxEBAjZh3BiThztNV1rPc8OstbpyqEiqSG/nRp+e+CGsXwbd83c+5XXissRP8UJYOB1O/Z2KkwRKoMoTkNfA46W4U397MMZkALX/vNTVRUWSxcALICcXuvX3nUTibXsVzLkGeg+Go6/ynUZkN4my2m4DDc+ZmgiU1bqtjlcoEYmx9Ha7LvpaVeE3i8TXB4/ChpVw7l+035cETrNHniLzlkY149BJkblObamxyeaTgCm17mehAiWSXOZcA2Wrtet4Khk0BnoeBj0G+E4isodmlydr7WxgdgyzAKxs4PGchp6z1lYBO9csG+3/IZJ88k+FRy6Gz1+H/Y/3nUZiacd2+GKhG3HcZ6DvNCL1CtRpO2vtSqA0MnG87nOaLC6Sqg46B3oOgvm/BWt9p5FYWngX3H+2O2UnElDxKk/1nnYzxuTV7OtUyyRgWK1jRgIzYphNRILOGDjll/DFW7Bcf0clrW9WwIu3wtCroGtD64dE/ItpeapVjsYCg40xt9XZ82kYdfZviuwmnmOMGRk59ihtkCki9C+EA86Ezet9J5FYqK6Gp8ZD5zCccpPvNCKNivUmmSuByZFbfc/PoJ5RpTqXY4n1PCsRSQTGwOiHfaeQWPnPw/D5a3DJk9BhL99pRBoVtH2eREQat/kbWPx3OO5aLWFPJgcPd6Up72TfSUSaFKgJ4yIiTSpdBfN/4653JomvuhpKi6FDJzj4fN9pRJpF5UlEEkvvwe66dy/dCls3+04jrbXoHrhjKFR85TuJSLOpPIlI4in8lZs4/uadvpNIa2xYCfNuhsNGQ9Y+vtOINJvKk4gknq59YciVsGAqVJb7TiMtUV0NT/4Y9uoORb/xnUYkKpowLiKJ6cTr3STjzBDuspaSUN75G6xaAJc+BRmdfacRiYrKk4gkpk5d3a26Gqo2+U4j0co/Fc78I/Q90XcSkaipPIlIYnv0EtiUDZzpO4k0R3U1bNsCXfrAkCt8pxFpEc15EpHEdtB5sOp13ymkuRb9Fe46BqoqfCcRaTGVJxFJbId8F8IHuX9X7/CbRRr39X9h7q+g4HTIyPKdRqTFVJ5EJLGlpcGx492/P33ab5YU8/Dbxbt9bdT2rfDYFZCTC8NuiXEykdhSeRKRxNdjgPvaLtNvjgRRUl7Jn+YupaS8ssXvMW3+MmYudKVp5sJips1f1vgLXrkN1n0EI2a43cTbOI9IPKk8iUjyKDjNd4KEUFJRxdT5yyipqGrR66fNX8aUuUt3e2zK3KWNF6i+J8Lpv4deh7d5HpF4U3kSkeRRtRnmXANffeg7SdKqrzjVqLdAbat0K+zyTtLqOkkaKk8ikjzadYDit+C5G8Fa32mSTmPFqcYeBerZn8KsMfp5SFLRPk8ikvB2Tlx+90tuPf338OAI+OhxOGSE52TBtryk+ZuLPvx28c45Tk2ZMncp68orGR0uhndehRN/CmsbvoxONDlEgkDlSUS8KCmvZObCYsYMzSUcavlE77oTl3uEChh34Nnw3EToXwiZ2XHPlCiunbUkZu89c2ExMwH4X5gHzNNeXJI8VJ5ExIuaScJFA3q0uKg0NHGZEyYwbtv5sOIlOPj8uGZKJLePGkT/cPOuKxfNyBPAmJwPGc0LMPJe6JjT6LHLSzbFtMiJtDWVJxFJSI1OXH7tKzj5GcYdfGicUyWW/uHOHNK7eSNztw4fSI9QZpNzngAmDMtnXOcvoMfN0LdPa2OKBI7Kk4gknGZNXH75C0jPYFz3RXDoKEhvH6d0yWtcYT5Ao/+3n/CdPowbVgAUxCmVSPypPImIV9FOFo5q4vL85axLf5fRazfBoNFtniUVNVagJpy8H+M+vhC6TYAjL493NJG4UXkSEa9iPddl5o5hzHwNeE0TlmuEszIYX5hPOCujRa+vr0BNGJbPuPW3uAv+5p8a1zwi8abyJCJeRTNpGVowcfnInoxeORGy9oGzb3fXwmtAqkxcDocyua6odafVxhXms67W6sRxWS/D60/DhQ9B9r5xzyMSTypPIuJVNJOWIcqJy0UFbpRk5c/hH+dCxQgYcF5r4koto4fkMnNhMaP33wxP/RyG/hAOPMt3LJGYU3kSkYTTrInLNcUJ3KVBLn8e9hsaj3ipJ6cPnHQDHHet7yQicaHLs4hIQhpXmM+EBk717FacauQeDca4y7dU74hDwhSwI/J/xw6dXHlq18FvHpE4UXkSES/aYpJwfQWq3uJU45sVcO/p8NZdMcuUUt65x32tqvCbQyTOVJ5ExIuaScKt3cl7XGE+Y4bmAriJyw0VJ4Bu/eDoq2H+b+CrD2KWKSV88jQscRdgISPLbxaROFN5EpGEN3pI7m5fG1X4K+ieD7Mvh62bY5wsSa37CB4fC31P8p1ExAuVJxFJLe0z3fXWylbDa1N8p0k81sLjP4QufeHkib7TiHih1XYiknr2PgDGzIZeg3wnSTzGwMj7oF0GbO7kO42IFxp5EpHUtP9x0GEvWL8cNq7ynSb4rIVF90DVJujeH3L2851IxBuVJxFJXdXV8Mgl7rbtW99pgu3Nv8AzP4GVL/lOIuKdypOIpK60NBh+F3z9qSsG1vpOFEwfPgYv/NJtgnnQOb7TiHin8iQiqa3nYXDOVLfsftE9vtMEz2evuZV1A0dC4c2+04gEgsqTiMhhF8KQsfDyJG34WNeXS6DPsXDenY1eVFkklWi1nYgIwGm3wjFXa8PHGtur3Iq6Y6+BoVdBuj4uRGrozwgREYD09tBlf7dx5rM3wpYNvhP5U74W7joW3ovsIK7iJLIblScRkdo2lcD7s2DWxW70JdVUrIP7z4VtlW47BxHZg8qTiEhtXfvC6Idh9SJ48kduO4NUsXk9/OM8N+/r0jluJE5E9qDyJCJSV+7RMGIGfPgveOa61NnC4PmbYMt6uPQpdxFlEamXTmSLiNTn4PPd/KfSFNp9/Izfu9OWexf4TiISaBp5EhFpyOFj4Ds/d9dz++rD5ByB2rAS7jsLSr+Ajl3cdf9EpFEqTyIiTVn3MUw/AV74RXIVqC/fh3tPh01f+U4iklBUnkREmtJjAJwx2V3fbc41UL3Dd6LW+++zrjhl9YTLntOFfkWioDlPIiLNMeQKt4HmE1e71WjDp0P7TN+pWmZTCTx6GfQvdBPjO+zlO5FIQlF5EhFprsMudAXq5d/D9srEK09bt0BaOnQOw+XPwT6HRnXJlZLySkoqdu19tbxk025fa4SzMgiHEuz/NiJRUHkSEYnGgWdBwemuhGxcBZWl7uLCQbd+GTxyCeR9B07/X+g1KOq3mLmwmKnzl+3x+LWzlux2f3xhPtcVacWeJC+VJxGRaKWlu68v/S989Li7Lt5RP3Cr8oLGWvjPw/Dvn0KoFwy+uMVvNWZoLkUDejR5XDgro8XfQyQRqDyJiLTUOVMhMwT/vh4+e9Xd79TVd6pdtm+FWRfBsufhsNFw5h8ho3OL3y4cytTpOBG02k5EpOXaZ8KZf4DvPeDK0z3DgrESr3qHu6xMuw4QPhBG/xOG392q4iQiu8R85MkYkwN8D7jAWlvUjOOHAWOBucBKoAhYZK2dHcucIiItNuBc2G8olHzsTultXg+bv4bwQfHP8tmr8NxEOPoqOPwiKPpN/DOIJLmYlidjzGDgSCAHaO5Ydg4wDBiJK0+3qTiJSG2BXPWV1cPdAN66C16f4srLiTfEZw+lz1+HVybDZ6/Avkf5KW4iKSKm5clauxhYbIwZGeVL+1prS2MQSUSSQOBXfZ10A+zV3ZWZ92bCwcPh5InQvX9svt+KF+GB4dBjIFxwPww4L5iT10WShCaMi0jCCfyqr3YZkdNmF8OSmfDWnbBti3vui0Vuknn3gpYVnG3fwsqX4dNnAAvn3QF9jofRs6DgNJUmkTgIann6njFmA+5UXz9r7c8aOtAYkwHU/g2ZFetwIuJXwqz6yugMQ8fCUVfs2ozy+Z/D6reh8z6w//Gwz0A3UtS1r9vEsqb8bPsWtm4CjDvtV/IJPHUtfLnEbdDZrT8cPMJtRdCuAxxwuqf/SJHUE8TytBjAWrsSwBhzpTHmUWvtBQ0cPxG4OV7hRESiVnsX70uegFVvwuevwucLYOlzrkB17QtvTIOXJ+3+2kMvhBHToWNXCPV0Rav/MNhbm1CK+GJsM68QHpm3NKoZh06KzHWq+9qJ1tojog7oVuttBLrUNw+qgZGn1WVlZYRCoWi/nYhIfFkLttqt0iv5BL76ADDu1F+HTm6Eqcv+vlOKJL3y8nKys7MBsq215Y0d2+yRp8iKt5ivejPGjKy9us5aW2rcMHYekVGpOrmqgKpar491RBGRtmMMmMiO5eGDtEpOJAEEapPMyCjTo8aYvDqPgdu2QERERMSreJWnevd4MsbkGWNuqLkfOS03uWa+U8SVwGxtXSAiIiJBEOtNMvNwm12OAgYbY25j993Ca3YTn1zrZZNqFyqgWyOTxUVERETiqtkTxhOFMSYElGnCuIiIiDRXNBPGAzXnSURERCToVJ5EREREoqDyJCIiIhIFlScRERGRKKg8iYiIiERB5UlEREQkCipPIiIiIlFQeRIRERGJQkx3GPepvLzR/a1EREREdoqmNyTjDuO9gdW+c4iIiEhC2tdau6axA5KxPBmgF1DhO0srZOEK4L4k9n9HstLPJ/j0Mwo2/XyCLZV/PlnAWttEOUq603aR/+BGG2PQuf4HQEVT19eR+NPPJ/j0Mwo2/XyCLcV/Ps3679WEcREREZEoqDyJiIiIREHlKZiqgFsiXyV49PMJPv2Mgk0/n2DTz6cJSTdhXERERCSWNPIkIiIiEgWVJxEREZEoqDyJiIiIREHlSURERCQKSbdJZqIzxtwAlEbu5lhrJ3uMI3VEfj4A/QCstWM9xpEmGGPmWmuLfOeQ3RljbgNWRO5usNbO9plHdjHGXAnk4D6H+gGTrLWlHiMFklbbBUjNB3NNYTLGDAMu0Ad0MBhjbrPW/qzW/elAnj6cg8kYMxJ41FprmjxY4sIYkwPMBwqttaXGmMHAu/oZBUPkM2hGTVmK/Lz+aq29wGeuINJpu2CZCMyouWOtnQdc6S+O1Ij8Ehkc+VpjOjDMGJPnJZQ0KPJz6uo7h+zhNmBWzYeztXYxoD8+gqOo9ihT5N85vsIEmcpTQEQ+gHPqGx6NjECJf0cCtYvSysjXnPhHkSZ8D3jEdwjZw5XAbGNMXs3vtcgfiRIMpcaYuTV/JEY+l1Y2/pLUpPIUHA2NXpSiD2fvrLWl1toukb+Ua9SUWv1yCZDIh7I+kAOm1gjtYNzvtJXGmOn64zBQrsB9Fm2MzEsbpmkj9VN5Cr4N6PRDUE0ExmoyZeDkWGtVaIOnpjyVWmsXR35GPwMe9ZhJaon8LrsNmA3cAFxQZ6qCRKg8BZ+KUwBF/iqbZa2d0eTBEjfGmCu1civw3qn5R82cGo0+BUPk99rKyATxfrjPn3f9pgomlafgaOgv5ZxGnhMPIqu4VmgbiWCJrNx6p8kDxZeGfo+V0vC0BYmTWvNu5wFYa1daa4/AzYMa6Tdd8Gifp4Cw1q40xpQaY/LqnnLQhMrgqDXJdUbkfg7QVaeJAqErbkVkzShGP9i5/HqlRqT8ivyOW4krSrXnDuag0hsEeezaY7C26XHOkRC0z1OA1GyQWeuDeSRu6agm7AVAZGRjGG4+QI2R1NoXRYJDewgFT+R32lE1+6VF7o/VXmnBYIyZi9tbsLTWY9P1GbQnlaeAqfkrOXJ35y8Z8SsywvQZ9ax81Idz8EQ+lEfhyu1kYK5GcIOh1g7WAN30Oy44Ir/nJgLfsGult/44rIfKk4iIiEgUNGFcREREJAoqTyIiIiJRUHkSERERiYLKk4iIiEgUVJ5EREREoqDyJCIiIhIFlScRERGRKKg8iYiIiERB5UlEREQkCipPIiIiIlFQeRIRERGJwv8HAmuMdqdTI1QAAAAASUVORK5CYII=", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAFyCAYAAAAZNbUwAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA8TklEQVR4nO3deXxU1eH+8c8kQMKSSUAICgYlLCqyGUTcRSFS61LUAEXcaiVoFxTaLyntr7W2tdG0RUWrgq07IhBc0aoJCu4IRBTcWIZNEIKEyQQkgSTz++OaYDDLJMydM3Pv83698kommeQ+hjjzzLnnnuMJBoNBRERERFwoznQAEREREVNUhERERMS1VIRERETEtVSERERExLVUhERERMS1VIRERETEtVSERERExLVamQ7QlOrqarZv305SUhIej8d0HBEREYkBwWCQsrIyunXrRlxcw+M+UV+Etm/fTlpamukYIiIiEoO2bt3Kscce2+DXo74IJSUlAdZ/iNfrNZxGREREYkEgECAtLa22RzQk6otQzekwr9erIiQiIiLN0tS0Gk2WFhEREddSERIRERHXUhESERER11IREhEREddSERIRERHXUhESERER11IREhEREddSERIRERHXUhESERER11IREhEREddSERIRERHXivq9xkRsU10Nu9fD16ugbUfokwml26DwNmjdDpLT4Kh0OHao9XET+9WIiEjsURES99n+ESz/L3z5Cny72/rc0IlWEao6AGU7oKIMvlhkfT3BCzmbwBMPJRuhU0+j8UVEJHxUhMQdKvbC/hJI6QElPtjyPpxyDfQ6H44ZZI0IgVVyrl906Pv27rLuHxcP5QH492nQ7RQ4czKc8GOI09llEZFY5gkGg0HTIRoTCARITk6mtLQUr9drOo7EmqqDUPQ4LLkT0obBT+dYp8Q8nuaf6qqqhHWvwXv3WUWqWwZclAdpQ+3JLiIiLRZqf9DLWXGubSth1rnw8m+h90j40Z3W5+PiWjbfJ74VnHgx3PAqXP8KVFfCW/8Ib2YREYkonRoTZyoPwBOjrVNdk5Zap7/C6fizIHsJlJdatze+DXGt4LgzwnscERGxlYqQOEtguzXfJ9EL174ARw+0RnLsEBcP7TpZHy9/GD5fBKP+DsMm6QozEZEYoVNj4hyb3oWHzoY377Bud8+wrwQd7spH4PSb4dUceGky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", "text/plain": [ "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -393,7 +392,7 @@ }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -403,7 +402,7 @@ }, { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiQAAAFhCAYAAABAjrEyAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA1Z0lEQVR4nO3deXhU5d0+8PucmUkmC2SSECCQsAwIIiRIUOuCYhFcUZBF37rVpWL7Wtda0F/Fbm+lpLa1VatEEapVq+DWal0I1doWFTUggoCYsGQjkGWyZzLb748osoUkM+fM9yz357q4kDA556YNw53nec7zKJFIJAIiIiIiQap0ACIiIiIWEiIiIhLHQkJERETiWEiIiIhIHAsJERERiWMhISIiInEsJERERCSOhYSIiIjEsZAQERGROBYSIiIiEueUDkBE1lRUVASfzwePx4PS0lLcc8898Hg80rGIyKBYSIhIc4WFhViwYMGBAuLz+XDjjTdi1apVssGIyLA4ZUNEmluzZs0hoyEejwc+n08sDxEZHwsJEWnO4/FgxowZB0pIWVkZvF6vbCgiMjQlEolEpEMQkbX4fD5MnjwZZWVlWLhwIUaNGoUFCxZIxyIiA2MhISJdFBUVYc2aNVi9ejWmT5+OVatWcVErEXWLhYRISGFh4SGLPhcuXCgbSEOLFi3CjBkzMH36dJSVlWH+/Pnw+XwoLS2VjkZEBsU1JEQCCgsLAQALFizAggULUFBQgJtuukk4lTbKysrg8/kwffp0AIDX68Unn3wCj8eD1atXC6cjIqPiCAmRgPT0dOzcufOQKQxFUWCFv47FxcVYs2YNli5desjHi4qKkJGRgXnz5gklIyIj4wgJUZx9PYJwtPUUxcXF8Q+ksenTp6OkpOSIx3w/+eQTlhEi6hY3RiOKs7KysqN+3Ep7daxatQpLlixBZmbmgT/X4SMmREQHYyEhMoiMjAzU19dLx9CEx+NhASGiPuGUDZFBWKWMEBFFg4WEKM6627HU5/NxN1Misi0+ZUMkID09HZ988skhBcQqT9kQEUWDIyREAu65555DnqhZvXo1t1YnIlvjCAmRkMLCwgMjJB999BEXgRKRrbGQEBERkThO2RAREZE4FhIiIiISx43RiOgIkUgETR1BNLYF4GvvhK8tAF97AI3tAfgDIYQjEYTC+OrnCE5td0JRANWhQFVVOFwKhtV9CCUpCWpyMtTkFKgpKVBTkuHMyIAjLU36j0hEBsNCQmQzncEwyhvasLuuFbtqu34ub2hHXWsnGts64WsPoLkjiFC498vLfuxLOuTXqkOBa+3ibl+vJCfDNXgwXIMHw5k9GK7sIXBlD4Zz8GC4srPhGjwYanJy1H9GIjIfFhIii6rytWNLVRN21rZgV11X8dhd14bqxo4+lY1oOJzKMX8/0taGzrIydHZzrg8AqGlpcA0ZAveYMXCPPwHuE06Ae9w4qCkpWsclIgNgISGygKaOADaVN+LTCh827PFhU4UP+5r9YnmcztiXp4UbG+FvbIR/61Y0vvpq1wcVBQnDh8N9wriugvLVD8dRTk4mInNhISEymVA4gi1VjdhY7sPGch8+LfehrLYVRnqA36HXO0skgs5du9C5axea/vHGgQ+7hgyBe/wJSJpUgJQpZ8A9ZoxOAYhILywkRCZQXt+Gf32xH//esR/vl9ahqSMoHemYHI743i9QVYVAVRWa1xQDhYBz0CCkTDkDqVOmIOW00ziCQmQCLCREBtQZDOPDnXVYu3Uf3t2+D7vq2qQj9Um8C8nhgjU1aHzxJTS++BKgqnDnTUDqlDORMuUMJOXnQ5EOSERH4E6tRAbR1BHAmi01KN5ag3/vqEWL39ijIAc7/CmbrEEO5D3/faE0x6ampSHl1FORevbZ6DdjBhypXCRLZAQsJESCAqEw/rV9P17eUInirTXwB8PSkaJyeCEZPFjFCX/9gVCa3lOSktBv2jSkzZ6FlNNP58gJkSBO2RAJ2Fjuw8slFfj7pmrUt3ZKx9GcQzVHsYq0t6Pp9dfR9PrrcGQNQNpFM5E26xK4x42TjkZkOxwhIYqT8vo2vLKhEi9vrETZ/lbpOJo6fIRk2JAwRj97i1Ca2CWOGYO0WZeg/8yL4Ro0UDoOkS2wkBDpKByOYM3WGqz87y58sLPOUI/maunwQjJiSADeZ2+XCaMlVUXKqafCM28u+p13Hqd0iHTEKRsiHbR1BrHq4wqs+O9O0z0howUHQtIRtBEOo3XdOrSuWwfX0N8h47vfhWfeXG5rT6QDjpAQaai6sR0r1+3CX9eXo7E9IB0nbg4fIRmT3YKc5xYJpdGXIy0Nnu/8DzKuugrOAQOk4xBZBgsJkQY+q2jE4/8uwz8+q0ZQ53NijOjwQnL8IB+GPP8ToTTxoSQkIG3WLGRcdx0SvSOl4xCZHqdsiGLw3y9r8Ye1O7B+Z710FENRI9YfHYp0dsK3ahV8q1cjddo0ZN5wPZILCqRjEZkWCwlRFDZV+LD0zW3475d10lEMyRGyfiE5IBJBy9q1aFm7FkmTJiHr1luQctpp0qmITIeFhKgPSve34IG3tuONzXuloxiaErLe3iq90b5hA/Zcdz1Spp6FQT/+MRJHj5aORGQaLCREvVDd2I7fr/kCL5ZUImTDNSJ9pdpphOQoWv/1Hsr+81945s1D1q23wJmZKR2JyPBYSIiOoaG1E4+88yWe/mC3abd1l6AG/dIR5IVC8D3/PJpeew2ZN34PGddeC9Xtlk5FZFiqdAAiIwqGwih6rxRnFb6DJ/6zk2Wkj9Rgh3QEwwi3tmL/g39A6fkXwPfKK+CDjURHx0JCdJj1O+tx0R//g/v/sQ3NJjpx10jUAEdIDhfcuxfVd9+DnXPnovWDD6XjEBkOCwnRV+pa/Ljz+Y24bNn72F7TLB3H1NRAu3QEw/J/vhV7rr0WFbffgWA9Hxcn+hoLCREAbPgLUp4+H299tkc6iSUoHCHpUfObb6Js5sVoeuMN6ShEhsBCQvbWsAt4ahbw6s1w15Rg+cj3pBNZguq33/k90QjV16PyjjtRcdvtCNZxTxuyNxYSsqdwCFj3MPCn04Cydw98+FtVT2FqZoNcLqtgIemT5rfeQtnMi9H4+uvSUYjEsJCQ/dR+CTwxHXj7J0Dg0H84lZAff0x9WiiYdXCEpO9CDQ2o+tFdqLjlVgRra6XjEMUdCwnZy2ergaKpQFVJty9Jq/kA93s/i2Mo61E7WqUjmFbzmjVdoyWvcbSE7IWFhOwh0AH8/XbgxRuAzpYeX/4/DUUYkcS9NKLGQhKTkM+HqrvuQvkPf4hgA6cQyR5YSMj66kqB5dOBT1b0+lPU9jqsGPo3HUNZm9LOQqKFluK12HnpHLRt2CAdhUh3LCRkbZtfApZNBfb2fQpmZMUr+O6QSh1CWZzCKRstBffuxe5rvov6P/9ZOgqRrlhIyJqCfuD1HwGrrwM6o9/k7CeRIqQ4uG18XzicfFvRXCCAmiW/RsVttyPU0vOUI5EZ8Z2DrKd+J7B8BvDREzFfKqFhB4q8/9YglH04nYp0BMtqfust7Jo3H/7SUukoRJpjISFr+fzVrima6k81u+Tp1X/GGemNml3P6hwsJLrq3LULuy67HM3//Kd0FCJNsZCQNUQiwNuLgReuAfzalgcl2IGH0/6i6TWtzOGQTmB94dZWVNz8Q+x/5BGeHkyWwUJC5hcKAC8tANb9UbdbpO/9L345cotu17cSJwtJfEQiqH3oYVTeehvCrVxETObHQkLm5m8BnpkPfPaC7re6snEZctw8NK4nHCGJr+Y1a7D7u9dyvxIyPRYSMq+W/cDKi4Cyd+JyO7WtFitzX4vLvczMoXAKId46Nm/G7iuuRKC6WjoKUdRYSMic6su6nqSp3hjX244qfwlXZlfF9Z5mo6p8TFpC586d2HXFlfCXlUlHIYoKCwmZT9UGYPm5QMPOuN9aQQT34XEkOUJxv7dZcIRETrC6GruvvArtn22WjkLUZywkZC6l/wRWzgRa94tFSGzYjse868Tub3QOsKxJCjU0YM93v4vWDz6QjkLUJywkZB6bXgCeuaxXh+Pp7azqlfiWp0k6hiGxkMgLt7WhfMFNaFqzRjoKUa+xkJA5rHuo69HecEA6CQBACbbj0fRnpGMYkoqgdAQCEOnsROXtd8C3erV0FKJeYSEh4/vPg8Db9wIw1tqEjOp/46cjtkrHMBw1zEJiGKEQqu9djLrly6WTEPWIhYSM7eMVQPFPpVN065rmZch2d0rHMBRHhIXEaPb95gHUrVgpHYPomFhIyLg2vwi8fqd0imNytO7DytzXpWMYimqQaTU61L7CQvheelk6BlG3WEjImHasAV66CYgYf0+LMeWrcXn2XukYhqGGWEgMKRJB9eLFaF67VjoJ0VGxkJDx7H4feP5qwyxg7YmCCH6uPI5EbggGAFBDnMIyrFAIlXf+CK3r10snIToCCwkZS/Um4NnLgWC7dJI+cddvxaOj3peOYQhqiOf9GFnE70fF/96M9i08LJKMhYWEjKP2S+AvcwB/o3SSqHx77wqclNYsHUOcGuQIidGFW1pQfuMC+HfGf7djou6wkJAxNFYAT88W3YE1VkqgDY9lPicdQ5wa6JCOQL0Qqq/HnhtuQGAv1z+RMbCQkLzWWuCp2UBjuXSSmA2oehc/GbFdOoYohYXENIJV1dhzw/cQbGiQjkLEQkLCAu3AX+YCdTukk2jm+uZlGJxo32kLjpCYS2dpKcpv+j7Cfq79IVksJCTrtTuA6o3SKTTlaN2LFcPelI4hRvWba0EyAR2bNmHvT38mHYNsjoWE5Hz0BPCpNddcHF/xAuYOqpGOIULpZCExo8ZXXkH9s89KxyAbYyEhGeUfAW/eI51CN0okjP9zLodLNdb5O/Gg+NukI1CUapb8Gm0bNkjHIJtiIaH4a9kPvHANYPENtJLqNuMR74fSMeJO8bdKR6BoBQKovO12BGtrpZOQDbGQUHyFQ8Dq64DmKukkcTGjZjkK0lqkY8SV2s4REjML7tuHittvRyTIQxIpvlhIKL6Kfwbs+rd0irhRAq14LPN56RhxpXRwhMTs2j/+BDWFhdIxyGZYSCh+Pn8VWPdH6RRxN7BqLRYOt85jzT1qt9eIkFU1PPU0Gv/+mnQMshEWEoqP/V8Ar9wsnULMTa3LkJVg7TUzAKCo3DreSqrvuw8d2+290R/FDwsJ6c/fAjx/FdBp33NeHC1VWDH8bekYunO6+JZiJZH2dlTccitCzfb9u0vxw3cP0t/fbgFq+V3W+IrnMWvQPukYunI4+ZZiNYE9e1Cz5NfSMcgG+O5B+tryCrDlJekUhqBEQljisvbeJA6HdALSQ+NLL6HlvfekY5DFsZCQftrqgX/8WDqFoSTXfoY/etdLx9CN06lIRyCdVC++j1M3pCsWEtLPm/cArdaeoojG+fuWI6+fNR+NdVh49MfugjU1nLohXbGQkD52rAE2/VU6hSEpnS14POsF6Ri6YCGxNk7dkJ5YSEh7/uauU3ypW4Or1uBHw0ulY2jOwXcUy+PUDemFbx+kveKfAY3l0ikM7wdtjyEzISAdQ1MOJSwdgXTGqRvSCwsJaWv3OuCj5dIpTMHZXIkVw4ulY2hKVULSESgOOHVDemAhIe0EOrr2HAHXEfRWXuVzmJllnZNVHeAIiV1w6oa0xkJC2nn3fqDuS+kUpqKEg1jqftIyUx0O8IRYuwjW1GBf4W+kY5CFsJCQNqo2AOselk5hSin7N+JBb4l0DE2oEU7Z2InvxRd51g1phoWEYhcOA3+7FeA/RlGbWfs4xqW2SceImSPCERJbCYexb2mhdAqyCBYSit1nq4C9m6RTmJrib8byQaulY8RMCVvrqSHqWeu6dVzgSppgIaHYBDuBd34lncIShlS+iduGlUnHiImDhcSWagoLEQlxhJRiw0JCsflkBeDbLZ3CMm7pWIZ0l3mnPdRQp3QEEtD5ZSl8q8w/wkeyWEgoev4W4D2usteSs6kcT45YKx0jaiwk9rX/oYcQarHmGU0UHywkFL33HwFa90unsJwTK5/B+Vl10jGiogZZSOwqVFeHuscfl45BJsZCQtFprQPWPSSdwpKUcBAPuFdAUcy3wZwa7JCOQILqV65EoKpKOgaZFAsJReffDwCd3KVRL6n7S/B77wbpGH2mBPzSEUhQxO/Hvt8/KB2DTIqFhPrOV87zauJgVu3jGJPSLh2jT9QAR0jsrum119C+eYt0DDIhFhLqu3fuB0L8Tlhvir8RTw5+UTpGn6id5ipQpINIBHVFRdIpyIRYSKhv9m0FNv1VOoVt5FT+Az/I3SUdo9cUFhIC0FxcjM7d3A6A+oaFhPrmn/8HRKxxEJxZ3OlfhjST7E2i+llICEA4jLoVK6RTkMmwkFDv1e4Atr0uncJ2XE27sXzku9IxekXxm/88HtJG48uvIFhnzsfXSQYLCfXe+iIA5nsU1QomVzyNczLrpWP0SOngxljUJeL3o+GZZ6RjkImwkFDv+JuBjc9Jp7AtJRzAgykrDb83icpCQgdpeOZZhNs4aka9w0JCvbPxWe47Iqzfvo/xm5GfSsc4tvYW6QRkIKHGRvhWm+tJMZLDQkI9i0S+mq4haXPqi3Ccgfcm4ZQNHa7+z3/mScDUKywk1LPStUDdl9IpCIDa4cPy7FekYxyVw6lAiRh7SoniL1BZiaY33pSOQSbAQkI9+5CjI0YyrOLvWJCzRzrGERwuvp3Q0dU9yZ2dqWd8B6Fjq98JfLlGOgUd5seBIqQ4jTUM7nAo0hHIoPyfb0XbRx9JxyCDYyGhY/voCW6EZkCuxjI8OfJf0jEO4XSykFD3fK+8Ih2BDI6FhLrX2QpseFo6BXXjlKqncHZGg3SMAxwO6QRkZM1vvY1wBw9fpO6xkFD3Nj0PdDRKp6BuKKFO/LHfU9IxDnCoXNBK3Qu3tKC5eK10DDIwFhLq3vrHpRNQD/rXfIil3k3SMQCwkFDPGl99VToCGRgLCR1d1UZg3+fSKagX5jcUwZssPxTOQkI9aV23DsH9+6VjkEGxkNDRbXlJOgH1ktpejyeHyH/n6QAXP1MPQiE0vsYDOunonNIByKA2vyydgPpgRMWruH7o6XiyMlcsg0Mx1mPIZEyNr76KzOuu1fSaixYtwqhRowAAGRkZmDdvnqbXp/hgIaEjlX8ENBpv4y06trtDRXje8XO0hmQGPlWwkFDP/Nu2oWP7drjHjo35Wj6fD+eccw7Wrl0Lj8eDkpISTJ48GRHuGGxKnLKhI3G6xpQSfKV43Pue2P0dERYS6p3GV7SZYly0aBEuv/xyeDweAEBBQQHWrOFGjmalRFgl6WCRCPC7E4DmKukkFIWIIxFXJz6I/9SnxfW+P/YlYUx2M3Keuzuu9yVzcmZlYfS770CJcfMaRVFQWloKACgrK8P06dO1iEdCOEJChypfzzJiYkrIj4f7y2xmp4aDIvcl8wnu34+29etjukZZWRkAoKSkBD6fD16vFzfddBOKi4u1iEgCWEjoUNu5At7sPHvX4VfezXG/ryMciPs9ybxa3n03ps//upB4PB4UFBTA6/Vi6dKlmD9/vgbpSAILCR1q2z+kE5AGvtNQhGFJ8d2bRGUhoT5oeVebs5hOOumkA//t8Xjg8/k4SmJSLCT0jdodQN0O6RSkAbW9FitzXovvPYOdcb0fmVvn7t3w79wZ9ed7vd6jftzj8RwYPSFzYSGhb2zjdI2VjCx/GdcMid96IDXEQkJ90/Kv6EdJvF4vvF7vEeXD5/MdMmpC5sFCQt/YzukaK1EQwU8iRUhyxOdxXDUov309mUsshQQAli5diueff/7Ar1evXo3p06ejoKAg1mgkgBujUZd2H1DxkXQK0lhiwxco8v4XV+84S/d7qUG/7vcga2n/+BOE29uhJiVF9fnz5s1DfX09CgsLAQB1dXXch8TEWEioS/l6IMKzSKxoSvVKnJY+Ee836Ls3iRpgIaG+iQQCaPv4E6SeOSXqayxYsEDDRCSJUzbUpfwD6QSkEyXYgUfSntH/PgFO2VDftb7/vnQEMggWEuqy50PpBKSjjL3/wc9GbtX1Hoq/TdfrkzWxkNDXWEgICAWAyk+kU5DOrml8DEPd+k2rKIF23a5N1uXftg3BhgbpGGQALCQEVG8CgvzHxOrUtv1Ymavfo91qB7+GKAqRCNo+5AgtsZAQwPUjNjK6/EVckV2ty7XVjlZdrkvW177pM+kIZAAsJATsYSGxCwUR3KcUwa3qsDcJ15BQlDo+/1w6AhkACwl1PfJLtuGu345HR2lfQtX2Fs2vSfbQsVXfBddkDiwkdtewC2jZK52C4uzsvStwiqdJ24tyyoaiFG5sRGdFhXQMEsZCYnd83NeWlEAbHk1/TrvrKYDayX1IKHodWzhtY3csJHbHBa22lVn9LywesU2TazlcfCuh2HAdCfFdxO44QmJr1zYvQ7Y79lN6nU5FgzRkZywkxEJiZ6EgULtdOgUJcrTWYGXuG7Ffh4WEYsRCQiwkdubbDYSD0ilI2JiKVZg/OLaFzQ4HCwnFJlRXh0BNjXQMEsRCYmcNO6UTkAEokTB+6XgCiWr0pz07HBENE5FdcWGrvbGQ2Fk9Cwl1cdd9jke80S9wdvCdhDTAaRt749uIndWXSScgAzmnZgUK0qLb3MwRw+gK0df8X34pHYEEsZDYGQsJHUQJtGJZZnR7kzgUTtlQ7ILV+pyzRObAQmJnnLKhw2RVvYO7h3/R589zKBwhodgF9nLXaDtjIbGrcLhr23iiw9zYugwDEwN9+hwVOhzWR7YT3L8fkSCf/LMrFhK7aqoEQn7pFGRAjpZqrBz2Zt8+h4WEtBAOI8hHf22LhcSu+MgvHcO4iucxZ9C+Xr/eEeF3taQNTtvYFwuJXXFBKx2DEgnjV64n4FJ7t1hVjXCEhLQRqGYhsSsWErtiIaEeJNVuxsPe9b16rSPStzUnRN0J7uWTNnbFQmJXXNBKvXDuvieQ37/nvUnUEAsJaYMjJPbFQmJXbfXSCcgElM5WPD7ghR5fp4ZiPzGYCOAaEjtjIbErf7N0AjKJQVXFuGv4sXfQVMMsJKQNbo5mXywkdtUZ3RbhZE/fb1uGrITup2XUIAsJaYMjJPbFQmJXHCGhPnA2V+LJ4W93+/tqgHvakDZCTU3SEUgIC4ld+TlCQn0zoeKvuHjg/qP+nhLsiHMasqxgkLu12hQLiR2Fw0CgVToFmYwSCWFpwpNHPbdGDbCQkHbCHRxxsyMWEjvi+hGKUnLtp3ho1MdHfFz1twukIauKdLKQ2BELiR2xkFAMLti/HOP7HTrCpnCEhDQU6eDXkx2xkNgRF7RSDBR/M54YuOqQj6n+NqE0ZEWcsrEnFhI74oJWilF25du4fdhBxw+wkJCGIn6OkNgRC4kddXKEhGL3w/bHkO7qehqCIySkpTCnbGyJhcSOOEJCGnA2V2DliDUAAKWdX1OknYifUzZ2xEJiRwF+N0vayK98Dhdk1ULp4NcUaYcjJPbEQmJHqkM6AVmEEg7iN+4VUDpZSEg7ES5qtSUWEjtyJkknIAtJ3b8B7ecWSMcgC4kEeDaSHbGQ2JHLLZ2ALObN04NQhgyWjkEWoSQmSkcgASwkdsQREtLYCHRi1aUDpGOQRahJydIRSAALiR1xhIQ0ll9fhRf6b4NvxmTpKGQBajK/abIjFhI74ggJaez4vduRoCbg3kmlULI4UkKxUZM5QmJHLCR2xBES0pgr1InjU3Oxz9GC1+cOlY5DJqcm8ZsmO2IhsSOOkJAO8tWu72pXpm9By9RJwmnIzBSuIbElFhI74ggJ6SC//ZsTgH96yh4o6R65MGRqagoLiR2xkNgRR0hIB3n7vjlsr9zZiHfmegXTkJlxysaeWEjsyJkAKNytlbSVU78HGYnpB379p6xN8J+aJ5iIzEhJSIDi4PuTHbGQ2JWL34GQ9vKTD90c7RdT9kHplyqUhsyIT9jYFwuJXbn4l560lxc69DvbHa46rJ97vFAaMiOFe5DYFguJXfUfIp2ALCivqfaIj/0meyOCBScIpCEzcqR5pCOQEBYSu/IMk05AFpRXvQ2qcuTbypJv+6BwoSL1gmsIv1myKxYSu2IhIR2kdjRhZMqR/6B8lrAPm+ZwgSv1jIXEvlhI7CotVzoBWVSeK/2oH79/2AaEJ4yJcxoyG9dQFhK7YiGxK46QkE7y/J1H/XgIEfzuXD+UhIQ4JyIz4QiJfbGQ2JWHIySkj4l1ld3+3vrESnwx+8T4hSHTcQ3lWUh2xUJiVxwhIZ2MrtmOpGPsBvxz76fAmJFxTERmwhES+2IhsSt3GpCYJp2CLMgRCeGElJxuf79TCeFPF6qA0xnHVGQGanIynOlHX4NE1sdCYmccJSGd5CvHPsDx3aTdKL+4IE5pyCy4oNXeWEjsjOtISCf5rU09vmbxmM+gjODXIH3DNYTrR+yMhcTOOEJCOsmrKe3xNW1qACsuSQZUvg1RFy5otTe+E9gZ9yIhnQxqrMJA94AeX/ePlFLUXDA5DonIDFhI7I2FxM4yR0knIAubmDSoV6+7d8JWKEMG9/xCsrzEMdw4z85YSOxsCBcVkn7ygr17XaPagRcuzdQ3DJmCe8J46QgkiIXEzvoNAvp3/3gmUSzyfPt6/dpV/bej4VxO3diZKyeHj/zaHAuJ3Q3lKAnpY/zerXAqvd9rZPGJpVAG9rzuhKzJPWGCdAQSxkJidzknSScgi0rqbMPo1N4vUtznaMFrc7io0a6S8lhI7I6FxO6Gcpic9JPn7N+n1/85fQtapk7SKQ0ZmXs8C4ndsZDY3ZBJgOKQTkEWld/h7/Pn3HfKHijpHu3DkHEpChe0EguJ7SWkAFnHS6cgi8qv3d3nz6lwNuKduV4d0pBRJYwYAUdqqnQMEsZCQkAOp21IHyP3fYl+rr7/Q/OnrE3oOC1fh0RkRFzQSgALCQFcR0K6URDBhJToFqr+8owaKP34XbMdJHG6hsBCQgAwlE/akH7yIglRfd4OVx0+nMvpRDtw5+VJRyADYCEhYOA4wJUinYIsKr/FF/XnPpC9EYHJJ2gXhgxHSUyE+wT+f0wsJAQAqgPIniidgiwqr3p7TJ+/5NuNUJKSNEpDRpN88slQ3W7pGGQALCTUxTtVOgFZVEZrLXKSoz88b7OrBpvmckjfqlLPOlM6AhkECwl1GXuBdAKysLzE2LaEvz93A8ITeBKsFaWedZZ0BDIIFhLqkj2RB+2RbvIDoZg+P4QIfneuH0pCdAtkyZhcw4chYcQI6RhkECwk9A2OkpBO8hv2xnyN9YmV+OLSE2MPQ4aRehaniukbLCT0DRYS0snx1dvgUl0xX+fnIz8FxozUIBEZAadr6GAsJPSNEWcCiX07DI2oNxJCfhyfmhvzdTqVEP50oQo4nRqkIklKUhKSTzlZOgYZCAsJfcOZAIw+RzoFWVS+qs2uq+8m7caeSwo0uRbJSTnlFKiJidIxyEBYSOhQYy+UTkAWldfeqtm17jvuMygjYh9xITkpUzldQ4diIaFDHTcDUDkcTtrL379Ts2u1qQE8eUkyoPItzKy4oJUOx7/NdKikdGDYadIpyIJy63YhPSFNs+u9kVKKmgt5MKQZJYwehYSc6A5dJOtiIaEjcdqGdJKXPETT6907YSuUodmaXpP0lzZzpnQEMiAWEjrS8SwkpI+8sEPT6zUqHXh+doam1ySdKQrSLr5YOgUZEAsJHSl9BDCYZ4eQ9vKb6jS/5ur+21F/7kmaX5f0kTx5MlxDOV1DR2IhoaM78UrpBGRBedXboEDR/Lr3TtoBZWBs5+VQfPS/hKMjdHQsJHR0+ZcDDu4RQNrq19GIESnariMBgFq1FX+fw++6jU5JSED/88+XjkEGxUJCR5ecARx/kXQKsqC8hHRdrvtU+hY0nz1Jl2uTNlLPmQZHf+4GTUfHQkLdK7hGOgFZ0MTOoG7X/unJe6Cke3S7PsUmff586QhkYCwk1D3v2YBnmHQKspi8ugrdrl3hbMQ/53p1uz5Fz5WTg+TTuMcRdY9bclpQYWEhAKC0tBQAsGzZsugupCjApGuAd/5Pq2hEOK7mC7i9I9ER8uty/UezNuG00/Lhfn+TLten6HjmzoGiaL+gmayDIyQWs2jRIixcuBALFy48UERmzJgR/QUnfxdwJGiUjghwhoM4QYOTf4/l51P2QunXT9d7UB84HEibM0c6BRkcC4mF+Hw+lJSUwOfzHfjYTTfdhOLiYpSVlUV30dSBwLhLtAlI9JV8JVnX65c66/HB3LG63oN6L/Wss+AaNEg6BhkcC4nFfPzxx4eUD6+3az794JLSZ6csiDEV0aHy2pp0v8dvszciMPkE3e9DPcu8/jrpCGQCLCQW4vF40NDQgIKCggMfKy4uBvBNMYnKsG8B2RNjjUd0QH5NaVzus+TbjVCSkuJyLzq6pEmTkHzyydIxyARYSCxuyZIlWLZsGTweT2wXOvlGTfIQAcBgXyUGujN1v89mVw0+nTtB9/tQ9zJv5HsH9Q4LiYUtWrQIl19+ORYs0GDKJW8+kMytuUk7eUnxWVPwq5wNCOeNicu96FCJxx2H1G+fLR2DTIKFxKJWr16NUaNGYeHChdpc0OUGzrhNm2sRAcgLxucR0IgC/HZGB5REHoUQb5k3fo+P+lKvsZBY0NfrRr4eGfH5fNE/ZXOwU24EUrlSnrSR37g/bvf6KLEK22dzHVQ8uYYORf8LL5SOQSbCQmIxJSUlKCkpQUFBAcrKylBWVoaioiJkZGTEfnFXEjDlztivQwRgfPVWOBRH3O73i5GfIjKWu7jGS8b110Fxcu9N6j0lEolEpEOQNnw+H0aOHHnUR3w1+7856Af+OAloqtTmemRrc/Om4IuWPXG739T24bj54d1AUL/zdAhwZGZi9NpiqG63dBQyEY6QWMjXj/1GIpEjfmjGmQic+SPtrke2ludKi+v9/pW0G7svKej5hRSTjKuvZhmhPmMhob4ruIaH7pEmJnboc57NsSweswnKSH796kVNTUX6lVdIxyATYiGhvnO4gLM0enqHbC2vrjzu9+xQgnjy4iRA5dufHjKuuRoOniNEUeDfSIrOxO8AGVwgSLHx7tuBVFdK3O/7Rkop9l44Oe73tTrnwIHI/N73pGOQSbGQUHQcTmDq3dIpyOTUSBjjU4aK3HvxhK1QcoaI3Nuqsu64A2qyvgcnknWxkFD08uYDA3iiKsUmPyKzYVmj0oG/zk4HuHGXJtwTJiBt9izpGGRiLCQUPVUFzuYoCcUmr8Undu8X+21H/bmcutHCoP93D3dlpZiwkFBsxl8K5PAkT4pe/t4vRO9/74k7oAzKEs1gdv0vvADJBXycmmLDQkKxURTg4j8AKndkpOhktuzH0GS5Iwlq1Vb8bU622P3NTklMxMC77pKOQRbAQkKxGzQeOP0W6RRkYnmJsidJP+35HM3fniSawawyrrsWriFcHEyxYyEhbUxdBKSPkE5BJpUXkD/B4r6Td0PJSJeOYSrOrCwM+OoQT6JYsZCQNlxJwEW/k05BJpXv2ysdAZWOJqydO0I6hqnwMV/SEgsJaWf0OV2PAhP10bjqbXAaYB3SYwM+Q/vp+dIxTCFp4kSkXTpbOgZZCAsJaeu8JYDbI52CTCYx2IGxqbnSMQAAvzhjLxRufX5MituN7CVL+JgvaYqFhLSVmgWc+0vpFGRC+aoxSkCpsx7vz+OGf8cy8M47kOgdKR2DLIaFhLQ36Wpg+BnSKchk8jrapCMc8LvBGxE4abx0DENKPuUUpF99tXQMsiAWEtKeogAzHwQcCdJJyETy9++UjnCI+6c2QOGCzUOoKSnIvv9+TtWQLlhISB9ZY4Apd0inIBMZXrsTnoQ06RgHbEnYh41zOEpysIF3L0JCjsxhiGR9LCSknzPvArJPlE5BJjIh2Vg7pt6fswGhPK4nAYCUqWchfT6foiP9sJCQfpwJwPyVQKJxvuslY8uPuKQjHCKiAL+d0Q4lUeZEYqNwpKUh+5dcrE76YiEhfWWMBGY/Ip2CTCKvqU46whE+TqzCtkvtvTfJoMWL4Ro4UDoGWRwLCelv3MXAqf8rnYJMIK96GxQYb8Hkz0d8isjxo6RjiOh3/vlIm3mRdAyyARYSio8ZvwCGniSdggwurd2H4SnGWkcCAEEljIcviABO+d1k48mVk4Psn/1UOgbZBAsJxYfD1bWeJImHl9Gx5SVkSEc4qn+792DXrALpGHGjJCcj55GH4fB4pKOQTbCQUPx4coFLlwEGHJIn48jvDEpH6NZ9x20CvMOkY8TFkPt/BfdYPmFE8cNCQvE15jzgjNukU5CB5dVXSUfoVocSxPKL3YDDIR1FV5k33oj+558vHYNshoWE4m/aYmDY6dIpyKDG7N0Ot8O4j9m+lVyG6osmS8fQTcqZZyLrjtulY5ANsZBQ/DmcwLwngeQB0knIgFzhAMYZ5OTf7tw7bguU3CHSMTTnGj4MQ3/7ABSV/zRQ/PGrjmT0z+4qJaqxNsIiY8hTjH2GTLPqx7OzPF3nNlmEmpyM3IcfhqN/f+koZFMsJCTHOxWY/Si4yJUOl9fWLB2hRy/3+wJ151lk6kZRkL3010g87jjpJGRjLCQkK38+cN6vpFOQweTvK5OO0Cv3TvwCyiDz72Ca+f2b0H/GDOkYZHMsJCTvtJuB02+VTkEGMqShHAMSjbkfycHq1Da8MmeQdIyY9LvgfGTdcot0DCIWEjKIGb8A8v9HOgUZSF7yYOkIvfKMZyuapplzw7SUqWdhaGEhF7GSIfCrkIxBUYBZjwCjp0snIYPID5lnbdHik3ZCyTT+iM7Bkk8+GTl/+AMUFxeWkzGwkJBxOJzAZU8BQy2yUJBikt9YKx2h16odzVgzxzw7uLrz8pDz6KNQ3W7pKEQHsJCQsSSkAFesAjJHSychYeOrt0FVzPMWVTRgM9rPmCgdo0eJx41GbtEyOFJTpKMQHcI8f9vJPlIygateAlLNsYaA9JHib4Y3Zah0jD75+elVUAy8j4crNxe5y5fDmc5DLsl4WEjImNKHA1e9CCSmSSchQRNdHukIfVLmbMB/5xpzLw/noEEYtuJJuAaa/zFlsiYWEjKuwROAa14Gksy1WJC0k+f3S0foswcHf4rOkydIxziEIz0dw55cjoScHOkoRN1iISFjGzoZuP5NoJ/1zg2hnuXVVkhHiMr9U+ugpBhjjYbavz9yn3gciaNGSUchOiYWEjK+rLHADW8BGV7pJBRno/d9gWSnsc+1OZrPXftRMucE6RhwDhyI4U8/jaTx46WjEPWIhYTMwTMMuP4tYFCedBKKIzUSxoQUc04z/HroBoQmjhW7f8LIkRjx3LNwjx0jloGoL1hIyDxSBwLXvgbkniqdhOIoT0mUjhCViAI8ML0NisBeH+78fAx/9hm4hprrKSWyNxYSMpckD3D1y8BoHgRmF3ktjdIRovZJQjW2zs6P6z1TzjwTw1eu4KO9ZDosJGQ+CcnAd54Dxs+RTkJxMHHvDukIMfnFiI2IHB+fBaX9L7kYuX96BGqy+dbdELGQkDk5XMDc5cDk66STkM4GNNcgOylLOkbUgkoYD10QBpxOXe+Tce21GLJ0Kc+mIdNiISHzUlXg4geBKXdKJyGd5bnNvZnXf9zl2DVLpxOBFQUDf3wXBt29CIpingMJiQ7HQkLmN/2nXaMlLmPs+0Dayw9KJ4jd4uM+BbzDNb2mkpCAIb9egswbbtD0ukQSWEjIGvLmAd8rBjK4+ZMV5TfslY4QM78SwhMzEwGHQ5PruYYOxfBnn0XarFmaXI9IGgsJWcegE4AF7wLHz5ROQhobt3cbnIq+azDi4e2UMlRdNDnm66RMPQsjX3oRSRO44RlZBwsJWYu7P3D5X4DpPwMUbb4TJXnuQDuOSzXnBmmHWzxuC5TcKPcHUVVk3XYrch97DI40HjxJ1sJCQtajKMCUO7r2K0keIJ2GNJLv7CcdQRPNqh9/md2/6+u0Dxzp6Rj2xOMY8IMfcPEqWRILCVmXdypw03tAzsnSSUgD+e0d0hE082rqDtSe1/upG/fEfIx86UWknH66jqmIZLGQkLWlDQWu/Qdw8vekk1CM8mp3SUfQ1OKJX0AZ3PPjzOlXXokRTz8NV3Z2HFIRyWEhIetzJgAX/Ra4dBmQkCqdhqI0Yn8Z+idYY9oGAOrUNrw8Z1C3v68mJ2PIAw9g8OJ7oSQkxDEZkQwWErKPif8D/O/7wKhzpJNQFBREkJdsrcPink3biqZzjtwwLflb38LIv/0NaTMvEkhFJIOFhOzFMwy4+iVg9qNAEg8fM5u8iPW2RV88eSeUzAwAgJKcjEGL78WwlSuQkGOt8kXUExYSsqcTrwBuXg+cMFs6CfVBfnO9dATNVTua8fbc4Ug+6SR4X30FGVdeyadoyJaUSCQSkQ5BJGrb68DrPwKaq6WTUA98yRk4c5C11gH1c/XDbQW34bKxl7GIkK2xkBABQEcj8Pa9QMlT0kmoBxeN/xb2tFmjPM4YPgN3n3I3Biab+/BAIi1wyoYIANxpwCUPAdf8DUgfKZ2GjiE/MVM6QswGpwzGQ9Mewu/O/h3LCNFXWEiIDuad2vUkzmk/BFTzn51iRXmBkHSEqDlVJ64adxVenfUqzs49WzoOkaFwyoaoO7U7gOKfAdtek05CB9k8NA/fSWiUjtEnqqLigpEX4OYTb0Zuv1zpOESGxEJC1JPd7wNrFgMVH0knIQAB1YXTvCPgD/mlo/TK1JypuGXSLRibMVY6CpGhsZAQ9daWV4C1Pwfqy6ST2N5VE7+NT5tKpWMcU8HAAtw++XZMGjhJOgqRKXCSnKi3xs8Gjp8JbHwGeO83QGO5dCLbylNT8Kl0iG6MTR+LWwtuxVk5Z0lHITIVjpAQRSPYCWx4Cnjvt0BzlXQa23lj7NlY2Gmskarcfrn44Yk/xAUjL+B+IkRRYCEhikXQD3y8AvjP74GWvdJpbKMyYxjOT5NO0SUrKQvfn/h9XHrcpXCp1tvaniheWEiItBDsBLa8DKwvAio/lk5jC1OPn4h6f4PY/cekj8FV467Chd4LkehIFMtBZBUsJERaqywBPnoC2PwiEOyQTmNZt0w6D+/6tsb1nqqiYmrOVFw17iqckn1KXO9NZHUsJER6aasHSv4MfPQk0LhHOo3lPD7xQvyxaXNc7tUvoR9mjZqFK8ZdwX1EiHTCQkKkt3AY+OLNrumcsncB8K+cFj4YeQpuhL7rdiZmTcT8MfNx3ojz4Ha6db0Xkd2xkBDFU+2Orumcjc8C/ibpNKbW4u6PM4ZkIBwJa3rdfgn9MNM7E/PGzMOY9DGaXpuIusdCQiQh0AGUrgW2/h3Y/gbQ4ZNOZEqz805HaUtFzNdJdaViytApOGfYOTg792yOhhAJ4MZoRBJcbuD4i7p+hALAzve6ysm214HWfdLpTCPflY5SRFdIBiQNwNm5Z2Na7jScmn0qXA4+skskiSMkREYSDgPlH3SVk62vcTFsD1aNn4FftG3v9euH9x+OabnTMG3YNORn5UNVeOA5kVGwkBAZWWXJV+Xk70DdDuk0hrN98DjMS2o95mvGZ47HtGHTMC13Gkanj45TMiLqKxYSIrNoqgbKPwTK13f9XP0pEA5IpxIVUhw4bfRotAfbD3xseP/hODHrRBQMKsDpQ07H4JTBggmJqLdYSIjMKtABVJUcVFLWA2210qniy+nGr06/Aon9hmLSwEk4ceCJyEzKlE6FGTNmYM2aNdIxiEyFhYTISmq//KqgfNg13VNfBgSOPaVhGo5EYPAEIPtEYMiJwJBJQNY4wGGstfmrV6/G/PnzwbdWor4x1t9kIorNgNFdPyZd+c3HmmuAhp1A/c6ugnLwf7fXy2U9mpQswDPssB/Du35kjAQM/iSMz+dDfb3B/jclMgmOkBDZWUdjVzlp+KqgNFUDnS2Av/mrn1uAztZDPxYO9u7ajsSux5tdyYDzq5+//nVy5mGF46v/TkjW98+rs6KiIlx22WVIT0/nCAlRH7GQEFHfBDq+KinNXYUlEgKcSYDroB/OJEC11yO1xcXF8Hq9yMjIYCEhigKnbIiob1zurh8p8otHjcTn88Hr9cLn80lHITIle30LQ0Skg6KiIsybN086BpGpsZAQEcWgpKQEJ510knQMItPjlA0RUQzq6+tRUlKC4uJiAEBpaSkAoLCwEF6vlyMnRL3ERa1ERBoqKSnB5MmTuaiVqI84ZUNEpJHVq1djyZIlAIBFixYdGDUhop5xhISIiIjEcYSEiIiIxLGQEBERkTgWEiIiIhLHQkJERETiWEiIiIhIHAsJERERiWMhISIiInEsJERERCSOhYSIiIjEsZAQERGROBYSIiIiEsdCQkREROJYSIiIiEgcCwkRERGJYyEhIiIicSwkREREJI6FhIiIiMT9f2hJkq+WI8PwAAAAAElFTkSuQmCC", "text/plain": [ "
" ] @@ -413,7 +412,7 @@ }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -430,11 +429,275 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, + "source": [ + "# Correlated fits with a covariance of your own choosing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "##### generate a random data set" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [] + }, "outputs": [], - "source": [] + "source": [ + "def fitf(p, x):\n", + " return p[1] * anp.exp(-p[0] * x)\n", + "\n", + "num_samples = 400\n", + "N = 10\n", + "x_random = norm.rvs(size=(N, num_samples)) # generate random numbers\n", + "\n", + "r = np.zeros((N, N))\n", + "for i in range(N):\n", + " for j in range(N):\n", + " r[i, j] = np.exp(-0.8 * np.fabs(i - j)) # element in correlation matrix\n", + "\n", + "errl = np.sqrt([10.0, 2.5, 25.0, 2.8, 4.2, 4.7, 4.9, 5.1, 3.2, 4.2]) # set y errors\n", + "for i in range(N):\n", + " for j in range(N):\n", + " r[i, j] *= errl[i] * errl[j] # element in covariance matrix\n", + "\n", + "c = cholesky(r, lower=True)\n", + "y = np.dot(c, x_random)\n", + "x = np.arange(N)\n", + "\n", + "\n", + "data = []\n", + "for i in range(N):\n", + " data.append(pe.Obs([[np.exp(-(i + 1)) + np.exp(-(i + 1)) * o for o in y[i]]], ['ens']))\n", + "\n", + "data[2] = data[2]+0.05\n", + "\n", + "[o.gamma_method() for o in data]\n", + "\n", + "corr = pe.covariance(data, correlation=True)\n", + "covdiag = np.diag(1 / np.asarray([o.dvalue for o in data]))\n", + "\n", + "chol_inv = pe.obs.invert_corr_cov_cholesky(corr,covdiag)\n", + "chol_inv_keys = [\"\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.matshow(corr, vmin=-1, vmax=1)\n", + "plt.title('The full correlation matrix')\n", + "plt.set_cmap('RdBu')\n", + "plt.colorbar()\n", + "plt.draw()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### generate a block diagonal covariance matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "e=0\n", + "block_diag_corr_matrix = np.zeros((N,N))\n", + "for k in range(3):\n", + " if(k==0):\n", + " step = 4\n", + " block = pe.covariance(data[:4],correlation=True)\n", + " else:\n", + " step = 3\n", + " block = pe.covariance(data[:3],correlation=True) \n", + " block_diag_corr_matrix[e:e+step,e:e+step] += block\n", + " e+=step\n", + "\n", + "block_diag_chol_inv = pe.obs.invert_corr_cov_cholesky(block_diag_corr_matrix,covdiag)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.matshow(block_diag_corr_matrix, vmin=-1, vmax=1)\n", + "plt.title('A block diagonal correlation matrix')\n", + "plt.set_cmap('RdBu')\n", + "plt.colorbar()\n", + "plt.draw()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "#### perform a fully correlated fit and a fit with a block diagonal covariance matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fit with 2 parameters\n", + "Method: Levenberg-Marquardt\n", + "`ftol` termination condition is satisfied.\n", + "chisquare/d.o.f.: 2.3597637233070254\n", + "fit parameters [0.9754457 0.28547338]\n", + "Fit with 2 parameters\n", + "inv_chol_cov_matrix handed over as kwargs.\n", + "Method: Levenberg-Marquardt\n", + "`ftol` termination condition is satisfied.\n", + "chisquare/d.o.f.: 2.3217921000302923\n", + "fit parameters [0.9766841 0.2933594]\n" + ] + } + ], + "source": [ + "fitpc = pe.least_squares(x, data, fitf, correlated_fit=True)\n", + "fitp_inv_block_diag_cov = pe.least_squares(x, data, fitf, correlated_fit = True, inv_chol_cov_matrix = [block_diag_chol_inv,chol_inv_keys])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### generate a block diagonal covariance matrix with modified weights for particular data points + perform the fit again" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fit with 2 parameters\n", + "inv_chol_cov_matrix handed over as kwargs.\n", + "Method: Levenberg-Marquardt\n", + "`ftol` termination condition is satisfied.\n", + "chisquare/d.o.f.: 0.3401961132842267\n", + "fit parameters [0.99320618 0.33488345]\n" + ] + } + ], + "source": [ + "covdiag[2][2] = covdiag[2][2]/100. # weight the third data point less\n", + "block_diag_chol_inv_weighted = pe.obs.invert_corr_cov_cholesky(block_diag_corr_matrix,covdiag)\n", + "\n", + "fitp_inv_block_diag_cov_weighted = pe.least_squares(x, data, fitf, correlated_fit = True, inv_chol_cov_matrix = [block_diag_chol_inv_weighted,chol_inv_keys])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### compare the fully correlated fit to those with block-diagonal covariance matrices (and modified weights)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x_fit = np.arange(min(x) - 1, max(x)+ 1, 0.01)\n", + "y_fit_correlated = fitf([o.value for o in fitpc.fit_parameters], x_fit)\n", + "y_fit = fitf([o.value for o in fitp_inv_block_diag_cov.fit_parameters], x_fit)\n", + "y_fit_weighted = fitf([o.value for o in fitp_inv_block_diag_cov_weighted.fit_parameters], x_fit)\n", + "\n", + "plt.figure()\n", + "plt.errorbar(x,data,yerr=[o.dvalue for o in data])\n", + "plt.plot(x_fit, y_fit_correlated, '--',label = '$\\chi^2/\\mathrm{d.o.f.}$=' + str(round(fitpc.chisquare/fitpc.dof,2)) +': fully correlated')\n", + "plt.plot(x_fit, y_fit, '--',label = '$\\chi^2/\\mathrm{d.o.f.}$=' + str(round(fitp_inv_block_diag_cov.chisquare/fitp_inv_block_diag_cov.dof,2)) +': block-diag. cov matrix')\n", + "plt.plot(x_fit, y_fit_weighted, '--',label = '$\\chi^2/\\mathrm{d.o.f.}$=' +str(round(fitp_inv_block_diag_cov_weighted.chisquare/fitp_inv_block_diag_cov_weighted.dof,2)) + \n", + " ': block-diag. cov matrix + reduced weight 2. point')\n", + "plt.xlim(-0.5,10.0)\n", + "plt.ylim(-0.1,0.6)\n", + "plt.legend(fontsize=11)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- the fully correlated fit vs. the fit with a block diagonal covariance matrix\n", + " - the fits do not differ significantly $\\rightarrow$ the block diagonal covariance matrix can be a good estimator \n", + " (if a large fraction of the off-diagonal elements are small)\\\n", + " $\\rightarrow$ sparser matrices can be more easily/cheaply inverted/saved \n", + "- the fit with a block diagonal covariance matrix vs. the fit with \" and a decreased weight for the third data point\n", + " - the $\\chi^2/\\mathrm{d.o.f.}$ improves - decreasing/increasing the weights can be used for points that are known to be less/more 'trustworthy'" + ] } ], "metadata": { @@ -456,7 +719,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/pyerrors/fits.py b/pyerrors/fits.py index b58f62b6..10b53fff 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -14,7 +14,7 @@ from autograd import hessian as auto_hessian from autograd import elementwise_grad as egrad from numdifftools import Jacobian as num_jacobian from numdifftools import Hessian as num_hessian -from .obs import Obs, derived_observable, covariance, cov_Obs +from .obs import Obs, derived_observable, covariance, cov_Obs, invert_corr_cov_cholesky class Fit_result(Sequence): @@ -151,6 +151,14 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): For details about how the covariance matrix is estimated see `pyerrors.obs.covariance`. In practice the correlation matrix is Cholesky decomposed and inverted (instead of the covariance matrix). This procedure should be numerically more stable as the correlation matrix is typically better conditioned (Jacobi preconditioning). + inv_chol_cov_matrix [array,list], optional + array: shape = (no of y values) X (no of y values) + list: for an uncombined fit: [""] + for a combined fit: list of keys belonging to the corr_matrix saved in the array, must be the same as the keys of the y dict in alphabetical order + If correlated_fit=True is set as well, can provide an inverse covariance matrix (y errors, dy_f included!) of your own choosing for a correlated fit. + The matrix must be a lower triangular matrix constructed from a Cholesky decomposition: The function invert_corr_cov_cholesky(corr, inverrdiag) can be + used to construct it from a correlation matrix (corr) and the errors dy_f of the data points (inverrdiag = np.diag(1 / np.asarray(dy_f))). For the correct + ordering the correlation matrix (corr) can be sorted via the function sort_corr(corr, kl, yd) where kl is the list of keys and yd the y dict. expected_chisquare : bool If True estimates the expected chisquare which is corrected by effects caused by correlated input data (default False). @@ -165,6 +173,57 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): ------- output : Fit_result Parameters and information on the fitted result. + Examples + ------ + >>> # Example of a correlated (correlated_fit = True, inv_chol_cov_matrix handed over) combined fit, based on a randomly generated data set + >>> import numpy as np + >>> from scipy.stats import norm + >>> from scipy.linalg import cholesky + >>> import pyerrors as pe + >>> # generating the random data set + >>> num_samples = 400 + >>> N = 3 + >>> x = np.arange(N) + >>> x1 = norm.rvs(size=(N, num_samples)) # generate random numbers + >>> x2 = norm.rvs(size=(N, num_samples)) # generate random numbers + >>> r = r1 = r2 = np.zeros((N, N)) + >>> y = {} + >>> for i in range(N): + >>> for j in range(N): + >>> r[i, j] = np.exp(-0.8 * np.fabs(i - j)) # element in correlation matrix + >>> errl = np.sqrt([3.4, 2.5, 3.6]) # set y errors + >>> for i in range(N): + >>> for j in range(N): + >>> r[i, j] *= errl[i] * errl[j] # element in covariance matrix + >>> c = cholesky(r, lower=True) + >>> y = {'a': np.dot(c, x1), 'b': np.dot(c, x2)} # generate y data with the covariance matrix defined + >>> # random data set has been generated, now the dictionaries and the inverse covariance matrix to be handed over are built + >>> x_dict = {} + >>> y_dict = {} + >>> chol_inv_dict = {} + >>> data = [] + >>> for key in y.keys(): + >>> x_dict[key] = x + >>> for i in range(N): + >>> data.append(pe.Obs([[i + 1 + o for o in y[key][i]]], ['ens'])) # generate y Obs from the y data + >>> [o.gamma_method() for o in data] + >>> corr = pe.covariance(data, correlation=True) + >>> inverrdiag = np.diag(1 / np.asarray([o.dvalue for o in data])) + >>> chol_inv = pe.obs.invert_corr_cov_cholesky(corr, inverrdiag) # gives form of the inverse covariance matrix needed for the combined correlated fit below + >>> y_dict = {'a': data[:3], 'b': data[3:]} + >>> # common fit parameter p[0] in combined fit + >>> def fit1(p, x): + >>> return p[0] + p[1] * x + >>> def fit2(p, x): + >>> return p[0] + p[2] * x + >>> fitf_dict = {'a': fit1, 'b':fit2} + >>> fitp_inv_cov_combined_fit = pe.least_squares(x_dict,y_dict, fitf_dict, correlated_fit = True, inv_chol_cov_matrix = [chol_inv,['a','b']]) + Fit with 3 parameters + Method: Levenberg-Marquardt + `ftol` termination condition is satisfied. + chisquare/d.o.f.: 0.5388013574561786 # random + fit parameters [1.11897846 0.96361162 0.92325319] # random + ''' output = Fit_result() @@ -297,15 +356,19 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): return anp.sum(general_chisqfunc_uncorr(p, y_f, p_f) ** 2) if kwargs.get('correlated_fit') is True: - corr = covariance(y_all, correlation=True, **kwargs) - covdiag = np.diag(1 / np.asarray(dy_f)) - condn = np.linalg.cond(corr) - if condn > 0.1 / np.finfo(float).eps: - raise Exception(f"Cannot invert correlation matrix as its condition number exceeds machine precision ({condn:1.2e})") - if condn > 1e13: - warnings.warn("Correlation matrix may be ill-conditioned, condition number: {%1.2e}" % (condn), RuntimeWarning) - chol = np.linalg.cholesky(corr) - chol_inv = scipy.linalg.solve_triangular(chol, covdiag, lower=True) + if 'inv_chol_cov_matrix' in kwargs: + chol_inv = kwargs.get('inv_chol_cov_matrix') + if (chol_inv[0].shape[0] != len(dy_f)): + raise TypeError('The number of columns of the inverse covariance matrix handed over needs to be equal to the number of y errors.') + if (chol_inv[0].shape[0] != chol_inv[0].shape[1]): + raise TypeError('The inverse covariance matrix handed over needs to have the same number of rows as columns.') + if (chol_inv[1] != key_ls): + raise ValueError('The keys of inverse covariance matrix are not the same or do not appear in the same order as the x and y values.') + chol_inv = chol_inv[0] + else: + corr = covariance(y_all, correlation=True, **kwargs) + inverrdiag = np.diag(1 / np.asarray(dy_f)) + chol_inv = invert_corr_cov_cholesky(corr, inverrdiag) def general_chisqfunc(p, ivars, pr): model = anp.concatenate([anp.array(funcd[key](p, xd[key])).reshape(-1) for key in key_ls]) @@ -350,7 +413,6 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): fit_result = scipy.optimize.least_squares(chisqfunc_residuals_uncorr, x0, method='lm', ftol=1e-15, gtol=1e-15, xtol=1e-15) if kwargs.get('correlated_fit') is True: - def chisqfunc_residuals(p): return general_chisqfunc(p, y_f, p_f) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 101f6b1f..a1c2fd55 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1544,6 +1544,92 @@ def covariance(obs, visualize=False, correlation=False, smooth=None, **kwargs): return cov +def invert_corr_cov_cholesky(corr, inverrdiag): + """Constructs a lower triangular matrix `chol` via the Cholesky decomposition of the correlation matrix `corr` + and then returns the inverse covariance matrix `chol_inv` as a lower triangular matrix by solving `chol * x = inverrdiag`. + + Parameters + ---------- + corr : np.ndarray + correlation matrix + inverrdiag : np.ndarray + diagonal matrix, the entries are the inverse errors of the data points considered + """ + + condn = np.linalg.cond(corr) + if condn > 0.1 / np.finfo(float).eps: + raise Exception(f"Cannot invert correlation matrix as its condition number exceeds machine precision ({condn:1.2e})") + if condn > 1e13: + warnings.warn("Correlation matrix may be ill-conditioned, condition number: {%1.2e}" % (condn), RuntimeWarning) + chol = np.linalg.cholesky(corr) + chol_inv = scipy.linalg.solve_triangular(chol, inverrdiag, lower=True) + + return chol_inv + + +def sort_corr(corr, kl, yd): + """ Reorders a correlation matrix to match the alphabetical order of its underlying y data. + + The ordering of the input correlation matrix `corr` is given by the list of keys `kl`. + The input dictionary `yd` (with the same keys `kl`) must contain the corresponding y data + that the correlation matrix is based on. + This function sorts the list of keys `kl` alphabetically and sorts the matrix `corr` + according to this alphabetical order such that the sorted matrix `corr_sorted` corresponds + to the y data `yd` when arranged in an alphabetical order by its keys. + + Parameters + ---------- + corr : np.ndarray + A square correlation matrix constructed using the order of the y data specified by `kl`. + The dimensions of `corr` should match the total number of y data points in `yd` combined. + kl : list of str + A list of keys that denotes the order in which the y data from `yd` was used to build the + input correlation matrix `corr`. + yd : dict of list + A dictionary where each key corresponds to a unique identifier, and its value is a list of + y data points. The total number of y data points across all keys must match the dimensions + of `corr`. The lists in the dictionary can be lists of Obs. + + Returns + ------- + np.ndarray + A new, sorted correlation matrix that corresponds to the y data from `yd` when arranged alphabetically by its keys. + + Example + ------- + >>> import numpy as np + >>> import pyerrors as pe + >>> corr = np.array([[1, 0.2, 0.3], [0.2, 1, 0.4], [0.3, 0.4, 1]]) + >>> kl = ['b', 'a'] + >>> yd = {'a': [1, 2], 'b': [3]} + >>> sorted_corr = pe.obs.sort_corr(corr, kl, yd) + >>> print(sorted_corr) + array([[1. , 0.3, 0.4], + [0.3, 1. , 0.2], + [0.4, 0.2, 1. ]]) + + """ + kl_sorted = sorted(kl) + + posd = {} + ofs = 0 + for ki, k in enumerate(kl): + posd[k] = [i + ofs for i in range(len(yd[k]))] + ofs += len(posd[k]) + + mapping = [] + for k in kl_sorted: + for i in range(len(yd[k])): + mapping.append(posd[k][i]) + + corr_sorted = np.zeros_like(corr) + for i in range(corr.shape[0]): + for j in range(corr.shape[0]): + corr_sorted[i][j] = corr[mapping[i]][mapping[j]] + + return corr_sorted + + def _smooth_eigenvalues(corr, E): """Eigenvalue smoothing as described in hep-lat/9412087 diff --git a/tests/fits_test.py b/tests/fits_test.py index 3ed8f5fb..28bf3bfc 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -152,6 +152,124 @@ def test_alternative_solvers(): chisquare_values = np.array(chisquare_values) assert np.all(np.isclose(chisquare_values, chisquare_values[0])) +def test_inv_cov_matrix_input_least_squares(): + + + num_samples = 400 + N = 10 + + x = norm.rvs(size=(N, num_samples)) # generate random numbers + + r = np.zeros((N, N)) + for i in range(N): + for j in range(N): + r[i, j] = np.exp(-0.8 * np.fabs(i - j)) # element in correlation matrix + + errl = np.sqrt([3.4, 2.5, 3.6, 2.8, 4.2, 4.7, 4.9, 5.1, 3.2, 4.2]) # set y errors + for i in range(N): + for j in range(N): + r[i, j] *= errl[i] * errl[j] # element in covariance matrix + + c = cholesky(r, lower=True) + y = np.dot(c, x) + x = np.arange(N) + x_dict = {} + y_dict = {} + for i,item in enumerate(x): + x_dict[str(item)] = [x[i]] + + for linear in [True, False]: + data = [] + for i in range(N): + if linear: + data.append(pe.Obs([[i + 1 + o for o in y[i]]], ['ens'])) + else: + data.append(pe.Obs([[np.exp(-(i + 1)) + np.exp(-(i + 1)) * o for o in y[i]]], ['ens'])) + + [o.gamma_method() for o in data] + + data_dict = {} + for i,item in enumerate(x): + data_dict[str(item)] = [data[i]] + + corr = pe.covariance(data, correlation=True) + chol = np.linalg.cholesky(corr) + covdiag = np.diag(1 / np.asarray([o.dvalue for o in data])) + chol_inv = scipy.linalg.solve_triangular(chol, covdiag, lower=True) + chol_inv_keys = [""] + chol_inv_keys_combined_fit = [str(item) for i,item in enumerate(x)] + + if linear: + def fitf(p, x): + return p[1] + p[0] * x + fitf_dict = {} + for i,item in enumerate(x): + fitf_dict[str(item)] = fitf + else: + def fitf(p, x): + return p[1] * anp.exp(-p[0] * x) + fitf_dict = {} + for i,item in enumerate(x): + fitf_dict[str(item)] = fitf + + fitpc = pe.least_squares(x, data, fitf, correlated_fit=True) + fitp_inv_cov = pe.least_squares(x, data, fitf, correlated_fit = True, inv_chol_cov_matrix = [chol_inv,chol_inv_keys]) + fitp_inv_cov_combined_fit = pe.least_squares(x_dict, data_dict, fitf_dict, correlated_fit = True, inv_chol_cov_matrix = [chol_inv,chol_inv_keys_combined_fit]) + for i in range(2): + diff_inv_cov = fitp_inv_cov[i] - fitpc[i] + diff_inv_cov.gamma_method() + assert(diff_inv_cov.is_zero(atol=0.0)) + diff_inv_cov_combined_fit = fitp_inv_cov_combined_fit[i] - fitpc[i] + diff_inv_cov_combined_fit.gamma_method() + assert(diff_inv_cov_combined_fit.is_zero(atol=1e-12)) + +def test_least_squares_invalid_inv_cov_matrix_input(): + xvals = [] + yvals = [] + err = 0.1 + def func_valid(a,x): + return a[0] + a[1] * x + for x in range(1, 8, 2): + xvals.append(x) + yvals.append(pe.pseudo_Obs(x + np.random.normal(0.0, err), err, 'test1') + pe.pseudo_Obs(0, err / 100, 'test2', samples=87)) + + [o.gamma_method() for o in yvals] + + #dictionaries for a combined fit + xvals_dict = { } + yvals_dict = { } + for i,item in enumerate(np.arange(1, 8, 2)): + xvals_dict[str(item)] = [xvals[i]] + yvals_dict[str(item)] = [yvals[i]] + chol_inv_keys_combined_fit = ['1', '3', '5', '7'] + chol_inv_keys_combined_fit_invalid = ['2', '7', '100', '8'] + func_dict_valid = {"1": func_valid,"3": func_valid,"5": func_valid,"7": func_valid} + + corr_valid = pe.covariance(yvals, correlation = True) + chol = np.linalg.cholesky(corr_valid) + covdiag = np.diag(1 / np.asarray([o.dvalue for o in yvals])) + chol_inv_valid = scipy.linalg.solve_triangular(chol, covdiag, lower=True) + chol_inv_keys = [""] + pe.least_squares(xvals, yvals,func_valid, correlated_fit = True, inv_chol_cov_matrix = [chol_inv_valid,chol_inv_keys]) + pe.least_squares(xvals_dict, yvals_dict,func_dict_valid, correlated_fit = True, inv_chol_cov_matrix = [chol_inv_valid,chol_inv_keys_combined_fit]) + chol_inv_invalid_shape1 = np.zeros((len(yvals),len(yvals)-1)) + chol_inv_invalid_shape2 = np.zeros((len(yvals)+2,len(yvals))) + + # for an uncombined fit + with pytest.raises(TypeError): + pe.least_squares(xvals, yvals, func_valid, correlated_fit = True, inv_chol_cov_matrix = [chol_inv_invalid_shape1,chol_inv_keys]) + with pytest.raises(TypeError): + pe.least_squares(xvals, yvals, func_valid,correlated_fit = True, inv_chol_cov_matrix = [chol_inv_invalid_shape2,chol_inv_keys]) + with pytest.raises(ValueError): + pe.least_squares(xvals, yvals, func_valid,correlated_fit = True, inv_chol_cov_matrix = [chol_inv_valid,chol_inv_keys_combined_fit_invalid]) + + #repeat for a combined fit + with pytest.raises(TypeError): + pe.least_squares(xvals_dict, yvals_dict,func_dict_valid, correlated_fit = True, inv_chol_cov_matrix = [chol_inv_invalid_shape1,chol_inv_keys_combined_fit]) + with pytest.raises(TypeError): + pe.least_squares(xvals_dict, yvals_dict,func_dict_valid, correlated_fit = True, inv_chol_cov_matrix = [chol_inv_invalid_shape2,chol_inv_keys_combined_fit]) + with pytest.raises(ValueError): + pe.least_squares(xvals_dict, yvals_dict,func_dict_valid, correlated_fit = True, inv_chol_cov_matrix = [chol_inv_valid,chol_inv_keys_combined_fit_invalid]) def test_correlated_fit(): num_samples = 400 diff --git a/tests/obs_test.py b/tests/obs_test.py index c6ca1a23..91f20b2c 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -1063,6 +1063,27 @@ def test_covariance_reorder_non_overlapping_data(): assert np.isclose(corr1[0, 1], corr2[0, 1], atol=1e-14) +def test_sort_corr(): + xd = { + 'b': [1, 2, 3], + 'a': [2.2, 4.4], + 'c': [3.7, 5.1] + } + + yd = {k : pe.cov_Obs(xd[k], [.2 * o for o in xd[k]], k) for k in xd} + key_orig = list(yd.keys()) + y_all = np.concatenate([np.array(yd[key]) for key in key_orig]) + [o.gm() for o in y_all] + cov = pe.covariance(y_all) + + key_ls = key_sorted = sorted(key_orig) + y_sorted = np.concatenate([np.array(yd[key]) for key in key_sorted]) + [o.gm() for o in y_sorted] + cov_sorted = pe.covariance(y_sorted) + retcov = pe.obs.sort_corr(cov, key_orig, yd) + assert np.sum(retcov - cov_sorted) == 0 + + def test_empty_obs(): o = pe.Obs([np.random.rand(100)], ['test']) q = o + pe.Obs([], [], means=[]) From 4b1bb0872af63ac10b62485a58c17b97b7281926 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Sat, 14 Sep 2024 02:15:59 +0900 Subject: [PATCH 086/107] fix: corrected bug that prevented combined fits with multiple x-obs in some cases (#241) * fix: corrected bug that prevented combined fits with multiple x-obs in some cases * made test more complex * [Fix] Slightly increase tolerance for matrix function test. * Adapt test_merge_idx to compare lists --------- Co-authored-by: Simon Kuberski Co-authored-by: Fabian Joswig --- pyerrors/fits.py | 2 +- tests/fits_test.py | 14 ++++++++++++++ tests/linalg_test.py | 6 +++--- tests/obs_test.py | 6 +++--- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 10b53fff..8ed540c5 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -256,7 +256,7 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): if sorted(list(funcd.keys())) != key_ls: raise ValueError('x and func dictionaries do not contain the same keys.') - x_all = np.concatenate([np.array(xd[key]) for key in key_ls]) + x_all = np.concatenate([np.array(xd[key]).transpose() for key in key_ls]).transpose() y_all = np.concatenate([np.array(yd[key]) for key in key_ls]) y_f = [o.value for o in y_all] diff --git a/tests/fits_test.py b/tests/fits_test.py index 28bf3bfc..2eeb6a49 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -1082,6 +1082,20 @@ def test_combined_resplot_qqplot(): fr = pe.least_squares(xd, yd, fd, resplot=True, qqplot=True) plt.close('all') +def test_combined_fit_xerr(): + fitd = { + 'a' : lambda p, x: p[0] * x[0] + p[1] * x[1], + 'b' : lambda p, x: p[0] * x[0] + p[2] * x[1], + 'c' : lambda p, x: p[0] * x[0] + p[3] * x[1], + } + yd = { + 'a': [pe.cov_Obs(3 + .1 * np.random.uniform(), .1**2, 'a' + str(i)) for i in range(5)], + 'b': [pe.cov_Obs(1 + .1 * np.random.uniform(), .1**2, 'b' + str(i)) for i in range(6)], + 'c': [pe.cov_Obs(3 + .1 * np.random.uniform(), .1**2, 'c' + str(i)) for i in range(3)], + } + xd = {k: np.transpose([[1 + .01 * np.random.uniform(), 2] for i in range(len(yd[k]))]) for k in fitd} + pe.fits.least_squares(xd, yd, fitd) + def test_x_multidim_fit(): x1 = np.arange(1, 10) diff --git a/tests/linalg_test.py b/tests/linalg_test.py index 329becb3..4fb952d3 100644 --- a/tests/linalg_test.py +++ b/tests/linalg_test.py @@ -276,10 +276,10 @@ def test_matrix_functions(): for (i, j), entry in np.ndenumerate(check_inv): entry.gamma_method() if(i == j): - assert math.isclose(entry.value, 1.0, abs_tol=1e-9), 'value ' + str(i) + ',' + str(j) + ' ' + str(entry.value) + assert math.isclose(entry.value, 1.0, abs_tol=2e-9), 'value ' + str(i) + ',' + str(j) + ' ' + str(entry.value) else: - assert math.isclose(entry.value, 0.0, abs_tol=1e-9), 'value ' + str(i) + ',' + str(j) + ' ' + str(entry.value) - assert math.isclose(entry.dvalue, 0.0, abs_tol=1e-9), 'dvalue ' + str(i) + ',' + str(j) + ' ' + str(entry.dvalue) + assert math.isclose(entry.value, 0.0, abs_tol=2e-9), 'value ' + str(i) + ',' + str(j) + ' ' + str(entry.value) + assert math.isclose(entry.dvalue, 0.0, abs_tol=2e-9), 'dvalue ' + str(i) + ',' + str(j) + ' ' + str(entry.dvalue) # Check Cholesky decomposition sym = np.dot(matrix, matrix.T) diff --git a/tests/obs_test.py b/tests/obs_test.py index 91f20b2c..726ecffa 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -554,11 +554,11 @@ def test_merge_idx(): for j in range(5): idll = [range(1, int(round(np.random.uniform(300, 700))), int(round(np.random.uniform(1, 14)))) for i in range(10)] - assert pe.obs._merge_idx(idll) == sorted(set().union(*idll)) + assert list(pe.obs._merge_idx(idll)) == sorted(set().union(*idll)) for j in range(5): idll = [range(int(round(np.random.uniform(1, 28))), int(round(np.random.uniform(300, 700))), int(round(np.random.uniform(1, 14)))) for i in range(10)] - assert pe.obs._merge_idx(idll) == sorted(set().union(*idll)) + assert list(pe.obs._merge_idx(idll)) == sorted(set().union(*idll)) idl = [list(np.arange(1, 14)) + list(range(16, 100, 4)), range(4, 604, 4), [2, 4, 5, 6, 8, 9, 12, 24], range(1, 20, 1), range(50, 789, 7)] new_idx = pe.obs._merge_idx(idl) @@ -1457,4 +1457,4 @@ def test_missing_replica(): for op in [[O1O2, O1O2b], [O1O2O3, O1O2O3b]]: assert np.isclose(op[1].value, op[0].value) - assert np.isclose(op[1].dvalue, op[0].dvalue, atol=0, rtol=5e-2) \ No newline at end of file + assert np.isclose(op[1].dvalue, op[0].dvalue, atol=0, rtol=5e-2) From b43a2cbd347209f6cf43c74ca359ccbde7b3ff25 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 14 Oct 2024 23:27:24 +0200 Subject: [PATCH 087/107] [ci] Add python 3.13 to pytest workflow. (#242) * [ci] Add python 3.13 to pytest workflow. * [ci] Remove py and pyarrow from pytest workflow --- .github/workflows/pytest.yml | 4 +--- setup.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 52ce74c7..36981809 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] include: - os: macos-latest python-version: "3.10" @@ -40,8 +40,6 @@ jobs: pip install pytest-cov pip install pytest-benchmark pip install hypothesis - pip install py - pip install pyarrow pip freeze - name: Run tests diff --git a/setup.py b/setup.py index af6a01b7..76efe7e2 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ setup(name='pyerrors', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Topic :: Scientific/Engineering :: Physics' ], ) From 47fd72b814c6e93b03d006d2109f4915ed3e1c4f Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 3 Nov 2024 16:57:20 +0100 Subject: [PATCH 088/107] [Build] Release workflow added. (#244) --- .github/workflows/release.yml | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..2548255f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,58 @@ +name: Release + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + build: + name: Build sdist and wheel + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout repository + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install pypa/build + run: >- + python3 -m + pip install + build + --user + + - name: Build wheel and source tarball + run: python3 -m build + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + if-no-files-found: error + + publish: + needs: [build] + name: Upload to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/pyerrors + permissions: + id-token: write + + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + - name: Sanity check + run: ls -la dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 From c057ecffdae82594e0bd6fa75ecca372858a7f93 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 3 Nov 2024 17:03:06 +0100 Subject: [PATCH 089/107] [Release] Updated changelog and bumped version --- CHANGELOG.md | 8 ++++++++ pyerrors/version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b59bb577..d019608c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## [2.13.0] - 2024-11-03 + +### Added +- Allow providing lower triangular matrix constructed from a Cholesky decomposition in least squares function for correlated fits. + +### Fixed +- Corrected bug that prevented combined fits with multiple x-obs in some cases. + ## [2.12.0] - 2024-08-22 ### Changed diff --git a/pyerrors/version.py b/pyerrors/version.py index 9b9dc340..930e2cd6 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.13.0-dev" +__version__ = "2.13.0" From 0ce765a99d0bcd5f7ae04c4d97a792bf83bc92d9 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 3 Nov 2024 17:07:29 +0100 Subject: [PATCH 090/107] [Version] Bumped version to 2.14.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index 930e2cd6..941c31df 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.13.0" +__version__ = "2.14.0-dev" From 30bfb55981f97b7c5e78c01b728b5f2277a74a25 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 26 Nov 2024 17:52:27 +0100 Subject: [PATCH 091/107] [Feat] Provide derivatives for pow (#246) * [Feat] Provide manual derivatives for __pow__ * [Feat] Also applied changes to rpow * [Test] Another pow test added. --- pyerrors/obs.py | 9 +++------ tests/obs_test.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index a1c2fd55..0caecfdc 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -856,15 +856,12 @@ class Obs: def __pow__(self, y): if isinstance(y, Obs): - return derived_observable(lambda x: x[0] ** x[1], [self, y]) + return derived_observable(lambda x, **kwargs: x[0] ** x[1], [self, y], man_grad=[y.value * self.value ** (y.value - 1), self.value ** y.value * np.log(self.value)]) else: - return derived_observable(lambda x: x[0] ** y, [self]) + return derived_observable(lambda x, **kwargs: x[0] ** y, [self], man_grad=[y * self.value ** (y - 1)]) def __rpow__(self, y): - if isinstance(y, Obs): - return derived_observable(lambda x: x[0] ** x[1], [y, self]) - else: - return derived_observable(lambda x: y ** x[0], [self]) + return derived_observable(lambda x, **kwargs: y ** x[0], [self], man_grad=[y ** self.value * np.log(y)]) def __abs__(self): return derived_observable(lambda x: anp.abs(x[0]), [self]) diff --git a/tests/obs_test.py b/tests/obs_test.py index 726ecffa..8b82213f 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -461,6 +461,18 @@ def test_cobs_overloading(): obs / cobs +def test_pow(): + data = [1, 2.341, pe.pseudo_Obs(4.8, 0.48, "test_obs"), pe.cov_Obs(1.1, 0.3 ** 2, "test_cov_obs")] + + for d in data: + assert d * d == d ** 2 + assert d * d * d == d ** 3 + + for d2 in data: + assert np.log(d ** d2) == d2 * np.log(d) + assert (d ** d2) ** (1 / d2) == d + + def test_reweighting(): my_obs = pe.Obs([np.random.rand(1000)], ['t']) assert not my_obs.reweighted From b1448a2703c69c169e864945a5015337ec572a5b Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:08:48 +0100 Subject: [PATCH 092/107] Fix plateaus in correlator (#247) --- pyerrors/correlators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index de1addfd..21a11533 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -862,7 +862,7 @@ class Corr: raise Exception("prange must be a list or array with two values") if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))): raise Exception("Start and end point must be integers") - if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]): + if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] <= prange[1]): raise Exception("Start and end point must define a range in the interval 0,T") self.prange = prange From d9085081202076e08b12359bb0480f27b3b3b1cc Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 18 Dec 2024 13:00:06 +0100 Subject: [PATCH 093/107] [docs] Simplify README --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index aa669ad5..7937da4d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![pytest](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml/badge.svg)](https://github.com/fjosw/pyerrors/actions/workflows/pytest.yml) [![](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![arXiv](https://img.shields.io/badge/arXiv-2209.14371-b31b1b.svg)](https://arxiv.org/abs/2209.14371) [![DOI](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2023.108750-blue)](https://doi.org/10.1016/j.cpc.2023.108750) +[![](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![arXiv](https://img.shields.io/badge/arXiv-2209.14371-b31b1b.svg)](https://arxiv.org/abs/2209.14371) [![DOI](https://img.shields.io/badge/DOI-10.1016%2Fj.cpc.2023.108750-blue)](https://doi.org/10.1016/j.cpc.2023.108750) # pyerrors `pyerrors` is a python framework for error computation and propagation of Markov chain Monte Carlo data from lattice field theory and statistical mechanics simulations. @@ -14,11 +14,6 @@ Install the most recent release using pip and [pypi](https://pypi.org/project/py python -m pip install pyerrors # Fresh install python -m pip install -U pyerrors # Update ``` -Install the most recent release using conda and [conda-forge](https://anaconda.org/conda-forge/pyerrors): -```bash -conda install -c conda-forge pyerrors # Fresh install -conda update -c conda-forge pyerrors # Update -``` ## Contributing We appreciate all contributions to the code, the documentation and the examples. If you want to get involved please have a look at our [contribution guideline](https://github.com/fjosw/pyerrors/blob/develop/CONTRIBUTING.md). From 3eac9214b40dc891e1053cc107d23cc8de00eb84 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 24 Dec 2024 15:35:59 +0100 Subject: [PATCH 094/107] [Fix] Ruff rules and more precise Excpetion types (#248) * [Fix] Fix test for membership should be 'not in' (E713) * [Fix] Fix module imported but unused (F401) * [Fix] More precise Exception types in dirac, obs and correlator --- pyerrors/__init__.py | 16 ++--- pyerrors/correlators.py | 142 ++++++++++++++++++------------------- pyerrors/dirac.py | 6 +- pyerrors/input/__init__.py | 16 ++--- pyerrors/input/dobs.py | 4 +- pyerrors/input/hadrons.py | 4 +- pyerrors/input/openQCD.py | 2 +- pyerrors/obs.py | 46 ++++++------ tests/correlators_test.py | 16 ++--- tests/dirac_test.py | 6 +- tests/obs_test.py | 28 ++++---- 11 files changed, 143 insertions(+), 143 deletions(-) diff --git a/pyerrors/__init__.py b/pyerrors/__init__.py index 2bfd688f..ca05aff4 100644 --- a/pyerrors/__init__.py +++ b/pyerrors/__init__.py @@ -481,12 +481,12 @@ from .obs import * from .correlators import * from .fits import * from .misc import * -from . import dirac -from . import input -from . import linalg -from . import mpm -from . import roots -from . import integrate -from . import special +from . import dirac as dirac +from . import input as input +from . import linalg as linalg +from . import mpm as mpm +from . import roots as roots +from . import integrate as integrate +from . import special as special -from .version import __version__ +from .version import __version__ as __version__ diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 21a11533..0375155f 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -101,7 +101,7 @@ class Corr: self.N = 1 elif all([isinstance(item, np.ndarray) or item is None for item in data_input]) and any([isinstance(item, np.ndarray) for item in data_input]): self.content = data_input - noNull = [a for a in self.content if not (a is None)] # To check if the matrices are correct for all undefined elements + noNull = [a for a in self.content if a is not None] # To check if the matrices are correct for all undefined elements self.N = noNull[0].shape[0] if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]: raise ValueError("Smearing matrices are not NxN.") @@ -141,7 +141,7 @@ class Corr: def gamma_method(self, **kwargs): """Apply the gamma method to the content of the Corr.""" for item in self.content: - if not (item is None): + if item is not None: if self.N == 1: item[0].gamma_method(**kwargs) else: @@ -159,7 +159,7 @@ class Corr: By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to """ if self.N == 1: - raise Exception("Trying to project a Corr, that already has N=1.") + raise ValueError("Trying to project a Corr, that already has N=1.") if vector_l is None: vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.]) @@ -167,16 +167,16 @@ class Corr: vector_r = vector_l if isinstance(vector_l, list) and not isinstance(vector_r, list): if len(vector_l) != self.T: - raise Exception("Length of vector list must be equal to T") + raise ValueError("Length of vector list must be equal to T") vector_r = [vector_r] * self.T if isinstance(vector_r, list) and not isinstance(vector_l, list): if len(vector_r) != self.T: - raise Exception("Length of vector list must be equal to T") + raise ValueError("Length of vector list must be equal to T") vector_l = [vector_l] * self.T if not isinstance(vector_l, list): if not vector_l.shape == vector_r.shape == (self.N,): - raise Exception("Vectors are of wrong shape!") + raise ValueError("Vectors are of wrong shape!") if normalize: vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r) newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content] @@ -201,7 +201,7 @@ class Corr: Second index to be picked. """ if self.N == 1: - raise Exception("Trying to pick item from projected Corr") + raise ValueError("Trying to pick item from projected Corr") newcontent = [None if (item is None) else item[i, j] for item in self.content] return Corr(newcontent) @@ -212,8 +212,8 @@ class Corr: timeslice and the error on each timeslice. """ if self.N != 1: - raise Exception("Can only make Corr[N=1] plottable") - x_list = [x for x in range(self.T) if not self.content[x] is None] + raise ValueError("Can only make Corr[N=1] plottable") + x_list = [x for x in range(self.T) if self.content[x] is not None] y_list = [y[0].value for y in self.content if y is not None] y_err_list = [y[0].dvalue for y in self.content if y is not None] @@ -222,9 +222,9 @@ class Corr: def symmetric(self): """ Symmetrize the correlator around x0=0.""" if self.N != 1: - raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.') + raise ValueError('symmetric cannot be safely applied to multi-dimensional correlators.') if self.T % 2 != 0: - raise Exception("Can not symmetrize odd T") + raise ValueError("Can not symmetrize odd T") if self.content[0] is not None: if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0: @@ -237,7 +237,7 @@ class Corr: else: newcontent.append(0.5 * (self.content[t] + self.content[self.T - t])) if (all([x is None for x in newcontent])): - raise Exception("Corr could not be symmetrized: No redundant values") + raise ValueError("Corr could not be symmetrized: No redundant values") return Corr(newcontent, prange=self.prange) def anti_symmetric(self): @@ -245,7 +245,7 @@ class Corr: if self.N != 1: raise TypeError('anti_symmetric cannot be safely applied to multi-dimensional correlators.') if self.T % 2 != 0: - raise Exception("Can not symmetrize odd T") + raise ValueError("Can not symmetrize odd T") test = 1 * self test.gamma_method() @@ -259,7 +259,7 @@ class Corr: else: newcontent.append(0.5 * (self.content[t] - self.content[self.T - t])) if (all([x is None for x in newcontent])): - raise Exception("Corr could not be symmetrized: No redundant values") + raise ValueError("Corr could not be symmetrized: No redundant values") return Corr(newcontent, prange=self.prange) def is_matrix_symmetric(self): @@ -292,7 +292,7 @@ class Corr: def matrix_symmetric(self): """Symmetrizes the correlator matrices on every timeslice.""" if self.N == 1: - raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.") + raise ValueError("Trying to symmetrize a correlator matrix, that already has N=1.") if self.is_matrix_symmetric(): return 1.0 * self else: @@ -336,10 +336,10 @@ class Corr: ''' if self.N == 1: - raise Exception("GEVP methods only works on correlator matrices and not single correlators.") + raise ValueError("GEVP methods only works on correlator matrices and not single correlators.") if ts is not None: if (ts <= t0): - raise Exception("ts has to be larger than t0.") + raise ValueError("ts has to be larger than t0.") if "sorted_list" in kwargs: warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning) @@ -371,9 +371,9 @@ class Corr: if sort is None: if (ts is None): - raise Exception("ts is required if sort=None.") + raise ValueError("ts is required if sort=None.") if (self.content[t0] is None) or (self.content[ts] is None): - raise Exception("Corr not defined at t0/ts.") + raise ValueError("Corr not defined at t0/ts.") Gt = _get_mat_at_t(ts) reordered_vecs = _GEVP_solver(Gt, G0, method=method, chol_inv=chol_inv) if kwargs.get('auto_gamma', False) and vector_obs: @@ -391,14 +391,14 @@ class Corr: all_vecs.append(None) if sort == "Eigenvector": if ts is None: - raise Exception("ts is required for the Eigenvector sorting method.") + raise ValueError("ts is required for the Eigenvector sorting method.") all_vecs = _sort_vectors(all_vecs, ts) reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)] if kwargs.get('auto_gamma', False) and vector_obs: [[[o.gm() for o in evn] for evn in ev if evn is not None] for ev in reordered_vecs] else: - raise Exception("Unknown value for 'sort'. Choose 'Eigenvalue', 'Eigenvector' or None.") + raise ValueError("Unknown value for 'sort'. Choose 'Eigenvalue', 'Eigenvector' or None.") if "state" in kwargs: return reordered_vecs[kwargs.get("state")] @@ -435,7 +435,7 @@ class Corr: """ if self.N != 1: - raise Exception("Multi-operator Prony not implemented!") + raise NotImplementedError("Multi-operator Prony not implemented!") array = np.empty([N, N], dtype="object") new_content = [] @@ -502,7 +502,7 @@ class Corr: correlator or a Corr of same length. """ if self.N != 1: - raise Exception("Only one-dimensional correlators can be safely correlated.") + raise ValueError("Only one-dimensional correlators can be safely correlated.") new_content = [] for x0, t_slice in enumerate(self.content): if _check_for_none(self, t_slice): @@ -516,7 +516,7 @@ class Corr: elif isinstance(partner, Obs): # Should this include CObs? new_content.append(np.array([correlate(o, partner) for o in t_slice])) else: - raise Exception("Can only correlate with an Obs or a Corr.") + raise TypeError("Can only correlate with an Obs or a Corr.") return Corr(new_content) @@ -583,7 +583,7 @@ class Corr: Available choice: symmetric, forward, backward, improved, log, default: symmetric """ if self.N != 1: - raise Exception("deriv only implemented for one-dimensional correlators.") + raise ValueError("deriv only implemented for one-dimensional correlators.") if variant == "symmetric": newcontent = [] for t in range(1, self.T - 1): @@ -592,7 +592,7 @@ class Corr: else: newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1])) if (all([x is None for x in newcontent])): - raise Exception('Derivative is undefined at all timeslices') + raise ValueError('Derivative is undefined at all timeslices') return Corr(newcontent, padding=[1, 1]) elif variant == "forward": newcontent = [] @@ -602,7 +602,7 @@ class Corr: else: newcontent.append(self.content[t + 1] - self.content[t]) if (all([x is None for x in newcontent])): - raise Exception("Derivative is undefined at all timeslices") + raise ValueError("Derivative is undefined at all timeslices") return Corr(newcontent, padding=[0, 1]) elif variant == "backward": newcontent = [] @@ -612,7 +612,7 @@ class Corr: else: newcontent.append(self.content[t] - self.content[t - 1]) if (all([x is None for x in newcontent])): - raise Exception("Derivative is undefined at all timeslices") + raise ValueError("Derivative is undefined at all timeslices") return Corr(newcontent, padding=[1, 0]) elif variant == "improved": newcontent = [] @@ -622,7 +622,7 @@ class Corr: else: newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2])) if (all([x is None for x in newcontent])): - raise Exception('Derivative is undefined at all timeslices') + raise ValueError('Derivative is undefined at all timeslices') return Corr(newcontent, padding=[2, 2]) elif variant == 'log': newcontent = [] @@ -632,11 +632,11 @@ class Corr: else: newcontent.append(np.log(self.content[t])) if (all([x is None for x in newcontent])): - raise Exception("Log is undefined at all timeslices") + raise ValueError("Log is undefined at all timeslices") logcorr = Corr(newcontent) return self * logcorr.deriv('symmetric') else: - raise Exception("Unknown variant.") + raise ValueError("Unknown variant.") def second_deriv(self, variant="symmetric"): r"""Return the second derivative of the correlator with respect to x0. @@ -656,7 +656,7 @@ class Corr: $$f(x) = \tilde{\partial}^2_0 log(f(x_0))+(\tilde{\partial}_0 log(f(x_0)))^2$$ """ if self.N != 1: - raise Exception("second_deriv only implemented for one-dimensional correlators.") + raise ValueError("second_deriv only implemented for one-dimensional correlators.") if variant == "symmetric": newcontent = [] for t in range(1, self.T - 1): @@ -665,7 +665,7 @@ class Corr: else: newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1])) if (all([x is None for x in newcontent])): - raise Exception("Derivative is undefined at all timeslices") + raise ValueError("Derivative is undefined at all timeslices") return Corr(newcontent, padding=[1, 1]) elif variant == "big_symmetric": newcontent = [] @@ -675,7 +675,7 @@ class Corr: else: newcontent.append((self.content[t + 2] - 2 * self.content[t] + self.content[t - 2]) / 4) if (all([x is None for x in newcontent])): - raise Exception("Derivative is undefined at all timeslices") + raise ValueError("Derivative is undefined at all timeslices") return Corr(newcontent, padding=[2, 2]) elif variant == "improved": newcontent = [] @@ -685,7 +685,7 @@ class Corr: else: newcontent.append((1 / 12) * (-self.content[t + 2] + 16 * self.content[t + 1] - 30 * self.content[t] + 16 * self.content[t - 1] - self.content[t - 2])) if (all([x is None for x in newcontent])): - raise Exception("Derivative is undefined at all timeslices") + raise ValueError("Derivative is undefined at all timeslices") return Corr(newcontent, padding=[2, 2]) elif variant == 'log': newcontent = [] @@ -695,11 +695,11 @@ class Corr: else: newcontent.append(np.log(self.content[t])) if (all([x is None for x in newcontent])): - raise Exception("Log is undefined at all timeslices") + raise ValueError("Log is undefined at all timeslices") logcorr = Corr(newcontent) return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2) else: - raise Exception("Unknown variant.") + raise ValueError("Unknown variant.") def m_eff(self, variant='log', guess=1.0): """Returns the effective mass of the correlator as correlator object @@ -728,7 +728,7 @@ class Corr: else: newcontent.append(self.content[t] / self.content[t + 1]) if (all([x is None for x in newcontent])): - raise Exception('m_eff is undefined at all timeslices') + raise ValueError('m_eff is undefined at all timeslices') return np.log(Corr(newcontent, padding=[0, 1])) @@ -742,7 +742,7 @@ class Corr: else: newcontent.append(self.content[t - 1] / self.content[t + 1]) if (all([x is None for x in newcontent])): - raise Exception('m_eff is undefined at all timeslices') + raise ValueError('m_eff is undefined at all timeslices') return np.log(Corr(newcontent, padding=[1, 1])) / 2 @@ -767,7 +767,7 @@ class Corr: else: newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess))) if (all([x is None for x in newcontent])): - raise Exception('m_eff is undefined at all timeslices') + raise ValueError('m_eff is undefined at all timeslices') return Corr(newcontent, padding=[0, 1]) @@ -779,11 +779,11 @@ class Corr: else: newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t])) if (all([x is None for x in newcontent])): - raise Exception("m_eff is undefined at all timeslices") + raise ValueError("m_eff is undefined at all timeslices") return np.arccosh(Corr(newcontent, padding=[1, 1])) else: - raise Exception('Unknown variant.') + raise ValueError('Unknown variant.') def fit(self, function, fitrange=None, silent=False, **kwargs): r'''Fits function to the data @@ -801,7 +801,7 @@ class Corr: Decides whether output is printed to the standard output. ''' if self.N != 1: - raise Exception("Correlator must be projected before fitting") + raise ValueError("Correlator must be projected before fitting") if fitrange is None: if self.prange: @@ -810,12 +810,12 @@ class Corr: fitrange = [0, self.T - 1] else: if not isinstance(fitrange, list): - raise Exception("fitrange has to be a list with two elements") + raise TypeError("fitrange has to be a list with two elements") if len(fitrange) != 2: - raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]") + raise ValueError("fitrange has to have exactly two elements [fit_start, fit_stop]") - xs = np.array([x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]) - ys = np.array([self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]) + xs = np.array([x for x in range(fitrange[0], fitrange[1] + 1) if self.content[x] is not None]) + ys = np.array([self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if self.content[x] is not None]) result = least_squares(xs, ys, function, silent=silent, **kwargs) return result @@ -840,9 +840,9 @@ class Corr: else: raise Exception("no plateau range provided") if self.N != 1: - raise Exception("Correlator must be projected before getting a plateau.") + raise ValueError("Correlator must be projected before getting a plateau.") if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])): - raise Exception("plateau is undefined at all timeslices in plateaurange.") + raise ValueError("plateau is undefined at all timeslices in plateaurange.") if auto_gamma: self.gamma_method() if method == "fit": @@ -854,16 +854,16 @@ class Corr: return returnvalue else: - raise Exception("Unsupported plateau method: " + method) + raise ValueError("Unsupported plateau method: " + method) def set_prange(self, prange): """Sets the attribute prange of the Corr object.""" if not len(prange) == 2: - raise Exception("prange must be a list or array with two values") + raise ValueError("prange must be a list or array with two values") if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))): - raise Exception("Start and end point must be integers") + raise TypeError("Start and end point must be integers") if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] <= prange[1]): - raise Exception("Start and end point must define a range in the interval 0,T") + raise ValueError("Start and end point must define a range in the interval 0,T") self.prange = prange return @@ -900,7 +900,7 @@ class Corr: Optional title of the figure. """ if self.N != 1: - raise Exception("Correlator must be projected before plotting") + raise ValueError("Correlator must be projected before plotting") if auto_gamma: self.gamma_method() @@ -941,7 +941,7 @@ class Corr: hide_from = None ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor']) else: - raise Exception("'comp' must be a correlator or a list of correlators.") + raise TypeError("'comp' must be a correlator or a list of correlators.") if plateau: if isinstance(plateau, Obs): @@ -950,14 +950,14 @@ class Corr: ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau)) ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-') else: - raise Exception("'plateau' must be an Obs") + raise TypeError("'plateau' must be an Obs") if references: if isinstance(references, list): for ref in references: ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--') else: - raise Exception("'references' must be a list of floating pint values.") + raise TypeError("'references' must be a list of floating pint values.") if self.prange: ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',', color="black", zorder=0) @@ -991,7 +991,7 @@ class Corr: if isinstance(save, str): fig.savefig(save, bbox_inches='tight') else: - raise Exception("'save' has to be a string.") + raise TypeError("'save' has to be a string.") def spaghetti_plot(self, logscale=True): """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations. @@ -1002,7 +1002,7 @@ class Corr: Determines whether the scale of the y-axis is logarithmic or standard. """ if self.N != 1: - raise Exception("Correlator needs to be projected first.") + raise ValueError("Correlator needs to be projected first.") mc_names = list(set([item for sublist in [sum(map(o[0].e_content.get, o[0].mc_names), []) for o in self.content if o is not None] for item in sublist])) x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None] @@ -1044,7 +1044,7 @@ class Corr: elif datatype == "pickle": dump_object(self, filename, **kwargs) else: - raise Exception("Unknown datatype " + str(datatype)) + raise ValueError("Unknown datatype " + str(datatype)) def print(self, print_range=None): print(self.__repr__(print_range)) @@ -1094,7 +1094,7 @@ class Corr: def __add__(self, y): if isinstance(y, Corr): if ((self.N != y.N) or (self.T != y.T)): - raise Exception("Addition of Corrs with different shape") + raise ValueError("Addition of Corrs with different shape") newcontent = [] for t in range(self.T): if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): @@ -1122,7 +1122,7 @@ class Corr: def __mul__(self, y): if isinstance(y, Corr): if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): - raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") + raise ValueError("Multiplication of Corr object requires N=N or N=1 and T=T") newcontent = [] for t in range(self.T): if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): @@ -1193,7 +1193,7 @@ class Corr: def __truediv__(self, y): if isinstance(y, Corr): if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): - raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") + raise ValueError("Multiplication of Corr object requires N=N or N=1 and T=T") newcontent = [] for t in range(self.T): if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): @@ -1207,16 +1207,16 @@ class Corr: newcontent[t] = None if all([item is None for item in newcontent]): - raise Exception("Division returns completely undefined correlator") + raise ValueError("Division returns completely undefined correlator") return Corr(newcontent) elif isinstance(y, (Obs, CObs)): if isinstance(y, Obs): if y.value == 0: - raise Exception('Division by zero will return undefined correlator') + raise ValueError('Division by zero will return undefined correlator') if isinstance(y, CObs): if y.is_zero(): - raise Exception('Division by zero will return undefined correlator') + raise ValueError('Division by zero will return undefined correlator') newcontent = [] for t in range(self.T): @@ -1228,7 +1228,7 @@ class Corr: elif isinstance(y, (int, float)): if y == 0: - raise Exception('Division by zero will return undefined correlator') + raise ValueError('Division by zero will return undefined correlator') newcontent = [] for t in range(self.T): if _check_for_none(self, self.content[t]): @@ -1284,7 +1284,7 @@ class Corr: if np.isnan(tmp_sum.value): newcontent[t] = None if all([item is None for item in newcontent]): - raise Exception('Operation returns undefined correlator') + raise ValueError('Operation returns undefined correlator') return Corr(newcontent) def sin(self): @@ -1392,13 +1392,13 @@ class Corr: ''' if self.N == 1: - raise Exception('Method cannot be applied to one-dimensional correlators.') + raise ValueError('Method cannot be applied to one-dimensional correlators.') if basematrix is None: basematrix = self if Ntrunc >= basematrix.N: - raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N)) + raise ValueError('Cannot truncate using Ntrunc <= %d' % (basematrix.N)) if basematrix.N != self.N: - raise Exception('basematrix and targetmatrix have to be of the same size.') + raise ValueError('basematrix and targetmatrix have to be of the same size.') evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc] diff --git a/pyerrors/dirac.py b/pyerrors/dirac.py index 48d1b547..016e4722 100644 --- a/pyerrors/dirac.py +++ b/pyerrors/dirac.py @@ -34,7 +34,7 @@ def epsilon_tensor(i, j, k): """ test_set = set((i, j, k)) if not (test_set <= set((1, 2, 3)) or test_set <= set((0, 1, 2))): - raise Exception("Unexpected input", i, j, k) + raise ValueError("Unexpected input", i, j, k) return (i - j) * (j - k) * (k - i) / 2 @@ -52,7 +52,7 @@ def epsilon_tensor_rank4(i, j, k, o): """ test_set = set((i, j, k, o)) if not (test_set <= set((1, 2, 3, 4)) or test_set <= set((0, 1, 2, 3))): - raise Exception("Unexpected input", i, j, k, o) + raise ValueError("Unexpected input", i, j, k, o) return (i - j) * (j - k) * (k - i) * (i - o) * (j - o) * (o - k) / 12 @@ -92,5 +92,5 @@ def Grid_gamma(gamma_tag): elif gamma_tag == 'SigmaZT': g = 0.5 * (gamma[2] @ gamma[3] - gamma[3] @ gamma[2]) else: - raise Exception('Unkown gamma structure', gamma_tag) + raise ValueError('Unkown gamma structure', gamma_tag) return g diff --git a/pyerrors/input/__init__.py b/pyerrors/input/__init__.py index 6910bd2a..257c5bd8 100644 --- a/pyerrors/input/__init__.py +++ b/pyerrors/input/__init__.py @@ -5,11 +5,11 @@ r''' For comparison with other analysis workflows `pyerrors` can also generate jackknife samples from an `Obs` object or import jackknife samples into an `Obs` object. See `pyerrors.obs.Obs.export_jackknife` and `pyerrors.obs.import_jackknife` for details. ''' -from . import bdio -from . import dobs -from . import hadrons -from . import json -from . import misc -from . import openQCD -from . import pandas -from . import sfcf +from . import bdio as bdio +from . import dobs as dobs +from . import hadrons as hadrons +from . import json as json +from . import misc as misc +from . import openQCD as openQCD +from . import pandas as pandas +from . import sfcf as sfcf diff --git a/pyerrors/input/dobs.py b/pyerrors/input/dobs.py index b8b005ff..aea9b7a9 100644 --- a/pyerrors/input/dobs.py +++ b/pyerrors/input/dobs.py @@ -79,7 +79,7 @@ def _dict_to_xmlstring_spaces(d, space=' '): o += space o += li + '\n' if li.startswith('<') and not cm: - if not '<%s' % ('/') in li: + if '<%s' % ('/') not in li: c += 1 cm = False return o @@ -671,7 +671,7 @@ def _dobsdict_to_xmlstring_spaces(d, space=' '): o += space o += li + '\n' if li.startswith('<') and not cm: - if not '<%s' % ('/') in li: + if '<%s' % ('/') not in li: c += 1 cm = False return o diff --git a/pyerrors/input/hadrons.py b/pyerrors/input/hadrons.py index 4390e3f0..525f564a 100644 --- a/pyerrors/input/hadrons.py +++ b/pyerrors/input/hadrons.py @@ -113,7 +113,7 @@ def read_hd5(filestem, ens_id, group, attrs=None, idl=None, part="real"): infos = [] for hd5_file in files: h5file = h5py.File(path + '/' + hd5_file, "r") - if not group + '/' + entry in h5file: + if group + '/' + entry not in h5file: raise Exception("Entry '" + entry + "' not contained in the files.") raw_data = h5file[group + '/' + entry + '/corr'] real_data = raw_data[:].view("complex") @@ -186,7 +186,7 @@ def _extract_real_arrays(path, files, tree, keys): for hd5_file in files: h5file = h5py.File(path + '/' + hd5_file, "r") for key in keys: - if not tree + '/' + key in h5file: + if tree + '/' + key not in h5file: raise Exception("Entry '" + key + "' not contained in the files.") raw_data = h5file[tree + '/' + key + '/data'] real_data = raw_data[:].astype(np.double) diff --git a/pyerrors/input/openQCD.py b/pyerrors/input/openQCD.py index 158bcaca..278977d2 100644 --- a/pyerrors/input/openQCD.py +++ b/pyerrors/input/openQCD.py @@ -47,7 +47,7 @@ def read_rwms(path, prefix, version='2.0', names=None, **kwargs): Reweighting factors read """ known_oqcd_versions = ['1.4', '1.6', '2.0'] - if not (version in known_oqcd_versions): + if version not in known_oqcd_versions: raise Exception('Unknown openQCD version defined!') print("Working with openQCD version " + version) if 'postfix' in kwargs: diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 0caecfdc..623c37fd 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -222,7 +222,7 @@ class Obs: tmp = kwargs.get(kwarg_name) if isinstance(tmp, (int, float)): if tmp < 0: - raise Exception(kwarg_name + ' has to be larger or equal to 0.') + raise ValueError(kwarg_name + ' has to be larger or equal to 0.') for e, e_name in enumerate(self.e_names): getattr(self, kwarg_name)[e_name] = tmp else: @@ -291,7 +291,7 @@ class Obs: texp = self.tau_exp[e_name] # Critical slowing down analysis if w_max // 2 <= 1: - raise Exception("Need at least 8 samples for tau_exp error analysis") + raise ValueError("Need at least 8 samples for tau_exp error analysis") for n in range(1, w_max // 2): _compute_drho(n + 1) if (self.e_rho[e_name][n] - self.N_sigma[e_name] * self.e_drho[e_name][n]) < 0 or n >= w_max // 2 - 2: @@ -620,7 +620,7 @@ class Obs: if not hasattr(self, 'e_dvalue'): raise Exception('Run the gamma method first.') if np.isclose(0.0, self._dvalue, atol=1e-15): - raise Exception('Error is 0.0') + raise ValueError('Error is 0.0') labels = self.e_names sizes = [self.e_dvalue[name] ** 2 for name in labels] / self._dvalue ** 2 fig1, ax1 = plt.subplots() @@ -659,7 +659,7 @@ class Obs: with open(file_name + '.p', 'wb') as fb: pickle.dump(self, fb) else: - raise Exception("Unknown datatype " + str(datatype)) + raise TypeError("Unknown datatype " + str(datatype)) def export_jackknife(self): """Export jackknife samples from the Obs @@ -676,7 +676,7 @@ class Obs: """ if len(self.names) != 1: - raise Exception("'export_jackknife' is only implemented for Obs defined on one ensemble and replicum.") + raise ValueError("'export_jackknife' is only implemented for Obs defined on one ensemble and replicum.") name = self.names[0] full_data = self.deltas[name] + self.r_values[name] @@ -711,7 +711,7 @@ class Obs: should agree with samples from a full bootstrap analysis up to O(1/N). """ if len(self.names) != 1: - raise Exception("'export_boostrap' is only implemented for Obs defined on one ensemble and replicum.") + raise ValueError("'export_boostrap' is only implemented for Obs defined on one ensemble and replicum.") name = self.names[0] length = self.N @@ -1267,7 +1267,7 @@ def derived_observable(func, data, array_mode=False, **kwargs): if 'man_grad' in kwargs: deriv = np.asarray(kwargs.get('man_grad')) if new_values.shape + data.shape != deriv.shape: - raise Exception('Manual derivative does not have correct shape.') + raise ValueError('Manual derivative does not have correct shape.') elif kwargs.get('num_grad') is True: if multi > 0: raise Exception('Multi mode currently not supported for numerical derivative') @@ -1333,7 +1333,7 @@ def derived_observable(func, data, array_mode=False, **kwargs): new_covobs = {name: Covobs(0, allcov[name], name, grad=new_grad[name]) for name in new_grad} if not set(new_covobs.keys()).isdisjoint(new_deltas.keys()): - raise Exception('The same name has been used for deltas and covobs!') + raise ValueError('The same name has been used for deltas and covobs!') new_samples = [] new_means = [] new_idl = [] @@ -1374,7 +1374,7 @@ def _reduce_deltas(deltas, idx_old, idx_new): Has to be a subset of idx_old. """ if not len(deltas) == len(idx_old): - raise Exception('Length of deltas and idx_old have to be the same: %d != %d' % (len(deltas), len(idx_old))) + raise ValueError('Length of deltas and idx_old have to be the same: %d != %d' % (len(deltas), len(idx_old))) if type(idx_old) is range and type(idx_new) is range: if idx_old == idx_new: return deltas @@ -1382,7 +1382,7 @@ def _reduce_deltas(deltas, idx_old, idx_new): return deltas indices = np.intersect1d(idx_old, idx_new, assume_unique=True, return_indices=True)[1] if len(indices) < len(idx_new): - raise Exception('Error in _reduce_deltas: Config of idx_new not in idx_old') + raise ValueError('Error in _reduce_deltas: Config of idx_new not in idx_old') return np.array(deltas)[indices] @@ -1404,12 +1404,12 @@ def reweight(weight, obs, **kwargs): result = [] for i in range(len(obs)): if len(obs[i].cov_names): - raise Exception('Error: Not possible to reweight an Obs that contains covobs!') + raise ValueError('Error: Not possible to reweight an Obs that contains covobs!') if not set(obs[i].names).issubset(weight.names): - raise Exception('Error: Ensembles do not fit') + raise ValueError('Error: Ensembles do not fit') for name in obs[i].names: if not set(obs[i].idl[name]).issubset(weight.idl[name]): - raise Exception('obs[%d] has to be defined on a subset of the configs in weight.idl[%s]!' % (i, name)) + raise ValueError('obs[%d] has to be defined on a subset of the configs in weight.idl[%s]!' % (i, name)) new_samples = [] w_deltas = {} for name in sorted(obs[i].names): @@ -1446,14 +1446,14 @@ def correlate(obs_a, obs_b): """ if sorted(obs_a.names) != sorted(obs_b.names): - raise Exception(f"Ensembles do not fit {set(sorted(obs_a.names)) ^ set(sorted(obs_b.names))}") + raise ValueError(f"Ensembles do not fit {set(sorted(obs_a.names)) ^ set(sorted(obs_b.names))}") if len(obs_a.cov_names) or len(obs_b.cov_names): - raise Exception('Error: Not possible to correlate Obs that contain covobs!') + raise ValueError('Error: Not possible to correlate Obs that contain covobs!') for name in obs_a.names: if obs_a.shape[name] != obs_b.shape[name]: - raise Exception('Shapes of ensemble', name, 'do not fit') + raise ValueError('Shapes of ensemble', name, 'do not fit') if obs_a.idl[name] != obs_b.idl[name]: - raise Exception('idl of ensemble', name, 'do not fit') + raise ValueError('idl of ensemble', name, 'do not fit') if obs_a.reweighted is True: warnings.warn("The first observable is already reweighted.", RuntimeWarning) @@ -1555,7 +1555,7 @@ def invert_corr_cov_cholesky(corr, inverrdiag): condn = np.linalg.cond(corr) if condn > 0.1 / np.finfo(float).eps: - raise Exception(f"Cannot invert correlation matrix as its condition number exceeds machine precision ({condn:1.2e})") + raise ValueError(f"Cannot invert correlation matrix as its condition number exceeds machine precision ({condn:1.2e})") if condn > 1e13: warnings.warn("Correlation matrix may be ill-conditioned, condition number: {%1.2e}" % (condn), RuntimeWarning) chol = np.linalg.cholesky(corr) @@ -1636,7 +1636,7 @@ def _smooth_eigenvalues(corr, E): Number of eigenvalues to be left substantially unchanged """ if not (2 < E < corr.shape[0] - 1): - raise Exception(f"'E' has to be between 2 and the dimension of the correlation matrix minus 1 ({corr.shape[0] - 1}).") + raise ValueError(f"'E' has to be between 2 and the dimension of the correlation matrix minus 1 ({corr.shape[0] - 1}).") vals, vec = np.linalg.eigh(corr) lambda_min = np.mean(vals[:-E]) vals[vals < lambda_min] = lambda_min @@ -1768,9 +1768,9 @@ def merge_obs(list_of_obs): """ replist = [item for obs in list_of_obs for item in obs.names] if (len(replist) == len(set(replist))) is False: - raise Exception('list_of_obs contains duplicate replica: %s' % (str(replist))) + raise ValueError('list_of_obs contains duplicate replica: %s' % (str(replist))) if any([len(o.cov_names) for o in list_of_obs]): - raise Exception('Not possible to merge data that contains covobs!') + raise ValueError('Not possible to merge data that contains covobs!') new_dict = {} idl_dict = {} for o in list_of_obs: @@ -1821,7 +1821,7 @@ def cov_Obs(means, cov, name, grad=None): for i in range(len(means)): ol.append(covobs_to_obs(Covobs(means[i], cov, name, pos=i, grad=grad))) if ol[0].covobs[name].N != len(means): - raise Exception('You have to provide %d mean values!' % (ol[0].N)) + raise ValueError('You have to provide %d mean values!' % (ol[0].N)) if len(ol) == 1: return ol[0] return ol @@ -1837,7 +1837,7 @@ def _determine_gap(o, e_content, e_name): gap = min(gaps) if not np.all([gi % gap == 0 for gi in gaps]): - raise Exception(f"Replica for ensemble {e_name} do not have a common spacing.", gaps) + raise ValueError(f"Replica for ensemble {e_name} do not have a common spacing.", gaps) return gap diff --git a/tests/correlators_test.py b/tests/correlators_test.py index d6d2012c..fc3528d2 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -129,7 +129,7 @@ def test_m_eff(): with pytest.warns(RuntimeWarning): my_corr.m_eff('sinh') - with pytest.raises(Exception): + with pytest.raises(ValueError): my_corr.m_eff('unkown_variant') @@ -140,7 +140,7 @@ def test_m_eff_negative_values(): assert m_eff_log[padding + 1] is None m_eff_cosh = my_corr.m_eff('cosh') assert m_eff_cosh[padding + 1] is None - with pytest.raises(Exception): + with pytest.raises(ValueError): my_corr.m_eff('logsym') @@ -155,7 +155,7 @@ def test_correlate(): my_corr = pe.correlators.Corr([pe.pseudo_Obs(10, 0.1, 't'), pe.pseudo_Obs(0, 0.05, 't')]) corr1 = my_corr.correlate(my_corr) corr2 = my_corr.correlate(my_corr[0]) - with pytest.raises(Exception): + with pytest.raises(TypeError): corr3 = my_corr.correlate(7.3) @@ -176,9 +176,9 @@ def test_fit_correlator(): assert fit_res[0] == my_corr[0] assert fit_res[1] == my_corr[1] - my_corr[0] - with pytest.raises(Exception): + with pytest.raises(TypeError): my_corr.fit(f, "from 0 to 3") - with pytest.raises(Exception): + with pytest.raises(ValueError): my_corr.fit(f, [0, 2, 3]) @@ -256,11 +256,11 @@ def test_prange(): corr = pe.correlators.Corr(corr_content) corr.set_prange([2, 4]) - with pytest.raises(Exception): + with pytest.raises(ValueError): corr.set_prange([2]) - with pytest.raises(Exception): + with pytest.raises(TypeError): corr.set_prange([2, 2.3]) - with pytest.raises(Exception): + with pytest.raises(ValueError): corr.set_prange([4, 1]) diff --git a/tests/dirac_test.py b/tests/dirac_test.py index 44812397..03605785 100644 --- a/tests/dirac_test.py +++ b/tests/dirac_test.py @@ -30,7 +30,7 @@ def test_grid_dirac(): 'SigmaYZ', 'SigmaZT']: pe.dirac.Grid_gamma(gamma) - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.dirac.Grid_gamma('Not a gamma matrix') @@ -44,7 +44,7 @@ def test_epsilon_tensor(): (1, 1, 3) : 0.0} for key, value in check.items(): assert pe.dirac.epsilon_tensor(*key) == value - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.dirac.epsilon_tensor(0, 1, 3) @@ -59,5 +59,5 @@ def test_epsilon_tensor_rank4(): (1, 2, 3, 1) : 0.0} for key, value in check.items(): assert pe.dirac.epsilon_tensor_rank4(*key) == value - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.dirac.epsilon_tensor_rank4(0, 1, 3, 4) diff --git a/tests/obs_test.py b/tests/obs_test.py index 8b82213f..2c642ad4 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -61,9 +61,9 @@ def test_Obs_exceptions(): my_obs.plot_rep_dist() with pytest.raises(Exception): my_obs.plot_piechart() - with pytest.raises(Exception): + with pytest.raises(TypeError): my_obs.gamma_method(S='2.3') - with pytest.raises(Exception): + with pytest.raises(ValueError): my_obs.gamma_method(tau_exp=2.3) my_obs.gamma_method() my_obs.details() @@ -199,7 +199,7 @@ def test_gamma_method_no_windowing(): assert np.isclose(np.sqrt(np.var(obs.deltas['ens'], ddof=1) / obs.shape['ens']), obs.dvalue) obs.gamma_method(S=1.1) assert obs.e_tauint['ens'] > 0.5 - with pytest.raises(Exception): + with pytest.raises(ValueError): obs.gamma_method(S=-0.2) @@ -490,12 +490,12 @@ def test_reweighting(): r_obs2 = r_obs[0] * my_obs assert r_obs2.reweighted my_covobs = pe.cov_Obs(1.0, 0.003, 'cov') - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.reweight(my_obs, [my_covobs]) my_obs2 = pe.Obs([np.random.rand(1000)], ['t2']) - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.reweight(my_obs, [my_obs + my_obs2]) - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.reweight(my_irregular_obs, [my_obs]) @@ -505,10 +505,10 @@ def test_merge_obs(): merged = pe.merge_obs([my_obs1, my_obs2]) diff = merged - my_obs2 - my_obs1 assert diff == -(my_obs1.value + my_obs2.value) / 2 - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.merge_obs([my_obs1, my_obs1]) my_covobs = pe.cov_Obs(1.0, 0.003, 'cov') - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.merge_obs([my_obs1, my_covobs]) @@ -531,11 +531,11 @@ def test_correlate(): assert corr1 == corr2 my_obs3 = pe.Obs([np.random.rand(100)], ['t'], idl=[range(2, 102)]) - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.correlate(my_obs1, my_obs3) my_obs4 = pe.Obs([np.random.rand(99)], ['t']) - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.correlate(my_obs1, my_obs4) my_obs5 = pe.Obs([np.random.rand(100)], ['t'], idl=[range(5, 505, 5)]) @@ -544,10 +544,10 @@ def test_correlate(): assert my_obs5.idl == corr3.idl my_new_obs = pe.Obs([np.random.rand(100)], ['q3']) - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.correlate(my_obs1, my_new_obs) my_covobs = pe.cov_Obs(1.0, 0.003, 'cov') - with pytest.raises(Exception): + with pytest.raises(ValueError): pe.correlate(my_covobs, my_covobs) r_obs = pe.reweight(my_obs1, [my_obs1])[0] with pytest.warns(RuntimeWarning): @@ -774,7 +774,7 @@ def test_gamma_method_irregular(): my_obs.gm() idl += [range(1, 400, 4)] my_obs = pe.Obs([dat for i in range(len(idl))], ['%s|%d' % ('A', i) for i in range(len(idl))], idl=idl) - with pytest.raises(Exception): + with pytest.raises(ValueError): my_obs.gm() # check cases where tau is large compared to the chain length @@ -1122,7 +1122,7 @@ def test_jackknife(): assert np.allclose(tmp_jacks, my_obs.export_jackknife()) my_new_obs = my_obs + pe.Obs([full_data], ['test2']) - with pytest.raises(Exception): + with pytest.raises(ValueError): my_new_obs.export_jackknife() From 997d360db37133b7dcbef8b9dbf1e3aff4d7c4bf Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 24 Dec 2024 17:52:08 +0100 Subject: [PATCH 095/107] [ci] Add ruff workflow (#250) * [ci] Add ruff workflow * [ci] Add src for ruff workflow * [ci] Rename ruff worklow * [ci] Adjust on for ruff workflow --- .github/workflows/ruff.yml | 15 +++++++++++++++ pyproject.toml | 3 +++ 2 files changed, 18 insertions(+) create mode 100644 .github/workflows/ruff.yml diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 00000000..2288bd3c --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,15 @@ +name: ruff +on: + push: + branches: + - master + - develop + pull_request: +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v2 + with: + src: "./pyerrors" diff --git a/pyproject.toml b/pyproject.toml index 0c4facc3..657ec5bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,6 @@ [build-system] requires = ["setuptools >= 63.0.0", "wheel"] build-backend = "setuptools.build_meta" + +[tool.ruff.lint] +ignore = ["F403"] From 9ff34c27d74a3312deda1219455568a7c065fa15 Mon Sep 17 00:00:00 2001 From: Justus Kuhlmann <82444481+jkuhl-uni@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:46:49 +0100 Subject: [PATCH 096/107] Fix/sfcf ensname (#253) * correct strings in _get_rep_names, add option for rep_sep * doc * add test for rep name getters --- pyerrors/input/sfcf.py | 19 ++++++++++--------- tests/sfcf_in_test.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/pyerrors/input/sfcf.py b/pyerrors/input/sfcf.py index e9f2837e..0431788a 100644 --- a/pyerrors/input/sfcf.py +++ b/pyerrors/input/sfcf.py @@ -127,7 +127,8 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= check_configs: list[list[int]] list of list of supposed configs, eg. [range(1,1000)] for one replicum with 1000 configs - + rep_string: str + Separator of ensemble name and replicum. Example: In "ensAr0", "r" would be the separator string. Returns ------- result: dict[list[Obs]] @@ -199,9 +200,9 @@ def read_sfcf_multi(path, prefix, name_list, quarks_list=['.*'], corr_type_list= else: ens_name = kwargs.get("ens_name") if not appended: - new_names = _get_rep_names(ls, ens_name) + new_names = _get_rep_names(ls, ens_name, rep_sep=(kwargs.get('rep_string', 'r'))) else: - new_names = _get_appended_rep_names(ls, prefix, name_list[0], ens_name) + new_names = _get_appended_rep_names(ls, prefix, name_list[0], ens_name, rep_sep=(kwargs.get('rep_string', 'r'))) new_names = sort_names(new_names) idl = [] @@ -646,22 +647,22 @@ def _read_append_rep(filename, pattern, b2b, cfg_separator, im, single): return T, rep_idl, data -def _get_rep_names(ls, ens_name=None): +def _get_rep_names(ls, ens_name=None, rep_sep='r'): new_names = [] for entry in ls: try: - idx = entry.index('r') + idx = entry.index(rep_sep) except Exception: raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.") if ens_name: - new_names.append('ens_name' + '|' + entry[idx:]) + new_names.append(ens_name + '|' + entry[idx:]) else: new_names.append(entry[:idx] + '|' + entry[idx:]) return new_names -def _get_appended_rep_names(ls, prefix, name, ens_name=None): +def _get_appended_rep_names(ls, prefix, name, ens_name=None, rep_sep='r'): new_names = [] for exc in ls: if not fnmatch.fnmatch(exc, prefix + '*.' + name): @@ -670,12 +671,12 @@ def _get_appended_rep_names(ls, prefix, name, ens_name=None): for entry in ls: myentry = entry[:-len(name) - 1] try: - idx = myentry.index('r') + idx = myentry.index(rep_sep) except Exception: raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.") if ens_name: - new_names.append('ens_name' + '|' + entry[idx:]) + new_names.append(ens_name + '|' + entry[idx:]) else: new_names.append(myentry[:idx] + '|' + myentry[idx:]) return new_names diff --git a/tests/sfcf_in_test.py b/tests/sfcf_in_test.py index f92126f9..60a71433 100644 --- a/tests/sfcf_in_test.py +++ b/tests/sfcf_in_test.py @@ -387,3 +387,33 @@ def test_find_correlator(): found_start, found_T = sfin._find_correlator(file, "2.0", "name f_A\nquarks lquark lquark\noffset 0\nwf 0", False, False) assert found_start == 21 assert found_T == 3 + + +def test_get_rep_name(): + names = ['data_r0', 'data_r1', 'data_r2'] + new_names = sfin._get_rep_names(names) + assert len(new_names) == 3 + assert new_names[0] == 'data_|r0' + assert new_names[1] == 'data_|r1' + assert new_names[2] == 'data_|r2' + names = ['data_q0', 'data_q1', 'data_q2'] + new_names = sfin._get_rep_names(names, rep_sep='q') + assert len(new_names) == 3 + assert new_names[0] == 'data_|q0' + assert new_names[1] == 'data_|q1' + assert new_names[2] == 'data_|q2' + + +def test_get_appended_rep_name(): + names = ['data_r0.f_1', 'data_r1.f_1', 'data_r2.f_1'] + new_names = sfin._get_appended_rep_names(names, 'data', 'f_1') + assert len(new_names) == 3 + assert new_names[0] == 'data_|r0' + assert new_names[1] == 'data_|r1' + assert new_names[2] == 'data_|r2' + names = ['data_q0.f_1', 'data_q1.f_1', 'data_q2.f_1'] + new_names = sfin._get_appended_rep_names(names, 'data', 'f_1', rep_sep='q') + assert len(new_names) == 3 + assert new_names[0] == 'data_|q0' + assert new_names[1] == 'data_|q1' + assert new_names[2] == 'data_|q2' From 7eabd68c5f19ad299eb677314be17985f410ee0a Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 10 Jan 2025 09:36:05 +0100 Subject: [PATCH 097/107] [CI] Speed up test workflow install phase by using uv (#254) * [CI] Speed up install phase by using uv * [CI] Use uv in examples workflow * [CI] Fix yml syntax * [CI] Install uv into system env * [CI] Add system install for examples workflow --- .github/workflows/examples.yml | 12 ++++++------ .github/workflows/pytest.yml | 14 ++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index b8220691..51322b2c 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -27,17 +27,17 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: uv + uses: astral-sh/setup-uv@v5 - name: Install run: | sudo apt-get update sudo apt-get install dvipng texlive-latex-extra texlive-fonts-recommended cm-super - python -m pip install --upgrade pip - pip install wheel - pip install . - pip install pytest - pip install nbmake - pip install -U matplotlib!=3.7.0 # Exclude version 3.7.0 of matplotlib as this breaks local imports of style files. + uv pip install wheel --system + uv pip install . --system + uv pip install pytest nbmake --system + uv pip install -U matplotlib!=3.7.0 --system # Exclude version 3.7.0 of matplotlib as this breaks local imports of style files. - name: Run tests run: pytest -vv --nbmake examples/*.ipynb diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 36981809..ff5d8223 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -30,17 +30,15 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: uv + uses: astral-sh/setup-uv@v5 - name: Install run: | - python -m pip install --upgrade pip - pip install wheel - pip install . - pip install pytest - pip install pytest-cov - pip install pytest-benchmark - pip install hypothesis - pip freeze + uv pip install wheel --system + uv pip install . --system + uv pip install pytest pytest-cov pytest-benchmark hypothesis --system + uv pip freeze --system - name: Run tests run: pytest --cov=pyerrors -vv From 6ed6ce6113d77f62d239dfa9f43a96787a1c97bc Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Thu, 13 Feb 2025 19:43:56 +0100 Subject: [PATCH 098/107] [fix] Corrected an error message (#257) Co-authored-by: Simon Kuberski --- pyerrors/fits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 8ed540c5..89f73e5b 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -293,7 +293,7 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): if len(key_ls) > 1: for key in key_ls: if np.asarray(yd[key]).shape != funcd[key](np.arange(n_parms), xd[key]).shape: - raise ValueError(f"Fit function {key} returns the wrong shape ({funcd[key](np.arange(n_parms), xd[key]).shape} instead of {xd[key].shape})\nIf the fit function is just a constant you could try adding x*0 to get the correct shape.") + raise ValueError(f"Fit function {key} returns the wrong shape ({funcd[key](np.arange(n_parms), xd[key]).shape} instead of {np.asarray(yd[key]).shape})\nIf the fit function is just a constant you could try adding x*0 to get the correct shape.") if not silent: print('Fit with', n_parms, 'parameter' + 's' * (n_parms > 1)) From 5f5438b56306237304d32264788d582fd9f06640 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Wed, 19 Feb 2025 18:15:55 +0100 Subject: [PATCH 099/107] [Feat] Introduce checks of the provided inverse matrix for correlated fits (#259) Co-authored-by: Simon Kuberski --- pyerrors/fits.py | 2 ++ tests/fits_test.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 89f73e5b..675bdca6 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -365,6 +365,8 @@ def least_squares(x, y, func, priors=None, silent=False, **kwargs): if (chol_inv[1] != key_ls): raise ValueError('The keys of inverse covariance matrix are not the same or do not appear in the same order as the x and y values.') chol_inv = chol_inv[0] + if np.any(np.diag(chol_inv) <= 0) or (not np.all(chol_inv == np.tril(chol_inv))): + raise ValueError('The inverse covariance matrix inv_chol_cov_matrix[0] has to be a lower triangular matrix constructed from a Cholesky decomposition.') else: corr = covariance(y_all, correlation=True, **kwargs) inverrdiag = np.diag(1 / np.asarray(dy_f)) diff --git a/tests/fits_test.py b/tests/fits_test.py index 2eeb6a49..283ff6a2 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -223,6 +223,9 @@ def test_inv_cov_matrix_input_least_squares(): diff_inv_cov_combined_fit.gamma_method() assert(diff_inv_cov_combined_fit.is_zero(atol=1e-12)) + with pytest.raises(ValueError): + pe.least_squares(x_dict, data_dict, fitf_dict, correlated_fit = True, inv_chol_cov_matrix = [corr,chol_inv_keys_combined_fit]) + def test_least_squares_invalid_inv_cov_matrix_input(): xvals = [] yvals = [] From dd4f8525f7325838da0f5be4ae829cdf4ccee09f Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 19 Feb 2025 18:23:56 +0100 Subject: [PATCH 100/107] [CI] Add ARM runner and bump macos runner python version to 3.12 (#260) --- .github/workflows/pytest.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index ff5d8223..a4c27116 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -20,7 +20,9 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] include: - os: macos-latest - python-version: "3.10" + python-version: "3.12" + - os: ubuntu-24.04-arm + python-version: "3.12" steps: - name: Checkout source From 17792418ed321be7f471cc27a291f5dc8671b385 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Tue, 25 Feb 2025 16:58:44 +0100 Subject: [PATCH 101/107] [Fix] Removed the possibility to create an Obs from data on several replica (#258) * [Fix] Removed the possibility to create an Obs from data on several replica * [Fix] extended tests and corrected a small bug in the previous commit --------- Co-authored-by: Simon Kuberski --- pyerrors/input/dobs.py | 3 +- pyerrors/input/json.py | 14 ++++--- pyerrors/obs.py | 15 ++++++- tests/json_io_test.py | 94 +++++++++++++++++++++++++----------------- tests/linalg_test.py | 14 +++---- tests/obs_test.py | 32 ++++++++++---- 6 files changed, 111 insertions(+), 61 deletions(-) diff --git a/pyerrors/input/dobs.py b/pyerrors/input/dobs.py index aea9b7a9..6907ec3c 100644 --- a/pyerrors/input/dobs.py +++ b/pyerrors/input/dobs.py @@ -529,7 +529,8 @@ def import_dobs_string(content, full_output=False, separator_insertion=True): deltas.append(repdeltas) idl.append(repidl) - res.append(Obs(deltas, obs_names, idl=idl)) + obsmeans = [np.average(deltas[j]) for j in range(len(deltas))] + res.append(Obs([np.array(deltas[j]) - obsmeans[j] for j in range(len(obsmeans))], obs_names, idl=idl, means=obsmeans)) res[-1]._value = mean[i] _check(len(e_names) == ne) diff --git a/pyerrors/input/json.py b/pyerrors/input/json.py index ca3fb0d2..a0d3304a 100644 --- a/pyerrors/input/json.py +++ b/pyerrors/input/json.py @@ -133,10 +133,11 @@ def create_json_string(ol, description='', indent=1): names = [] idl = [] for key, value in obs.idl.items(): - samples.append([np.nan] * len(value)) + samples.append(np.array([np.nan] * len(value))) names.append(key) idl.append(value) - my_obs = Obs(samples, names, idl) + my_obs = Obs(samples, names, idl, means=[np.nan for n in names]) + my_obs._value = np.nan my_obs._covobs = obs._covobs for name in obs._covobs: my_obs.names.append(name) @@ -331,7 +332,8 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False): cd = _gen_covobsd_from_cdatad(o.get('cdata', {})) if od: - ret = Obs([[ddi[0] + values[0] for ddi in di] for di in od['deltas']], od['names'], idl=od['idl']) + r_offsets = [np.average([ddi[0] for ddi in di]) for di in od['deltas']] + ret = Obs([np.array([ddi[0] for ddi in od['deltas'][i]]) - r_offsets[i] for i in range(len(od['deltas']))], od['names'], idl=od['idl'], means=[ro + values[0] for ro in r_offsets]) ret._value = values[0] else: ret = Obs([], [], means=[]) @@ -356,7 +358,8 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False): taglist = o.get('tag', layout * [None]) for i in range(layout): if od: - ret.append(Obs([list(di[:, i] + values[i]) for di in od['deltas']], od['names'], idl=od['idl'])) + r_offsets = np.array([np.average(di[:, i]) for di in od['deltas']]) + ret.append(Obs([od['deltas'][j][:, i] - r_offsets[j] for j in range(len(od['deltas']))], od['names'], idl=od['idl'], means=[ro + values[i] for ro in r_offsets])) ret[-1]._value = values[i] else: ret.append(Obs([], [], means=[])) @@ -383,7 +386,8 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False): taglist = o.get('tag', N * [None]) for i in range(N): if od: - ret.append(Obs([di[:, i] + values[i] for di in od['deltas']], od['names'], idl=od['idl'])) + r_offsets = np.array([np.average(di[:, i]) for di in od['deltas']]) + ret.append(Obs([od['deltas'][j][:, i] - r_offsets[j] for j in range(len(od['deltas']))], od['names'], idl=od['idl'], means=[ro + values[i] for ro in r_offsets])) ret[-1]._value = values[i] else: ret.append(Obs([], [], means=[])) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 623c37fd..87591cd9 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -82,6 +82,8 @@ class Obs: raise ValueError('Names are not unique.') if not all(isinstance(x, str) for x in names): raise TypeError('All names have to be strings.') + if len(set([o.split('|')[0] for o in names])) > 1: + raise ValueError('Cannot initialize Obs based on multiple ensembles. Please average separate Obs from each ensemble.') else: if not isinstance(names[0], str): raise TypeError('All names have to be strings.') @@ -1407,6 +1409,8 @@ def reweight(weight, obs, **kwargs): raise ValueError('Error: Not possible to reweight an Obs that contains covobs!') if not set(obs[i].names).issubset(weight.names): raise ValueError('Error: Ensembles do not fit') + if len(obs[i].mc_names) > 1 or len(weight.mc_names) > 1: + raise ValueError('Error: Cannot reweight an Obs that contains multiple ensembles.') for name in obs[i].names: if not set(obs[i].idl[name]).issubset(weight.idl[name]): raise ValueError('obs[%d] has to be defined on a subset of the configs in weight.idl[%s]!' % (i, name)) @@ -1442,9 +1446,12 @@ def correlate(obs_a, obs_b): ----- Keep in mind to only correlate primary observables which have not been reweighted yet. The reweighting has to be applied after correlating the observables. - Currently only works if ensembles are identical (this is not strictly necessary). + Only works if a single ensemble is present in the Obs. + Currently only works if ensemble content is identical (this is not strictly necessary). """ + if len(obs_a.mc_names) > 1 or len(obs_b.mc_names) > 1: + raise ValueError('Error: Cannot correlate Obs that contain multiple ensembles.') if sorted(obs_a.names) != sorted(obs_b.names): raise ValueError(f"Ensembles do not fit {set(sorted(obs_a.names)) ^ set(sorted(obs_b.names))}") if len(obs_a.cov_names) or len(obs_b.cov_names): @@ -1755,7 +1762,11 @@ def import_bootstrap(boots, name, random_numbers): def merge_obs(list_of_obs): - """Combine all observables in list_of_obs into one new observable + """Combine all observables in list_of_obs into one new observable. + This allows to merge Obs that have been computed on multiple replica + of the same ensemble. + If you like to merge Obs that are based on several ensembles, please + average them yourself. Parameters ---------- diff --git a/tests/json_io_test.py b/tests/json_io_test.py index dafaaa41..a9263691 100644 --- a/tests/json_io_test.py +++ b/tests/json_io_test.py @@ -12,7 +12,7 @@ def test_jsonio(): o = pe.pseudo_Obs(1.0, .2, 'one') o2 = pe.pseudo_Obs(0.5, .1, 'two|r1') o3 = pe.pseudo_Obs(0.5, .1, 'two|r2') - o4 = pe.merge_obs([o2, o3]) + o4 = pe.merge_obs([o2, o3, pe.pseudo_Obs(0.5, .1, 'two|r3', samples=3221)]) otag = 'This has been merged!' o4.tag = otag do = o - .2 * o4 @@ -101,8 +101,8 @@ def test_json_string_reconstruction(): def test_json_corr_io(): - my_list = [pe.Obs([np.random.normal(1.0, 0.1, 100)], ['ens1']) for o in range(8)] - rw_list = pe.reweight(pe.Obs([np.random.normal(1.0, 0.1, 100)], ['ens1']), my_list) + my_list = [pe.Obs([np.random.normal(1.0, 0.1, 100), np.random.normal(1.0, 0.1, 321)], ['ens1|r1', 'ens1|r2'], idl=[range(1, 201, 2), range(321)]) for o in range(8)] + rw_list = pe.reweight(pe.Obs([np.random.normal(1.0, 0.1, 100), np.random.normal(1.0, 0.1, 321)], ['ens1|r1', 'ens1|r2'], idl=[range(1, 201, 2), range(321)]), my_list) for obs_list in [my_list, rw_list]: for tag in [None, "test"]: @@ -111,40 +111,51 @@ def test_json_corr_io(): for corr_tag in [None, 'my_Corr_tag']: for prange in [None, [3, 6]]: for gap in [False, True]: - my_corr = pe.Corr(obs_list, padding=[pad, pad], prange=prange) - my_corr.tag = corr_tag - if gap: - my_corr.content[4] = None - pe.input.json.dump_to_json(my_corr, 'corr') - recover = pe.input.json.load_json('corr') - os.remove('corr.json.gz') - assert np.all([o.is_zero() for o in [x for x in (my_corr - recover) if x is not None]]) - for index, entry in enumerate(my_corr): - if entry is None: - assert recover[index] is None - assert my_corr.tag == recover.tag - assert my_corr.prange == recover.prange - assert my_corr.reweighted == recover.reweighted + for mult in [1., pe.cov_Obs([12.22, 1.21], [.212**2, .11**2], 'renorm')[0]]: + my_corr = mult * pe.Corr(obs_list, padding=[pad, pad], prange=prange) + my_corr.tag = corr_tag + if gap: + my_corr.content[4] = None + pe.input.json.dump_to_json(my_corr, 'corr') + recover = pe.input.json.load_json('corr') + os.remove('corr.json.gz') + assert np.all([o.is_zero() for o in [x for x in (my_corr - recover) if x is not None]]) + for index, entry in enumerate(my_corr): + if entry is None: + assert recover[index] is None + assert my_corr.tag == recover.tag + assert my_corr.prange == recover.prange + assert my_corr.reweighted == recover.reweighted def test_json_corr_2d_io(): - obs_list = [np.array([[pe.pseudo_Obs(1.0 + i, 0.1 * i, 'test'), pe.pseudo_Obs(0.0, 0.1 * i, 'test')], [pe.pseudo_Obs(0.0, 0.1 * i, 'test'), pe.pseudo_Obs(1.0 + i, 0.1 * i, 'test')]]) for i in range(4)] + obs_list = [np.array([ + [ + pe.merge_obs([pe.pseudo_Obs(1.0 + i, 0.1 * i, 'test|r2'), pe.pseudo_Obs(1.0 + i, 0.1 * i, 'test|r1', samples=321)]), + pe.merge_obs([pe.pseudo_Obs(0.0, 0.1 * i, 'test|r2'), pe.pseudo_Obs(0.0, 0.1 * i, 'test|r1', samples=321)]), + ], + [ + pe.merge_obs([pe.pseudo_Obs(0.0, 0.1 * i, 'test|r2'), pe.pseudo_Obs(0.0, 0.1 * i, 'test|r1', samples=321),]), + pe.merge_obs([pe.pseudo_Obs(1.0 + i, 0.1 * i, 'test|r2'), pe.pseudo_Obs(1.0 + i, 0.1 * i, 'test|r1', samples=321)]), + ], + ]) for i in range(4)] for tag in [None, "test"]: obs_list[3][0, 1].tag = tag for padding in [0, 1]: for prange in [None, [3, 6]]: - my_corr = pe.Corr(obs_list, padding=[padding, padding], prange=prange) - my_corr.tag = tag - pe.input.json.dump_to_json(my_corr, 'corr') - recover = pe.input.json.load_json('corr') - os.remove('corr.json.gz') - assert np.all([np.all([o.is_zero() for o in q]) for q in [x.ravel() for x in (my_corr - recover) if x is not None]]) - for index, entry in enumerate(my_corr): - if entry is None: - assert recover[index] is None - assert my_corr.tag == recover.tag - assert my_corr.prange == recover.prange + for mult in [1., pe.cov_Obs([12.22, 1.21], [.212**2, .11**2], 'renorm')[0]]: + my_corr = mult * pe.Corr(obs_list, padding=[padding, padding], prange=prange) + my_corr.tag = tag + pe.input.json.dump_to_json(my_corr, 'corr') + recover = pe.input.json.load_json('corr') + os.remove('corr.json.gz') + assert np.all([np.all([o.is_zero() for o in q]) for q in [x.ravel() for x in (my_corr - recover) if x is not None]]) + for index, entry in enumerate(my_corr): + if entry is None: + assert recover[index] is None + assert my_corr.tag == recover.tag + assert my_corr.prange == recover.prange def test_json_dict_io(): @@ -211,6 +222,7 @@ def test_json_dict_io(): 'd': pe.pseudo_Obs(.01, .001, 'testd', samples=10) * pe.cov_Obs(1, .01, 'cov1'), 'se': None, 'sf': 1.2, + 'k': pe.cov_Obs(.1, .001**2, 'cov') * pe.merge_obs([pe.pseudo_Obs(1.0, 0.1, 'test|r2'), pe.pseudo_Obs(1.0, 0.1, 'test|r1', samples=321)]), } } @@ -314,7 +326,7 @@ def test_dobsio(): o2 = pe.pseudo_Obs(0.5, .1, 'two|r1') o3 = pe.pseudo_Obs(0.5, .1, 'two|r2') - o4 = pe.merge_obs([o2, o3]) + o4 = pe.merge_obs([o2, o3, pe.pseudo_Obs(0.5, .1, 'two|r3', samples=3221)]) otag = 'This has been merged!' o4.tag = otag do = o - .2 * o4 @@ -328,7 +340,7 @@ def test_dobsio(): o5 /= co2[0] o5.tag = 2 * otag - tt1 = pe.Obs([np.random.rand(100), np.random.rand(100)], ['t|r1', 't|r2'], idl=[range(2, 202, 2), range(22, 222, 2)]) + tt1 = pe.Obs([np.random.rand(100), np.random.rand(102)], ['t|r1', 't|r2'], idl=[range(2, 202, 2), range(22, 226, 2)]) tt3 = pe.Obs([np.random.rand(102)], ['qe|r1']) tt = tt1 + tt3 @@ -337,7 +349,7 @@ def test_dobsio(): tt4 = pe.Obs([np.random.rand(100), np.random.rand(100)], ['t|r1', 't|r2'], idl=[range(1, 101, 1), range(2, 202, 2)]) - ol = [o2, o3, o4, do, o5, tt, tt4, np.log(tt4 / o5**2), np.exp(o5 + np.log(co3 / tt3 + o4) / tt)] + ol = [o2, o3, o4, do, o5, tt, tt4, np.log(tt4 / o5**2), np.exp(o5 + np.log(co3 / tt3 + o4) / tt), o4.reweight(o4)] print(ol) fname = 'test_rw' @@ -362,9 +374,12 @@ def test_dobsio(): def test_reconstruct_non_linear_r_obs(tmp_path): - to = pe.Obs([np.random.rand(500), np.random.rand(500), np.random.rand(111)], - ["e|r1", "e|r2", "my_new_ensemble_54^£$|8'[@124435%6^7&()~#"], - idl=[range(1, 501), range(0, 500), range(1, 999, 9)]) + to = ( + pe.Obs([np.random.rand(500), np.random.rand(1200)], + ["e|r1", "e|r2", ], + idl=[range(1, 501), range(0, 1200)]) + + pe.Obs([np.random.rand(111)], ["my_new_ensemble_54^£$|8'[@124435%6^7&()~#"], idl=[range(1, 999, 9)]) + ) to = np.log(to ** 2) / to to.dump((tmp_path / "test_equality").as_posix()) ro = pe.input.json.load_json((tmp_path / "test_equality").as_posix()) @@ -372,9 +387,12 @@ def test_reconstruct_non_linear_r_obs(tmp_path): def test_reconstruct_non_linear_r_obs_list(tmp_path): - to = pe.Obs([np.random.rand(500), np.random.rand(500), np.random.rand(111)], - ["e|r1", "e|r2", "my_new_ensemble_54^£$|8'[@124435%6^7&()~#"], - idl=[range(1, 501), range(0, 500), range(1, 999, 9)]) + to = ( + pe.Obs([np.random.rand(500), np.random.rand(1200)], + ["e|r1", "e|r2", ], + idl=[range(1, 501), range(0, 1200)]) + + pe.Obs([np.random.rand(111)], ["my_new_ensemble_54^£$|8'[@124435%6^7&()~#"], idl=[range(1, 999, 9)]) + ) to = np.log(to ** 2) / to for to_list in [[to, to, to], np.array([to, to, to])]: pe.input.json.dump_to_json(to_list, (tmp_path / "test_equality_list").as_posix()) diff --git a/tests/linalg_test.py b/tests/linalg_test.py index 4fb952d3..9323cfcf 100644 --- a/tests/linalg_test.py +++ b/tests/linalg_test.py @@ -34,7 +34,7 @@ def test_matmul(): my_list = [] length = 100 + np.random.randint(200) for i in range(dim ** 2): - my_list.append(pe.Obs([np.random.rand(length), np.random.rand(length + 1)], ['t1', 't2'])) + my_list.append(pe.Obs([np.random.rand(length)], ['t1']) + pe.Obs([np.random.rand(length + 1)], ['t2'])) my_array = const * np.array(my_list).reshape((dim, dim)) tt = pe.linalg.matmul(my_array, my_array) - my_array @ my_array for t, e in np.ndenumerate(tt): @@ -43,8 +43,8 @@ def test_matmul(): my_list = [] length = 100 + np.random.randint(200) for i in range(dim ** 2): - my_list.append(pe.CObs(pe.Obs([np.random.rand(length), np.random.rand(length + 1)], ['t1', 't2']), - pe.Obs([np.random.rand(length), np.random.rand(length + 1)], ['t1', 't2']))) + my_list.append(pe.CObs(pe.Obs([np.random.rand(length)], ['t1']) + pe.Obs([np.random.rand(length + 1)], ['t2']), + pe.Obs([np.random.rand(length)], ['t1']) + pe.Obs([np.random.rand(length + 1)], ['t2']))) my_array = np.array(my_list).reshape((dim, dim)) * const tt = pe.linalg.matmul(my_array, my_array) - my_array @ my_array for t, e in np.ndenumerate(tt): @@ -151,7 +151,7 @@ def test_multi_dot(): my_list = [] length = 1000 + np.random.randint(200) for i in range(dim ** 2): - my_list.append(pe.Obs([np.random.rand(length), np.random.rand(length + 1)], ['t1', 't2'])) + my_list.append(pe.Obs([np.random.rand(length)], ['t1']) + pe.Obs([np.random.rand(length + 1)], ['t2'])) my_array = pe.cov_Obs(1.0, 0.002, 'cov') * np.array(my_list).reshape((dim, dim)) tt = pe.linalg.matmul(my_array, my_array, my_array, my_array) - my_array @ my_array @ my_array @ my_array for t, e in np.ndenumerate(tt): @@ -160,8 +160,8 @@ def test_multi_dot(): my_list = [] length = 1000 + np.random.randint(200) for i in range(dim ** 2): - my_list.append(pe.CObs(pe.Obs([np.random.rand(length), np.random.rand(length + 1)], ['t1', 't2']), - pe.Obs([np.random.rand(length), np.random.rand(length + 1)], ['t1', 't2']))) + my_list.append(pe.CObs(pe.Obs([np.random.rand(length)], ['t1']) + pe.Obs([np.random.rand(length + 1)], ['t2']), + pe.Obs([np.random.rand(length)], ['t1']) + pe.Obs([np.random.rand(length + 1)], ['t2']))) my_array = np.array(my_list).reshape((dim, dim)) * pe.cov_Obs(1.0, 0.002, 'cov') tt = pe.linalg.matmul(my_array, my_array, my_array, my_array) - my_array @ my_array @ my_array @ my_array for t, e in np.ndenumerate(tt): @@ -209,7 +209,7 @@ def test_irregular_matrix_inverse(): for idl in [range(8, 508, 10), range(250, 273), [2, 8, 19, 20, 78, 99, 828, 10548979]]: irregular_array = [] for i in range(dim ** 2): - irregular_array.append(pe.Obs([np.random.normal(1.1, 0.2, len(idl)), np.random.normal(0.25, 0.1, 10)], ['ens1', 'ens2'], idl=[idl, range(1, 11)])) + irregular_array.append(pe.Obs([np.random.normal(1.1, 0.2, len(idl))], ['ens1'], idl=[idl]) + pe.Obs([np.random.normal(0.25, 0.1, 10)], ['ens2'], idl=[range(1, 11)])) irregular_matrix = np.array(irregular_array).reshape((dim, dim)) * pe.cov_Obs(1.0, 0.002, 'cov') * pe.pseudo_Obs(1.0, 0.002, 'ens2|r23') invertible_irregular_matrix = np.identity(dim) + irregular_matrix @ irregular_matrix.T diff --git a/tests/obs_test.py b/tests/obs_test.py index 2c642ad4..546a4bfd 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -333,7 +333,7 @@ def test_derived_observables(): def test_multi_ens(): names = ['A0', 'A1|r001', 'A1|r002'] - test_obs = pe.Obs([np.random.rand(50), np.random.rand(50), np.random.rand(50)], names) + test_obs = pe.Obs([np.random.rand(50)], names[:1]) + pe.Obs([np.random.rand(50), np.random.rand(50)], names[1:]) assert test_obs.e_names == ['A0', 'A1'] assert test_obs.e_content['A0'] == ['A0'] assert test_obs.e_content['A1'] == ['A1|r001', 'A1|r002'] @@ -345,6 +345,9 @@ def test_multi_ens(): ensembles.append(str(i)) assert my_sum.e_names == sorted(ensembles) + with pytest.raises(ValueError): + test_obs = pe.Obs([np.random.rand(50), np.random.rand(50), np.random.rand(50)], names) + def test_multi_ens2(): names = ['ens', 'e', 'en', 'e|r010', 'E|er', 'ens|', 'Ens|34', 'ens|r548984654ez4e3t34terh'] @@ -498,18 +501,25 @@ def test_reweighting(): with pytest.raises(ValueError): pe.reweight(my_irregular_obs, [my_obs]) + my_merged_obs = my_obs + pe.Obs([np.random.rand(1000)], ['q']) + with pytest.raises(ValueError): + pe.reweight(my_merged_obs, [my_merged_obs]) + def test_merge_obs(): - my_obs1 = pe.Obs([np.random.rand(100)], ['t']) - my_obs2 = pe.Obs([np.random.rand(100)], ['q'], idl=[range(1, 200, 2)]) + my_obs1 = pe.Obs([np.random.normal(1, .1, 100)], ['t|1']) + my_obs2 = pe.Obs([np.random.normal(1, .1, 100)], ['t|2'], idl=[range(1, 200, 2)]) merged = pe.merge_obs([my_obs1, my_obs2]) - diff = merged - my_obs2 - my_obs1 - assert diff == -(my_obs1.value + my_obs2.value) / 2 + diff = merged - (my_obs2 + my_obs1) / 2 + assert np.isclose(0, diff.value, atol=1e-16) with pytest.raises(ValueError): pe.merge_obs([my_obs1, my_obs1]) my_covobs = pe.cov_Obs(1.0, 0.003, 'cov') with pytest.raises(ValueError): pe.merge_obs([my_obs1, my_covobs]) + my_obs3 = pe.Obs([np.random.rand(100)], ['q|2'], idl=[range(1, 200, 2)]) + with pytest.raises(ValueError): + pe.merge_obs([my_obs1, my_obs3]) @@ -542,6 +552,9 @@ def test_correlate(): my_obs6 = pe.Obs([np.random.rand(100)], ['t'], idl=[range(5, 505, 5)]) corr3 = pe.correlate(my_obs5, my_obs6) assert my_obs5.idl == corr3.idl + my_obs7 = pe.Obs([np.random.rand(99)], ['q']) + with pytest.raises(ValueError): + pe.correlate(my_obs1, my_obs7) my_new_obs = pe.Obs([np.random.rand(100)], ['q3']) with pytest.raises(ValueError): @@ -681,14 +694,14 @@ def test_gamma_method_irregular(): assert (a.dvalue - 5 * a.ddvalue < expe and expe < a.dvalue + 5 * a.ddvalue) arr2 = np.random.normal(1, .2, size=N) - afull = pe.Obs([arr, arr2], ['a1', 'a2']) + afull = pe.Obs([arr], ['a1']) + pe.Obs([arr2], ['a2']) configs = np.ones_like(arr2) for i in np.random.uniform(0, len(arr2), size=int(.8*N)): configs[int(i)] = 0 zero_arr2 = [arr2[i] for i in range(len(arr2)) if not configs[i] == 0] idx2 = [i + 1 for i in range(len(configs)) if configs[i] == 1] - a = pe.Obs([zero_arr, zero_arr2], ['a1', 'a2'], idl=[idx, idx2]) + a = pe.Obs([zero_arr], ['a1'], idl=[idx]) + pe.Obs([zero_arr2], ['a2'], idl=[idx2]) afull.gamma_method() a.gamma_method() @@ -1022,7 +1035,7 @@ def test_correlation_intersection_of_idls(): def test_covariance_non_identical_objects(): - obs1 = pe.Obs([np.random.normal(1.0, 0.1, 1000), np.random.normal(1.0, 0.1, 1000), np.random.normal(1.0, 0.1, 732)], ["ens|r1", "ens|r2", "ens2"]) + obs1 = pe.Obs([np.random.normal(1.0, 0.1, 1000), np.random.normal(1.0, 0.1, 1000)], ["ens|r1", "ens|r2"]) + pe.Obs([np.random.normal(1.0, 0.1, 732)], ['ens2']) obs1.gamma_method() obs2 = obs1 + 1e-18 obs2.gamma_method() @@ -1106,6 +1119,9 @@ def test_reweight_method(): obs1 = pe.pseudo_Obs(0.2, 0.01, 'test') rw = pe.pseudo_Obs(0.999, 0.001, 'test') assert obs1.reweight(rw) == pe.reweight(rw, [obs1])[0] + rw2 = pe.pseudo_Obs(0.999, 0.001, 'test2') + with pytest.raises(ValueError): + obs1.reweight(rw2) def test_jackknife(): From b2847a1f80bce588c986a443fd38d5fae6380e7c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 9 Mar 2025 12:35:29 +0100 Subject: [PATCH 102/107] [Release] Bump version to 2.14.0 and update CHANGELOG --- CHANGELOG.md | 15 +++++++++++++++ pyerrors/version.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d019608c..7a61e766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. +## [2.14.0] - 2025-03-09 + +### Added +- Explicit checks of the provided inverse matrix for correlated fits #259 + +### Changed +- Compute derivative for pow explicitly instead of relying on autograd. This results in a ~4x speedup for pow operations #246 +- More explicit exception types #248 + +### Fixed +- Removed the possibility to create an Obs from data on several replica #258 +- Fix range in `set_prange` #247 +- Fix ensemble name handling in sfcf input modules #253 +- Correct error message for fit shape mismatch #257 + ## [2.13.0] - 2024-11-03 ### Added diff --git a/pyerrors/version.py b/pyerrors/version.py index 941c31df..d0979fd0 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.14.0-dev" +__version__ = "2.14.0" From 3c36ab08c87f7d7fb2c564037b7ddd35a3139c2d Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 9 Mar 2025 12:37:42 +0100 Subject: [PATCH 103/107] [Version] Bump version to 2.15.0-dev --- pyerrors/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index d0979fd0..806254b1 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.14.0" +__version__ = "2.15.0-dev" From 934a61e124aa73fca8f281a6f9b55df9c2b5ea58 Mon Sep 17 00:00:00 2001 From: s-kuberski Date: Tue, 22 Apr 2025 10:19:14 +0200 Subject: [PATCH 104/107] [Fix] removed unnecessary lines that raised the flake8 error code F824 (#262) Co-authored-by: Simon Kuberski --- pyerrors/input/json.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyerrors/input/json.py b/pyerrors/input/json.py index a0d3304a..a2008f9c 100644 --- a/pyerrors/input/json.py +++ b/pyerrors/input/json.py @@ -571,7 +571,6 @@ def _ol_from_dict(ind, reps='DICTOBS'): counter = 0 def dict_replace_obs(d): - nonlocal ol nonlocal counter x = {} for k, v in d.items(): @@ -592,7 +591,6 @@ def _ol_from_dict(ind, reps='DICTOBS'): return x def list_replace_obs(li): - nonlocal ol nonlocal counter x = [] for e in li: @@ -613,7 +611,6 @@ def _ol_from_dict(ind, reps='DICTOBS'): return x def obslist_replace_obs(li): - nonlocal ol nonlocal counter il = [] for e in li: @@ -694,7 +691,6 @@ def _od_from_list_and_dict(ol, ind, reps='DICTOBS'): def dict_replace_string(d): nonlocal counter - nonlocal ol x = {} for k, v in d.items(): if isinstance(v, dict): @@ -710,7 +706,6 @@ def _od_from_list_and_dict(ol, ind, reps='DICTOBS'): def list_replace_string(li): nonlocal counter - nonlocal ol x = [] for e in li: if isinstance(e, list): From dcb95265ac5c1c0dfd56d70e293290effd4d0399 Mon Sep 17 00:00:00 2001 From: JanNeuendorf <75676159+JanNeuendorf@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:26:20 +0200 Subject: [PATCH 105/107] Fixed index in GEVP example (#261) Co-authored-by: Fabian Joswig --- examples/06_gevp.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/06_gevp.ipynb b/examples/06_gevp.ipynb index 3e6c12d5..3de14d5e 100644 --- a/examples/06_gevp.ipynb +++ b/examples/06_gevp.ipynb @@ -151,7 +151,7 @@ "\n", "$$C_{\\textrm{projected}}(t)=v_1^T \\underline{C}(t) v_2$$\n", "\n", - "If we choose the vectors to be $v_1=v_2=(0,1,0,0)$, we should get the same correlator as in the cell above. \n", + "If we choose the vectors to be $v_1=v_2=(1,0,0,0)$, we should get the same correlator as in the cell above. \n", "\n", "Thinking about it this way is usefull in the Context of the generalized eigenvalue problem (GEVP), used to find the source-sink combination, which best describes a certain energy eigenstate.\n", "A good introduction is found in https://arxiv.org/abs/0902.1265." From 8183ee2ef4428307ffbc0279b3bc50b61b9a5e8d Mon Sep 17 00:00:00 2001 From: Alexander Puck Neuwirth Date: Mon, 5 May 2025 10:52:22 +0200 Subject: [PATCH 106/107] setup.py: Drop deprecated license classifiers (#264) Closes: #263 --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 76efe7e2..8c42f4a6 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,6 @@ setup(name='pyerrors', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', From d6e6a435a8203cb314af90bf397b8d5adfe53038 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 5 May 2025 17:09:40 +0200 Subject: [PATCH 107/107] [ci] Re-enable fail on warning for pytest pipeline. (#265) * [ci] Re-enable fail on warning for pytest pipeline. * [Fix] Use sqlite3 context managers in pandas module. * [Fix] Add closing context. --- .github/workflows/pytest.yml | 2 +- pyerrors/input/pandas.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index a4c27116..af98e210 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -43,4 +43,4 @@ jobs: uv pip freeze --system - name: Run tests - run: pytest --cov=pyerrors -vv + run: pytest --cov=pyerrors -vv -Werror diff --git a/pyerrors/input/pandas.py b/pyerrors/input/pandas.py index 13482983..af446cfc 100644 --- a/pyerrors/input/pandas.py +++ b/pyerrors/input/pandas.py @@ -1,6 +1,7 @@ import warnings import gzip import sqlite3 +from contextlib import closing import pandas as pd from ..obs import Obs from ..correlators import Corr @@ -29,9 +30,8 @@ def to_sql(df, table_name, db, if_exists='fail', gz=True, **kwargs): None """ se_df = _serialize_df(df, gz=gz) - con = sqlite3.connect(db) - se_df.to_sql(table_name, con, if_exists=if_exists, index=False, **kwargs) - con.close() + with closing(sqlite3.connect(db)) as con: + se_df.to_sql(table_name, con=con, if_exists=if_exists, index=False, **kwargs) def read_sql(sql, db, auto_gamma=False, **kwargs): @@ -52,9 +52,8 @@ def read_sql(sql, db, auto_gamma=False, **kwargs): data : pandas.DataFrame Dataframe with the content of the sqlite database. """ - con = sqlite3.connect(db) - extract_df = pd.read_sql(sql, con, **kwargs) - con.close() + with closing(sqlite3.connect(db)) as con: + extract_df = pd.read_sql(sql, con=con, **kwargs) return _deserialize_df(extract_df, auto_gamma=auto_gamma)