From 344da7a3d9e290bcdececadcac3dcf077ebbebca Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sat, 26 Feb 2022 08:45:33 +0000 Subject: [PATCH 01/14] feat: first version of strictly positive semi-definite covariance --- pyerrors/obs.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index aea75752..8089cf2e 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1390,8 +1390,6 @@ def covariance(obs1, obs2, correlation=False, **kwargs): dvalue = 0 e_gamma = {} e_dvalue = {} - e_n_tauint = {} - e_rho = {} for e_name in obs1.mc_names: @@ -1435,14 +1433,10 @@ def covariance(obs1, obs2, correlation=False, **kwargs): e_N += np.sum(np.ones_like(idl_d[r_name])) e_gamma[e_name] /= gamma_div[:w_max] - e_rho[e_name] = e_gamma[e_name][:w_max] / e_gamma[e_name][0] - e_n_tauint[e_name] = np.cumsum(np.concatenate(([0.5], e_rho[e_name][1:]))) - # Make sure no entry of tauint is smaller than 0.5 - e_n_tauint[e_name][e_n_tauint[e_name] < 0.5] = 0.500000000001 + tau_int = min(obs1.e_tauint[e_name], obs2.e_tauint[e_name]) - window = min(obs1.e_windowsize[e_name], obs2.e_windowsize[e_name]) # Bias correction hep-lat/0306017 eq. (49) - e_dvalue[e_name] = 2 * (e_n_tauint[e_name][window] + obs1.tau_exp[e_name] * np.abs(e_rho[e_name][window + 1])) * (1 + (2 * window + 1) / e_N) * e_gamma[e_name][0] / e_N + e_dvalue[e_name] = 2 * (tau_int) * (1 + 1 / e_N) * e_gamma[e_name][0] / e_N dvalue += e_dvalue[e_name] From 2ba59f90c0619830b92cfb019b5b1aa581688633 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 28 Feb 2022 13:18:04 +0000 Subject: [PATCH 02/14] Version number bumped to 2.1.0+dev --- pyerrors/version.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyerrors/version.py b/pyerrors/version.py index c71c5a3d..6cb8ec9d 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.0.0-rc.2+dev" +__version__ = "2.1.0+dev" diff --git a/setup.py b/setup.py index 0edc33a9..b578a727 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages setup(name='pyerrors', - version='2.0.0-rc.2+dev', + version='2.1.0+dev', description='Error analysis for lattice QCD', author='Fabian Joswig', author_email='fabian.joswig@ed.ac.uk', From 0f749fd107a4cddb665f765170f4740ad10e5bae Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 28 Feb 2022 13:25:09 +0000 Subject: [PATCH 03/14] refactor: instantiation of Obs in import_jackknife slightly optimized --- pyerrors/obs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 8089cf2e..9ad3a0d4 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1470,7 +1470,8 @@ def import_jackknife(jacks, name, idl=None): length = len(jacks) - 1 prj = (np.ones((length, length)) - (length - 1) * np.identity(length)) samples = jacks[1:] @ prj - new_obs = Obs([samples], [name], idl=idl) + mean = np.mean(samples) + new_obs = Obs([samples - mean], [name], idl=idl, means=[mean]) new_obs._value = jacks[0] return new_obs From 42df2542885d980720f92647b8dd93cd68b7ce3d Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 28 Feb 2022 13:26:48 +0000 Subject: [PATCH 04/14] refactor: else case for empty observables in Obs.__init__ simplified. --- pyerrors/obs.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 9ad3a0d4..cdf7bce8 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -90,6 +90,9 @@ class Obs: self.deltas = {} self._covobs = {} + self._value = 0 + self.N = 0 + self.is_merged = {} self.idl = {} if len(samples): if idl is not None: @@ -110,8 +113,6 @@ class Obs: for name, sample in sorted(zip(names, samples)): self.idl[name] = range(1, len(sample) + 1) - self._value = 0 - self.N = 0 if kwargs.get("means") is not None: for name, sample, mean in sorted(zip(names, samples, kwargs.get("means"))): self.shape[name] = len(self.idl[name]) @@ -129,13 +130,6 @@ class Obs: self._value += self.shape[name] * self.r_values[name] self._value /= self.N - self.is_merged = {} - - else: - self._value = 0 - self.is_merged = {} - self.N = 0 - self._dvalue = 0.0 self.ddvalue = 0.0 self.reweighted = False From 498a251072afaafd8f14c8ef4d7fa7e3b60268e1 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 28 Feb 2022 13:43:49 +0000 Subject: [PATCH 05/14] refactor!: if clause in Obs.__init__ eliminated, empty observables need to be initialized with means=[] from now on. --- pyerrors/input/json.py | 6 ++-- pyerrors/obs.py | 67 +++++++++++++++++++++--------------------- tests/obs_test.py | 4 +-- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/pyerrors/input/json.py b/pyerrors/input/json.py index a6060d5f..c4c2aa4b 100644 --- a/pyerrors/input/json.py +++ b/pyerrors/input/json.py @@ -358,7 +358,7 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False): ret = Obs([[ddi[0] + values[0] for ddi in di] for di in od['deltas']], od['names'], idl=od['idl']) ret.is_merged = od['is_merged'] else: - ret = Obs([], []) + ret = Obs([], [], means=[]) ret._value = values[0] for name in cd: co = cd[name][0] @@ -383,7 +383,7 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False): ret.append(Obs([list(di[:, i] + values[i]) for di in od['deltas']], od['names'], idl=od['idl'])) ret[-1].is_merged = od['is_merged'] else: - ret.append(Obs([], [])) + ret.append(Obs([], [], means=[])) ret[-1]._value = values[i] print('Created Obs with means= ', values[i]) for name in cd: @@ -410,7 +410,7 @@ def _parse_json_dict(json_dict, verbose=True, full_output=False): ret.append(Obs([di[:, i] + values[i] for di in od['deltas']], od['names'], idl=od['idl'])) ret[-1].is_merged = od['is_merged'] else: - ret.append(Obs([], [])) + ret.append(Obs([], [], means=[])) ret[-1]._value = values[i] for name in cd: co = cd[name][i] diff --git a/pyerrors/obs.py b/pyerrors/obs.py index cdf7bce8..1d459247 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -94,41 +94,40 @@ class Obs: self.N = 0 self.is_merged = {} self.idl = {} - if len(samples): - if idl is not None: - for name, idx in sorted(zip(names, idl)): - if isinstance(idx, range): - self.idl[name] = idx - elif isinstance(idx, (list, np.ndarray)): - dc = np.unique(np.diff(idx)) - if np.any(dc < 0): - raise Exception("Unsorted idx for idl[%s]" % (name)) - if len(dc) == 1: - self.idl[name] = range(idx[0], idx[-1] + dc[0], dc[0]) - else: - self.idl[name] = list(idx) + if idl is not None: + for name, idx in sorted(zip(names, idl)): + if isinstance(idx, range): + self.idl[name] = idx + elif isinstance(idx, (list, np.ndarray)): + dc = np.unique(np.diff(idx)) + if np.any(dc < 0): + raise Exception("Unsorted idx for idl[%s]" % (name)) + if len(dc) == 1: + self.idl[name] = range(idx[0], idx[-1] + dc[0], dc[0]) else: - raise Exception('incompatible type for idl[%s].' % (name)) - else: - for name, sample in sorted(zip(names, samples)): - self.idl[name] = range(1, len(sample) + 1) + self.idl[name] = list(idx) + else: + raise Exception('incompatible type for idl[%s].' % (name)) + else: + for name, sample in sorted(zip(names, samples)): + self.idl[name] = range(1, len(sample) + 1) - if kwargs.get("means") is not None: - for name, sample, mean in sorted(zip(names, samples, kwargs.get("means"))): - self.shape[name] = len(self.idl[name]) - self.N += self.shape[name] - self.r_values[name] = mean - self.deltas[name] = sample - else: - for name, sample in sorted(zip(names, samples)): - self.shape[name] = len(self.idl[name]) - self.N += self.shape[name] - if len(sample) != self.shape[name]: - raise Exception('Incompatible samples and idx for %s: %d vs. %d' % (name, len(sample), self.shape[name])) - self.r_values[name] = np.mean(sample) - self.deltas[name] = sample - self.r_values[name] - self._value += self.shape[name] * self.r_values[name] - self._value /= self.N + if kwargs.get("means") is not None: + for name, sample, mean in sorted(zip(names, samples, kwargs.get("means"))): + self.shape[name] = len(self.idl[name]) + self.N += self.shape[name] + self.r_values[name] = mean + self.deltas[name] = sample + else: + for name, sample in sorted(zip(names, samples)): + self.shape[name] = len(self.idl[name]) + self.N += self.shape[name] + if len(sample) != self.shape[name]: + raise Exception('Incompatible samples and idx for %s: %d vs. %d' % (name, len(sample), self.shape[name])) + self.r_values[name] = np.mean(sample) + self.deltas[name] = sample - self.r_values[name] + self._value += self.shape[name] * self.r_values[name] + self._value /= self.N self._dvalue = 0.0 self.ddvalue = 0.0 @@ -1522,7 +1521,7 @@ def cov_Obs(means, cov, name, grad=None): co : Covobs Covobs to be embedded into the Obs """ - o = Obs([], []) + o = Obs([], [], means=[]) o._value = co.value o.names.append(co.name) o._covobs[co.name] = co diff --git a/tests/obs_test.py b/tests/obs_test.py index 2fed00af..14f18c81 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -653,7 +653,7 @@ def test_covariance_symmetry(): def test_empty_obs(): o = pe.Obs([np.random.rand(100)], ['test']) - q = o + pe.Obs([], []) + q = o + pe.Obs([], [], means=[]) assert q == o @@ -704,4 +704,4 @@ def test_merge_idx(): new_idx = pe.obs._merge_idx(idl) assert(new_idx[-1] > new_idx[0]) for i in range(1, len(new_idx)): - assert(new_idx[i - 1] < new_idx[i]) \ No newline at end of file + assert(new_idx[i - 1] < new_idx[i]) From 8d0bfafaab2b796ae109d1a7cc309eeb516393c0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 8 Mar 2022 10:50:46 +0000 Subject: [PATCH 06/14] feat: spaghetti_plot method for monitoring exceptional configurations added to Corr class, tests added. --- pyerrors/correlators.py | 29 ++++++++++++++++++++++++++++- tests/correlators_test.py | 14 ++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/pyerrors/correlators.py b/pyerrors/correlators.py index 94dd2f09..ac3f8b07 100644 --- a/pyerrors/correlators.py +++ b/pyerrors/correlators.py @@ -794,7 +794,34 @@ class Corr: else: raise Exception("'save' has to be a string.") - return + def spaghetti_plot(self, logscale=True): + """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations. + + Parameters + ---------- + logscale : bool + Determines whether the scale of the y-axis is logarithmic or standard. + """ + if self.N != 1: + raise Exception("Correlator needs to be projected first.") + + mc_names = list(set([item for sublist in [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] + + for name in mc_names: + data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T + + fig = plt.figure() + ax = fig.add_subplot(111) + for dat in data: + ax.plot(x0_vals, dat, ls='-', marker='') + + if logscale is True: + ax.set_yscale('log') + + ax.set_xlabel(r'$x_0 / a$') + plt.title(name) + plt.draw() def dump(self, filename, datatype="json.gz", **kwargs): """Dumps the Corr into a file of chosen type diff --git a/tests/correlators_test.py b/tests/correlators_test.py index 9a2e7ea1..8ebca6aa 100644 --- a/tests/correlators_test.py +++ b/tests/correlators_test.py @@ -325,10 +325,20 @@ def test_corr_vector_operations(): assert np.all([o == 0 for o in ((my_corr * my_vec) / my_vec) - my_corr]) assert np.all([o == 0 for o in ((my_corr / my_vec) * my_vec) - my_corr]) -def _gen_corr(val): + +def test_spaghetti_plot(): + corr = _gen_corr(12, 50) + corr += pe.pseudo_Obs(0.0, 0.1, 'another_ensemble') + corr += pe.cov_Obs(0.0, 0.01 ** 2, 'covobs') + + corr.spaghetti_plot(True) + corr.spaghetti_plot(False) + + +def _gen_corr(val, samples=2000): corr_content = [] for t in range(16): - corr_content.append(pe.pseudo_Obs(val, 0.1, 't', 2000)) + corr_content.append(pe.pseudo_Obs(val, 0.1, 't', samples)) return pe.correlators.Corr(corr_content) From b72897a1bd789df541b0bcc4a73f357f1b19ba3d Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 8 Mar 2022 10:57:43 +0000 Subject: [PATCH 07/14] tests: random data removed from test_gamma_method_standard_data --- tests/obs_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/obs_test.py b/tests/obs_test.py index a2f40fdf..4eae6db4 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -142,7 +142,6 @@ def test_overloading_vectorization(): def test_gamma_method_standard_data(): for data in [np.tile([1, -1], 1000), - np.random.rand(100001), np.zeros(1195), np.sin(np.sqrt(2) * np.pi * np.arange(1812))]: test_obs = pe.Obs([data], ['t']) From e6813a6c8ee9d68c15d4b72a347d34f3c42aba0f Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Wed, 9 Mar 2022 12:24:52 +0000 Subject: [PATCH 08/14] feat: correlated_fit now throws an exception when the correlation matrix is ill conditioned with respect to the machine precision. Criterion for warning for ill-conditioned covariance matrix changed to cond > sqrt(eps) Test added. --- pyerrors/fits.py | 6 ++++-- tests/fits_test.py | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 6db0b1dc..17da5e71 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -464,8 +464,10 @@ def _standard_fit(x, y, func, silent=False, **kwargs): corr = covariance(y, correlation=True) covdiag = np.diag(1 / np.asarray(dy_f)) condn = np.linalg.cond(corr) - if condn > 1e8: - warnings.warn("Correlation matrix may be ill-conditioned, condition number: %1.2e" % (condn), RuntimeWarning) + 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 > 1 / np.sqrt(np.finfo(float).eps): + warnings.warn("Correlation matrix may be ill-conditioned, condition number: {%1.2e}" % (condn), RuntimeWarning) chol = np.linalg.cholesky(corr) chol_inv = np.linalg.inv(chol) chol_inv = np.dot(chol_inv, covdiag) diff --git a/tests/fits_test.py b/tests/fits_test.py index cc958dbc..b6236fb9 100644 --- a/tests/fits_test.py +++ b/tests/fits_test.py @@ -375,6 +375,12 @@ def test_fit_no_autograd(): pe.total_least_squares(oy, oy, func) +def test_singular_correlated_fit(): + obs1 = pe.pseudo_Obs(1.0, 0.1, 'test') + with pytest.raises(Exception): + pe.fits.fit_lin([0, 1], [obs1, obs1], correlated_fit=True) + + def test_ks_test(): def f(a, x): y = a[0] + a[1] * x From e21e2d1904097f8741d27212199dfe31b5a24eaa Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 11 Mar 2022 14:54:24 +0000 Subject: [PATCH 09/14] fix: formatting of qqplot fixed --- pyerrors/fits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 17da5e71..cfb10907 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -625,7 +625,7 @@ def qqplot(x, o_y, func, p): fit_stop = my_x[-1] samples = np.arange(fit_start, fit_stop, 0.01) plt.plot(samples, samples, 'k--', zorder=11, label='Standard normal distribution') - plt.plot(samples, probplot[1][0] * samples + probplot[1][1], zorder=10, label='Least squares fit, r=' + str(np.around(probplot[1][2], 3))) + plt.plot(samples, probplot[1][0] * samples + probplot[1][1], zorder=10, label='Least squares fit, r=' + str(np.around(probplot[1][2], 3)), marker='', ls='-') plt.xlabel('Theoretical quantiles') plt.ylabel('Ordered Values') From 2a02020a71a7cf578449f7e9e9f80b1a1f05b671 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Mon, 21 Mar 2022 16:45:37 +0000 Subject: [PATCH 10/14] fix: Increased the precision at which two observables are considered to be equal. --- pyerrors/obs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 2439bc03..0b46537c 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -443,7 +443,7 @@ class Obs: """ return self.is_zero() or np.abs(self.value) <= sigma * self._dvalue - def is_zero(self, rtol=1.e-5, atol=1.e-8): + def is_zero(self, rtol=1e-14, atol=1e-10): """Checks whether the observable is zero within a given tolerance. Parameters From da45a398b7934be1b21c466cde4f9eeb6eef64d8 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 22 Mar 2022 10:26:48 +0000 Subject: [PATCH 11/14] refactor: unnecessary parameter rtol removed from Obs.is_zero --- pyerrors/obs.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 0b46537c..bf8575bd 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -443,17 +443,15 @@ class Obs: """ return self.is_zero() or np.abs(self.value) <= sigma * self._dvalue - def is_zero(self, rtol=1e-14, atol=1e-10): + def is_zero(self, atol=1e-10): """Checks whether the observable is zero within a given tolerance. Parameters ---------- - rtol : float - Relative tolerance (for details see numpy documentation). atol : float Absolute tolerance (for details see numpy documentation). """ - return np.isclose(0.0, self.value, rtol, atol) and all(np.allclose(0.0, delta, rtol, atol) for delta in self.deltas.values()) and all(np.allclose(0.0, delta.errsq(), rtol, atol) for delta in self.covobs.values()) + return np.isclose(0.0, self.value, 1e-14, atol) and all(np.allclose(0.0, delta, 1e-14, atol) for delta in self.deltas.values()) and all(np.allclose(0.0, delta.errsq(), 1e-14, atol) for delta in self.covobs.values()) def plot_tauint(self, save=None): """Plot integrated autocorrelation time for each ensemble. From c9e1cd10af15ec6e79398ceaf90f05ad0c20aa3e Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 24 Mar 2022 11:44:36 +0000 Subject: [PATCH 12/14] feat: implemented unary positive operation for Obs and CObs classes, included tolerance for exception in Obs.plot_piechart --- pyerrors/obs.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index bf8575bd..6874bd12 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -582,7 +582,7 @@ class Obs: ensemble to the error and returns a dictionary containing the fractions.""" if not hasattr(self, 'e_dvalue'): raise Exception('Run the gamma method first.') - if self._dvalue == 0.0: + if np.isclose(0.0, self._dvalue, atol=1e-15): raise Exception('Error is 0.0') labels = self.e_names sizes = [self.e_dvalue[name] ** 2 for name in labels] / self._dvalue ** 2 @@ -729,6 +729,9 @@ class Obs: def __rsub__(self, y): return -1 * (self - y) + def __pos__(self): + return self + def __neg__(self): return -1 * self @@ -911,8 +914,11 @@ class CObs: def __abs__(self): return np.sqrt(self.real**2 + self.imag**2) - def __neg__(other): - return -1 * other + def __pos__(self): + return self + + def __neg__(self): + return -1 * self def __eq__(self, other): return self.real == other.real and self.imag == other.imag From cd2f361a3e81e42774c7a2ac97045af43d570476 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 24 Mar 2022 11:45:18 +0000 Subject: [PATCH 13/14] tests: tests for linalg and obs extended. --- tests/linalg_test.py | 10 ++++++++++ tests/obs_test.py | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/tests/linalg_test.py b/tests/linalg_test.py index 61c71514..09db0ac5 100644 --- a/tests/linalg_test.py +++ b/tests/linalg_test.py @@ -314,6 +314,9 @@ def test_matrix_functions(): # Check determinant assert pe.linalg.det(np.diag(np.diag(matrix))) == np.prod(np.diag(matrix)) + with pytest.raises(Exception): + pe.linalg.det(5) + pe.linalg.pinv(matrix[:,:3]) @@ -347,3 +350,10 @@ def test_complex_matrix_operations(): diff = ta * tb - 1 for (i, j), entry in np.ndenumerate(diff): assert entry.is_zero() + + +def test_complex_matrix_real_entries(): + my_mat = get_complex_matrix(4) + 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) diff --git a/tests/obs_test.py b/tests/obs_test.py index 4eae6db4..c8aef487 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -51,6 +51,12 @@ def test_Obs_exceptions(): my_obs.gamma_method() my_obs.details() + obs = pe.Obs([np.random.normal(1.0, 0.1, 100)], ['t']) + one = obs / obs + one.gamma_method() + with pytest.raises(Exception): + one.plot_piechart() + def test_dump(): value = np.random.normal(5, 10) dvalue = np.abs(np.random.normal(0, 1)) @@ -86,6 +92,8 @@ def test_comparison(): assert test_obs2 != value2 assert test_obs1 != test_obs2 assert test_obs2 != test_obs1 + assert +test_obs1 == test_obs1 + assert -test_obs1 == 0 - test_obs1 def test_function_overloading(): @@ -367,6 +375,8 @@ def test_cobs(): obs2 = pe.pseudo_Obs(-0.2, 0.03, 't') my_cobs = pe.CObs(obs1, obs2) + assert +my_cobs == my_cobs + assert -my_cobs == 0 - my_cobs my_cobs == my_cobs str(my_cobs) repr(my_cobs) @@ -758,3 +768,15 @@ def test_merge_idx(): assert(new_idx[-1] > new_idx[0]) for i in range(1, len(new_idx)): assert(new_idx[i - 1] < new_idx[i]) + +def test_cobs_array(): + cobs = pe.Obs([np.random.normal(1.0, 0.1, 100)], ['t']) * (1 + 2j) + np.identity(4) + cobs + cobs + np.identity(4) + np.identity(4) - cobs + cobs - np.identity(4) + np.identity(4) * cobs + cobs * np.identity(4) + np.identity(4) / cobs + cobs / np.ones((4, 4)) + From f907c62b4136f16f67dc30ba5424b5d544d545e1 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Thu, 31 Mar 2022 11:25:37 +0100 Subject: [PATCH 14/14] Version number bumped to 2.0.0, CHANGELOG updated. --- CHANGELOG.md | 37 +++++++++++++++++++------------------ pyerrors/version.py | 2 +- setup.py | 2 +- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d78cc00..0ac0ee11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,53 +2,54 @@ All notable changes to this project will be documented in this file. -## [2.0.0] - 2022-??-?? +## [2.0.0] - 2022-03-31 ### Added - The possibility to work with Monte Carlo histories which are evenly or unevenly spaced was added. - `cov_Obs` added as a possibility to propagate the error of non Monte Carlo data together with Monte Carlo data. - `CObs` class added which can handle complex valued Markov chain Monte Carlo data and the corresponding error propagation. - Matrix to matrix operations like the matrix inverse now also work for complex matrices and matrices containing entries that are not `Obs` but `float` or `int`. -- Support for a new `json.gz` file format was added +- Support for a new `json.gz` file format was added. - The Corr class now has additional methods like `reverse`, `T_symmetry`, `correlate` and `reweight`. -- `Corr.m_eff` can now cope with periodic and anti-periodic correlation functions -- Forward, backward and improved variants of the first and second derivative were added to the `Corr` class -- The `linalg` module now has explicit functions `inv` and `cholesky`. +- `Corr.m_eff` can now cope with periodic and anti-periodic correlation functions. +- Forward, backward and improved variants of the first and second derivative were added to the `Corr` class. +- `GEVP` functionality of the `Corr` class was reworked and improved. +- The `linalg` module now has explicit functions `inv`, `cholesky` and `det`. - `Obs` objects now have methods `is_zero` and `is_zero_within_error` as well as overloaded comparison operations. -- Functions to convert Obs data to or from jackknife was added. -- Alternative matrix multiplication routine `jack_matmul` was added to `linalg` module which makes use of the jackknife approximation and is much faster for large matrices. +- Functions to convert `Obs` data to or from jackknife was added. +- Alternative matrix multiplication routines `einsum` and `jack_matmul` were added to `linalg` module which make use of the jackknife approximation and are much faster for large matrices. - Additional input routines for npr data added to `input.hadrons`. - The `sfcf` and `openQCD` input modules can now handle all recent file type versions. -- `extract_t0` can now visualize the extraction on the fly +- `extract_t0` can now visualize the extraction on the fly. - Module added which provides the Dirac gamma matrices in the Grid convention. - Version number added. ### Changed - The internal bookkeeping system for ensembles/replica was changed. The separator for replica is now `|`. - The fit functions were renamed to `least_squares` and `total_least_squares`. -- The output of the fit functions is now a dedicated results class which keeps track of all relevant information +- The output of the fit functions is now a dedicated results class which keeps track of all relevant information. - The fit functions can now deal with provided covariance matrices. -- `covariance` can now operate on a list or array of `Obs` and returns a matrix +- `covariance` can now operate on a list or array of `Obs` and returns a matrix. The covariance estimate by pyerrors is now always positive semi-definite (within machine precision. Various warnings and exceptions were added for cases in which estimated covariances are close to singular. - The convention for the fit range in the Corr class has been changed. -- Various method of the `Corr` class were renamed +- Various method of the `Corr` class were renamed. - `Obs.print` was renamed to `Obs.details` and the output was improved. - The default value for `Corr.prange` is now `None`. - The `input` module was restructured to contain one submodule per data source. - Performance of Obs.__init__ improved. ### Removed -- The function `plot_corrs` was deprecated as all its functionality is now contained within `Corr.show` -- `fits.covariance_matrix` was removed as it is now redundant with the functionality of `covariance` -- The kwarg `bias_correction` in `derived_observable` was removed -- Obs no longer have an attribute `e_Q` -- Removed `fits.fit_exp` -- Removed jackknife module +- The function `plot_corrs` was deprecated as all its functionality is now contained within `Corr.show`. +- `fits.covariance_matrix` was removed as it is now redundant with the functionality of `covariance`. +- The kwarg `bias_correction` in `derived_observable` was removed. +- Obs no longer have an attribute `e_Q`. +- Removed `fits.fit_exp`. +- Removed jackknife module. ## [1.1.0] - 2021-10-11 ### Added - `Corr` class added - `roots` module added which can find the roots of a function that depends on Monte Carlo data via pyerrors `Obs` - `input/hadrons` module added which can read hdf5 files written by [Hadrons](https://github.com/aportelli/Hadrons) -- `read_rwms` can now read reweighting factors in the format used by openQCD-2.0 +- `read_rwms` can now read reweighting factors in the format used by openQCD-2.0. ## [1.0.1] - 2020-11-03 ### Fixed diff --git a/pyerrors/version.py b/pyerrors/version.py index 69bd966d..8c0d5d5b 100644 --- a/pyerrors/version.py +++ b/pyerrors/version.py @@ -1 +1 @@ -__version__ = "2.0.0-rc.3+dev" +__version__ = "2.0.0" diff --git a/setup.py b/setup.py index e7a997db..5a971d92 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages setup(name='pyerrors', - version='2.0.0-rc.3+dev', + version='2.0.0', description='Error analysis for lattice QCD', author='Fabian Joswig', author_email='fabian.joswig@ed.ac.uk',