diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 2340d9d5..3c30013f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -33,4 +33,4 @@ jobs: pip install pytest-benchmark - name: Run tests - run: pytest --cov=pyerrors -v + run: pytest --cov=pyerrors -vv diff --git a/pyerrors/pyerrors.py b/pyerrors/pyerrors.py index b17a8208..8daac2bb 100644 --- a/pyerrors/pyerrors.py +++ b/pyerrors/pyerrors.py @@ -24,9 +24,6 @@ class Obs: Attributes ---------- - e_tag_global : int - Integer which determines which part of the name belongs - to the ensemble and which to the replicum. S_global : float Standard value for S (default 2.0) S_dict : dict @@ -41,12 +38,11 @@ class Obs: Standard value for N_sigma (default 1.0) """ __slots__ = ['names', 'shape', 'r_values', 'deltas', 'N', '_value', '_dvalue', - 'ddvalue', 'reweighted', 'S', 'tau_exp', 'N_sigma', 'e_names', - 'e_content', 'e_dvalue', 'e_ddvalue', 'e_tauint', 'e_dtauint', + 'ddvalue', 'reweighted', 'S', 'tau_exp', 'N_sigma', + 'e_dvalue', 'e_ddvalue', 'e_tauint', 'e_dtauint', 'e_windowsize', 'e_rho', 'e_drho', 'e_n_tauint', 'e_n_dtauint', 'idl', 'is_merged', 'tag', '__dict__'] - e_tag_global = 0 S_global = 2.0 S_dict = {} tau_exp_global = 0.0 @@ -141,6 +137,19 @@ class Obs: def dvalue(self): return self._dvalue + @property + def e_names(self): + return sorted(set([o.split('|')[0] for o in self.names])) + + @property + def e_content(self): + res = {} + for e, e_name in enumerate(self.e_names): + res[e_name] = sorted(filter(lambda x: x.startswith(e_name + '|'), self.names)) + if e_name in self.names: + res[e_name].append(e_name) + return res + def expand_deltas(self, deltas, idx, shape): """Expand deltas defined on idx to a regular, contiguous range, where holes are filled by 0. If idx is of type range, the deltas are not changed @@ -202,23 +211,12 @@ class Obs: N_sigma : float number of standard deviations from zero until the tail is attached to the autocorrelation function (default 1) - e_tag : int - number of characters which label the ensemble. The remaining - ones label replica (default 0) fft : bool determines whether the fft algorithm is used for the computation of the autocorrelation function (default True) """ - if 'e_tag' in kwargs: - e_tag_local = kwargs.get('e_tag') - if not isinstance(e_tag_local, int): - raise TypeError('Error: e_tag is not integer') - else: - e_tag_local = Obs.e_tag_global - - self.e_names = sorted(set([o[:e_tag_local] for o in self.names])) - self.e_content = {} + e_content = self.e_content self.e_dvalue = {} self.e_ddvalue = {} self.e_tauint = {} @@ -295,36 +293,26 @@ class Obs: else: self.N_sigma = Obs.N_sigma_global - if max([len(x) for x in self.names]) <= e_tag_local: - for e, e_name in enumerate(self.e_names): - self.e_content[e_name] = [e_name] - else: - for e, e_name in enumerate(self.e_names): - if len(e_name) < e_tag_local: - self.e_content[e_name] = [e_name] - else: - self.e_content[e_name] = sorted(filter(lambda x: x.startswith(e_name), self.names)) - for e, e_name in enumerate(self.e_names): r_length = [] - for r_name in self.e_content[e_name]: + for r_name in e_content[e_name]: if self.idl[r_name] is range: r_length.append(len(self.idl[r_name])) else: r_length.append((self.idl[r_name][-1] - self.idl[r_name][0] + 1)) - e_N = np.sum([self.shape[r_name] for r_name in self.e_content[e_name]]) + e_N = np.sum([self.shape[r_name] for r_name in e_content[e_name]]) w_max = max(r_length) // 2 e_gamma[e_name] = np.zeros(w_max) self.e_rho[e_name] = np.zeros(w_max) self.e_drho[e_name] = np.zeros(w_max) - for r_name in self.e_content[e_name]: + for r_name in e_content[e_name]: e_gamma[e_name] += self.calc_gamma(self.deltas[r_name], self.idl[r_name], self.shape[r_name], w_max, fft) gamma_div = np.zeros(w_max) - for r_name in self.e_content[e_name]: + for r_name in e_content[e_name]: gamma_div += self.calc_gamma(np.ones((self.shape[r_name])), self.idl[r_name], self.shape[r_name], w_max, fft) e_gamma[e_name] /= gamma_div[:w_max] @@ -384,11 +372,11 @@ class Obs: self.ddvalue += (self.e_dvalue[e_name] * self.e_ddvalue[e_name]) ** 2 self._dvalue = np.sqrt(self.dvalue) - if self.dvalue == 0.0: + if self._dvalue == 0.0: self.ddvalue = 0.0 else: self.ddvalue = np.sqrt(self.ddvalue) / self.dvalue - return 0 + return def print(self, level=1): """Print basic properties of the Obs.""" @@ -400,7 +388,7 @@ class Obs: else: percentage = np.abs(self.dvalue / self.value) * 100 print('Result\t %3.8e +/- %3.8e +/- %3.8e (%3.3f%%)' % (self.value, self.dvalue, self.ddvalue, percentage)) - if hasattr(self, 'e_names'): + if hasattr(self, 'e_dvalue'): if len(self.e_names) > 1: print(' Ensemble errors:') for e_name in self.e_names: diff --git a/tests/fits_test.py b/tests/fits_test.py index f39c9661..69a58a33 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -30,9 +30,8 @@ def test_least_squares(): out = pe.least_squares(x, oy, func) beta = out.fit_parameters - pe.Obs.e_tag_global = 5 for i in range(2): - beta[i].gamma_method(e_tag=5, S=1.0) + beta[i].gamma_method(S=1.0) assert math.isclose(beta[i].value, popt[i], abs_tol=1e-5) assert math.isclose(pcov[i, i], beta[i].dvalue ** 2, abs_tol=1e-3) assert math.isclose(pe.covariance(beta[0], beta[1]), pcov[0, 1], abs_tol=1e-3) diff --git a/tests/pyerrors_test.py b/tests/pyerrors_test.py index de025f08..ba930096 100644 --- a/tests/pyerrors_test.py +++ b/tests/pyerrors_test.py @@ -135,7 +135,7 @@ def test_fft(): test_obs2 = copy.deepcopy(test_obs1) test_obs1.gamma_method() test_obs2.gamma_method(fft=False) - assert max(np.abs(test_obs1.e_rho[''] - test_obs2.e_rho[''])) <= 10 * np.finfo(np.float64).eps + assert max(np.abs(test_obs1.e_rho['t'] - test_obs2.e_rho['t'])) <= 10 * np.finfo(np.float64).eps assert np.abs(test_obs1.dvalue - test_obs2.dvalue) <= 10 * max(test_obs1.dvalue, test_obs2.dvalue) * np.finfo(np.float64).eps @@ -190,22 +190,19 @@ def test_derived_observables(): assert i_am_one.e_ddvalue['t'] <= 2 * np.finfo(np.float64).eps -def test_multi_ens_system(): - names = [] - for i in range(100 + int(np.random.rand() * 50)): - tmp_string = '' - for _ in range(int(2 + np.random.rand() * 4)): - tmp_string += random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) - names.append(tmp_string) - names = list(set(names)) - samples = [np.random.rand(5)] * len(names) - new_obs = pe.Obs(samples, names) +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) + assert test_obs.e_names == ['A0', 'A1'] + assert test_obs.e_content['A0'] == ['A0'] + assert test_obs.e_content['A1'] == ['A1|r001', 'A1|r002'] - for e_tag_length in range(1, 6): - new_obs.gamma_method(e_tag=e_tag_length) - e_names = sorted(set([n[:e_tag_length] for n in names])) - assert e_names == new_obs.e_names - assert sorted(x for y in sorted(new_obs.e_content.values()) for x in y) == sorted(new_obs.names) + my_sum = 0 + ensembles = [] + for i in range(100): + my_sum += pe.Obs([np.random.rand(50)], [str(i)]) + ensembles.append(str(i)) + assert my_sum.e_names == sorted(ensembles) def test_overloaded_functions():