diff --git a/docs/pyerrors/correlators.html b/docs/pyerrors/correlators.html index b1951891..afc027f7 100644 --- a/docs/pyerrors/correlators.html +++ b/docs/pyerrors/correlators.html @@ -239,1298 +239,1300 @@ 23 24 """ 25 - 26 def __init__(self, data_input, padding=[0, 0], prange=None): - 27 """ Initialize a Corr object. - 28 - 29 Parameters - 30 ---------- - 31 data_input : list or array - 32 list of Obs or list of arrays of Obs or array of Corrs - 33 padding : list, optional - 34 List with two entries where the first labels the padding - 35 at the front of the correlator and the second the padding - 36 at the back. - 37 prange : list, optional - 38 List containing the first and last timeslice of the plateau - 39 region indentified for this correlator. - 40 """ - 41 - 42 if isinstance(data_input, np.ndarray): + 26 __slots__ = ["content", "N", "T", "tag", "prange"] + 27 + 28 def __init__(self, data_input, padding=[0, 0], prange=None): + 29 """ Initialize a Corr object. + 30 + 31 Parameters + 32 ---------- + 33 data_input : list or array + 34 list of Obs or list of arrays of Obs or array of Corrs + 35 padding : list, optional + 36 List with two entries where the first labels the padding + 37 at the front of the correlator and the second the padding + 38 at the back. + 39 prange : list, optional + 40 List containing the first and last timeslice of the plateau + 41 region indentified for this correlator. + 42 """ 43 - 44 # This only works, if the array fulfills the conditions below - 45 if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]: - 46 raise Exception("Incompatible array shape") - 47 if not all([isinstance(item, Corr) for item in data_input.flatten()]): - 48 raise Exception("If the input is an array, its elements must be of type pe.Corr") - 49 if not all([item.N == 1 for item in data_input.flatten()]): - 50 raise Exception("Can only construct matrix correlator from single valued correlators") - 51 if not len(set([item.T for item in data_input.flatten()])) == 1: - 52 raise Exception("All input Correlators must be defined over the same timeslices.") - 53 - 54 T = data_input[0, 0].T - 55 N = data_input.shape[0] - 56 input_as_list = [] - 57 for t in range(T): - 58 if any([(item.content[t] is None) for item in data_input.flatten()]): - 59 if not all([(item.content[t] is None) for item in data_input.flatten()]): - 60 warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning) - 61 input_as_list.append(None) - 62 else: - 63 array_at_timeslace = np.empty([N, N], dtype="object") - 64 for i in range(N): - 65 for j in range(N): - 66 array_at_timeslace[i, j] = data_input[i, j][t] - 67 input_as_list.append(array_at_timeslace) - 68 data_input = input_as_list - 69 - 70 if isinstance(data_input, list): + 44 if isinstance(data_input, np.ndarray): + 45 + 46 # This only works, if the array fulfills the conditions below + 47 if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]: + 48 raise Exception("Incompatible array shape") + 49 if not all([isinstance(item, Corr) for item in data_input.flatten()]): + 50 raise Exception("If the input is an array, its elements must be of type pe.Corr") + 51 if not all([item.N == 1 for item in data_input.flatten()]): + 52 raise Exception("Can only construct matrix correlator from single valued correlators") + 53 if not len(set([item.T for item in data_input.flatten()])) == 1: + 54 raise Exception("All input Correlators must be defined over the same timeslices.") + 55 + 56 T = data_input[0, 0].T + 57 N = data_input.shape[0] + 58 input_as_list = [] + 59 for t in range(T): + 60 if any([(item.content[t] is None) for item in data_input.flatten()]): + 61 if not all([(item.content[t] is None) for item in data_input.flatten()]): + 62 warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning) + 63 input_as_list.append(None) + 64 else: + 65 array_at_timeslace = np.empty([N, N], dtype="object") + 66 for i in range(N): + 67 for j in range(N): + 68 array_at_timeslace[i, j] = data_input[i, j][t] + 69 input_as_list.append(array_at_timeslace) + 70 data_input = input_as_list 71 - 72 if all([isinstance(item, (Obs, CObs)) or item is None for item in data_input]): - 73 _assert_equal_properties([o for o in data_input if o is not None]) - 74 self.content = [np.asarray([item]) if item is not None else None for item in data_input] - 75 self.N = 1 - 76 - 77 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]): - 78 self.content = data_input - 79 noNull = [a for a in self.content if not (a is None)] # To check if the matrices are correct for all undefined elements - 80 self.N = noNull[0].shape[0] - 81 if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]: - 82 raise Exception("Smearing matrices are not NxN") - 83 if (not all([item.shape == noNull[0].shape for item in noNull])): - 84 raise Exception("Items in data_input are not of identical shape." + str(noNull)) - 85 else: - 86 raise Exception("data_input contains item of wrong type") - 87 else: - 88 raise Exception("Data input was not given as list or correct array") - 89 - 90 self.tag = None + 72 if isinstance(data_input, list): + 73 + 74 if all([isinstance(item, (Obs, CObs)) or item is None for item in data_input]): + 75 _assert_equal_properties([o for o in data_input if o is not None]) + 76 self.content = [np.asarray([item]) if item is not None else None for item in data_input] + 77 self.N = 1 + 78 + 79 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]): + 80 self.content = data_input + 81 noNull = [a for a in self.content if not (a is None)] # To check if the matrices are correct for all undefined elements + 82 self.N = noNull[0].shape[0] + 83 if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]: + 84 raise Exception("Smearing matrices are not NxN") + 85 if (not all([item.shape == noNull[0].shape for item in noNull])): + 86 raise Exception("Items in data_input are not of identical shape." + str(noNull)) + 87 else: + 88 raise Exception("data_input contains item of wrong type") + 89 else: + 90 raise Exception("Data input was not given as list or correct array") 91 - 92 # An undefined timeslice is represented by the None object - 93 self.content = [None] * padding[0] + self.content + [None] * padding[1] - 94 self.T = len(self.content) - 95 self.prange = prange - 96 - 97 def __getitem__(self, idx): - 98 """Return the content of timeslice idx""" - 99 if self.content[idx] is None: - 100 return None - 101 elif len(self.content[idx]) == 1: - 102 return self.content[idx][0] - 103 else: - 104 return self.content[idx] - 105 - 106 @property - 107 def reweighted(self): - 108 bool_array = np.array([list(map(lambda x: x.reweighted, o)) for o in [x for x in self.content if x is not None]]) - 109 if np.all(bool_array == 1): - 110 return True - 111 elif np.all(bool_array == 0): - 112 return False - 113 else: - 114 raise Exception("Reweighting status of correlator corrupted.") - 115 - 116 def gamma_method(self, **kwargs): - 117 """Apply the gamma method to the content of the Corr.""" - 118 for item in self.content: - 119 if not (item is None): - 120 if self.N == 1: - 121 item[0].gamma_method(**kwargs) - 122 else: - 123 for i in range(self.N): - 124 for j in range(self.N): - 125 item[i, j].gamma_method(**kwargs) - 126 - 127 gm = gamma_method + 92 self.tag = None + 93 + 94 # An undefined timeslice is represented by the None object + 95 self.content = [None] * padding[0] + self.content + [None] * padding[1] + 96 self.T = len(self.content) + 97 self.prange = prange + 98 + 99 def __getitem__(self, idx): + 100 """Return the content of timeslice idx""" + 101 if self.content[idx] is None: + 102 return None + 103 elif len(self.content[idx]) == 1: + 104 return self.content[idx][0] + 105 else: + 106 return self.content[idx] + 107 + 108 @property + 109 def reweighted(self): + 110 bool_array = np.array([list(map(lambda x: x.reweighted, o)) for o in [x for x in self.content if x is not None]]) + 111 if np.all(bool_array == 1): + 112 return True + 113 elif np.all(bool_array == 0): + 114 return False + 115 else: + 116 raise Exception("Reweighting status of correlator corrupted.") + 117 + 118 def gamma_method(self, **kwargs): + 119 """Apply the gamma method to the content of the Corr.""" + 120 for item in self.content: + 121 if not (item is None): + 122 if self.N == 1: + 123 item[0].gamma_method(**kwargs) + 124 else: + 125 for i in range(self.N): + 126 for j in range(self.N): + 127 item[i, j].gamma_method(**kwargs) 128 - 129 def projected(self, vector_l=None, vector_r=None, normalize=False): - 130 """We need to project the Correlator with a Vector to get a single value at each timeslice. - 131 - 132 The method can use one or two vectors. - 133 If two are specified it returns v1@G@v2 (the order might be very important.) - 134 By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to - 135 """ - 136 if self.N == 1: - 137 raise Exception("Trying to project a Corr, that already has N=1.") - 138 - 139 if vector_l is None: - 140 vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.]) - 141 elif (vector_r is None): - 142 vector_r = vector_l - 143 if isinstance(vector_l, list) and not isinstance(vector_r, list): - 144 if len(vector_l) != self.T: - 145 raise Exception("Length of vector list must be equal to T") - 146 vector_r = [vector_r] * self.T - 147 if isinstance(vector_r, list) and not isinstance(vector_l, list): - 148 if len(vector_r) != self.T: - 149 raise Exception("Length of vector list must be equal to T") - 150 vector_l = [vector_l] * self.T - 151 - 152 if not isinstance(vector_l, list): - 153 if not vector_l.shape == vector_r.shape == (self.N,): - 154 raise Exception("Vectors are of wrong shape!") - 155 if normalize: - 156 vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r) - 157 newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content] - 158 - 159 else: - 160 # There are no checks here yet. There are so many possible scenarios, where this can go wrong. - 161 if normalize: - 162 for t in range(self.T): - 163 vector_l[t], vector_r[t] = vector_l[t] / np.sqrt((vector_l[t] @ vector_l[t])), vector_r[t] / np.sqrt(vector_r[t] @ vector_r[t]) - 164 - 165 newcontent = [None if (_check_for_none(self, self.content[t]) or vector_l[t] is None or vector_r[t] is None) else np.asarray([vector_l[t].T @ self.content[t] @ vector_r[t]]) for t in range(self.T)] - 166 return Corr(newcontent) - 167 - 168 def item(self, i, j): - 169 """Picks the element [i,j] from every matrix and returns a correlator containing one Obs per timeslice. - 170 - 171 Parameters - 172 ---------- - 173 i : int - 174 First index to be picked. - 175 j : int - 176 Second index to be picked. - 177 """ - 178 if self.N == 1: - 179 raise Exception("Trying to pick item from projected Corr") - 180 newcontent = [None if (item is None) else item[i, j] for item in self.content] - 181 return Corr(newcontent) - 182 - 183 def plottable(self): - 184 """Outputs the correlator in a plotable format. - 185 - 186 Outputs three lists containing the timeslice index, the value on each - 187 timeslice and the error on each timeslice. - 188 """ - 189 if self.N != 1: - 190 raise Exception("Can only make Corr[N=1] plottable") - 191 x_list = [x for x in range(self.T) if not self.content[x] is None] - 192 y_list = [y[0].value for y in self.content if y is not None] - 193 y_err_list = [y[0].dvalue for y in self.content if y is not None] - 194 - 195 return x_list, y_list, y_err_list + 129 gm = gamma_method + 130 + 131 def projected(self, vector_l=None, vector_r=None, normalize=False): + 132 """We need to project the Correlator with a Vector to get a single value at each timeslice. + 133 + 134 The method can use one or two vectors. + 135 If two are specified it returns v1@G@v2 (the order might be very important.) + 136 By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to + 137 """ + 138 if self.N == 1: + 139 raise Exception("Trying to project a Corr, that already has N=1.") + 140 + 141 if vector_l is None: + 142 vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.]) + 143 elif (vector_r is None): + 144 vector_r = vector_l + 145 if isinstance(vector_l, list) and not isinstance(vector_r, list): + 146 if len(vector_l) != self.T: + 147 raise Exception("Length of vector list must be equal to T") + 148 vector_r = [vector_r] * self.T + 149 if isinstance(vector_r, list) and not isinstance(vector_l, list): + 150 if len(vector_r) != self.T: + 151 raise Exception("Length of vector list must be equal to T") + 152 vector_l = [vector_l] * self.T + 153 + 154 if not isinstance(vector_l, list): + 155 if not vector_l.shape == vector_r.shape == (self.N,): + 156 raise Exception("Vectors are of wrong shape!") + 157 if normalize: + 158 vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r) + 159 newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content] + 160 + 161 else: + 162 # There are no checks here yet. There are so many possible scenarios, where this can go wrong. + 163 if normalize: + 164 for t in range(self.T): + 165 vector_l[t], vector_r[t] = vector_l[t] / np.sqrt((vector_l[t] @ vector_l[t])), vector_r[t] / np.sqrt(vector_r[t] @ vector_r[t]) + 166 + 167 newcontent = [None if (_check_for_none(self, self.content[t]) or vector_l[t] is None or vector_r[t] is None) else np.asarray([vector_l[t].T @ self.content[t] @ vector_r[t]]) for t in range(self.T)] + 168 return Corr(newcontent) + 169 + 170 def item(self, i, j): + 171 """Picks the element [i,j] from every matrix and returns a correlator containing one Obs per timeslice. + 172 + 173 Parameters + 174 ---------- + 175 i : int + 176 First index to be picked. + 177 j : int + 178 Second index to be picked. + 179 """ + 180 if self.N == 1: + 181 raise Exception("Trying to pick item from projected Corr") + 182 newcontent = [None if (item is None) else item[i, j] for item in self.content] + 183 return Corr(newcontent) + 184 + 185 def plottable(self): + 186 """Outputs the correlator in a plotable format. + 187 + 188 Outputs three lists containing the timeslice index, the value on each + 189 timeslice and the error on each timeslice. + 190 """ + 191 if self.N != 1: + 192 raise Exception("Can only make Corr[N=1] plottable") + 193 x_list = [x for x in range(self.T) if not self.content[x] is None] + 194 y_list = [y[0].value for y in self.content if y is not None] + 195 y_err_list = [y[0].dvalue for y in self.content if y is not None] 196 - 197 def symmetric(self): - 198 """ Symmetrize the correlator around x0=0.""" - 199 if self.N != 1: - 200 raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.') - 201 if self.T % 2 != 0: - 202 raise Exception("Can not symmetrize odd T") - 203 - 204 if self.content[0] is not None: - 205 if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0: - 206 warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning) - 207 - 208 newcontent = [self.content[0]] - 209 for t in range(1, self.T): - 210 if (self.content[t] is None) or (self.content[self.T - t] is None): - 211 newcontent.append(None) - 212 else: - 213 newcontent.append(0.5 * (self.content[t] + self.content[self.T - t])) - 214 if (all([x is None for x in newcontent])): - 215 raise Exception("Corr could not be symmetrized: No redundant values") - 216 return Corr(newcontent, prange=self.prange) - 217 - 218 def anti_symmetric(self): - 219 """Anti-symmetrize the correlator around x0=0.""" - 220 if self.N != 1: - 221 raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.') - 222 if self.T % 2 != 0: - 223 raise Exception("Can not symmetrize odd T") - 224 - 225 test = 1 * self - 226 test.gamma_method() - 227 if not all([o.is_zero_within_error(3) for o in test.content[0]]): - 228 warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning) - 229 - 230 newcontent = [self.content[0]] - 231 for t in range(1, self.T): - 232 if (self.content[t] is None) or (self.content[self.T - t] is None): - 233 newcontent.append(None) - 234 else: - 235 newcontent.append(0.5 * (self.content[t] - self.content[self.T - t])) - 236 if (all([x is None for x in newcontent])): - 237 raise Exception("Corr could not be symmetrized: No redundant values") - 238 return Corr(newcontent, prange=self.prange) - 239 - 240 def is_matrix_symmetric(self): - 241 """Checks whether a correlator matrices is symmetric on every timeslice.""" - 242 if self.N == 1: - 243 raise Exception("Only works for correlator matrices.") - 244 for t in range(self.T): - 245 if self[t] is None: - 246 continue - 247 for i in range(self.N): - 248 for j in range(i + 1, self.N): - 249 if self[t][i, j] is self[t][j, i]: - 250 continue - 251 if hash(self[t][i, j]) != hash(self[t][j, i]): - 252 return False - 253 return True - 254 - 255 def matrix_symmetric(self): - 256 """Symmetrizes the correlator matrices on every timeslice.""" - 257 if self.N == 1: - 258 raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.") - 259 if self.is_matrix_symmetric(): - 260 return 1.0 * self - 261 else: - 262 transposed = [None if _check_for_none(self, G) else G.T for G in self.content] - 263 return 0.5 * (Corr(transposed) + self) - 264 - 265 def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs): - 266 r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors. - 267 - 268 The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the - 269 largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing - 270 ```python - 271 C.GEVP(t0=2)[0] # Ground state vector(s) - 272 C.GEVP(t0=2)[:3] # Vectors for the lowest three states - 273 ``` - 274 - 275 Parameters - 276 ---------- - 277 t0 : int - 278 The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$ - 279 ts : int - 280 fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None. - 281 If sort="Eigenvector" it gives a reference point for the sorting method. - 282 sort : string - 283 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. - 284 - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice. - 285 - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state. - 286 The reference state is identified by its eigenvalue at $t=t_s$. - 287 - 288 Other Parameters - 289 ---------------- - 290 state : int - 291 Returns only the vector(s) for a specified state. The lowest state is zero. - 292 ''' - 293 - 294 if self.N == 1: - 295 raise Exception("GEVP methods only works on correlator matrices and not single correlators.") - 296 if ts is not None: - 297 if (ts <= t0): - 298 raise Exception("ts has to be larger than t0.") - 299 - 300 if "sorted_list" in kwargs: - 301 warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning) - 302 sort = kwargs.get("sorted_list") - 303 - 304 if self.is_matrix_symmetric(): - 305 symmetric_corr = self - 306 else: - 307 symmetric_corr = self.matrix_symmetric() - 308 - 309 G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0]) - 310 np.linalg.cholesky(G0) # Check if matrix G0 is positive-semidefinite. - 311 - 312 if sort is None: - 313 if (ts is None): - 314 raise Exception("ts is required if sort=None.") - 315 if (self.content[t0] is None) or (self.content[ts] is None): - 316 raise Exception("Corr not defined at t0/ts.") - 317 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts]) - 318 reordered_vecs = _GEVP_solver(Gt, G0) - 319 - 320 elif sort in ["Eigenvalue", "Eigenvector"]: - 321 if sort == "Eigenvalue" and ts is not None: - 322 warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning) - 323 all_vecs = [None] * (t0 + 1) - 324 for t in range(t0 + 1, self.T): - 325 try: - 326 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t]) - 327 all_vecs.append(_GEVP_solver(Gt, G0)) - 328 except Exception: - 329 all_vecs.append(None) - 330 if sort == "Eigenvector": - 331 if ts is None: - 332 raise Exception("ts is required for the Eigenvector sorting method.") - 333 all_vecs = _sort_vectors(all_vecs, ts) - 334 - 335 reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)] - 336 else: - 337 raise Exception("Unkown value for 'sort'.") - 338 - 339 if "state" in kwargs: - 340 return reordered_vecs[kwargs.get("state")] - 341 else: - 342 return reordered_vecs - 343 - 344 def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"): - 345 """Determines the eigenvalue of the GEVP by solving and projecting the correlator - 346 - 347 Parameters - 348 ---------- - 349 state : int - 350 The state one is interested in ordered by energy. The lowest state is zero. - 351 - 352 All other parameters are identical to the ones of Corr.GEVP. - 353 """ - 354 vec = self.GEVP(t0, ts=ts, sort=sort)[state] - 355 return self.projected(vec) - 356 - 357 def Hankel(self, N, periodic=False): - 358 """Constructs an NxN Hankel matrix - 359 - 360 C(t) c(t+1) ... c(t+n-1) - 361 C(t+1) c(t+2) ... c(t+n) - 362 ................. - 363 C(t+(n-1)) c(t+n) ... c(t+2(n-1)) - 364 - 365 Parameters - 366 ---------- - 367 N : int - 368 Dimension of the Hankel matrix - 369 periodic : bool, optional - 370 determines whether the matrix is extended periodically - 371 """ - 372 - 373 if self.N != 1: - 374 raise Exception("Multi-operator Prony not implemented!") - 375 - 376 array = np.empty([N, N], dtype="object") - 377 new_content = [] - 378 for t in range(self.T): - 379 new_content.append(array.copy()) - 380 - 381 def wrap(i): - 382 while i >= self.T: - 383 i -= self.T - 384 return i - 385 - 386 for t in range(self.T): - 387 for i in range(N): - 388 for j in range(N): - 389 if periodic: - 390 new_content[t][i, j] = self.content[wrap(t + i + j)][0] - 391 elif (t + i + j) >= self.T: - 392 new_content[t] = None - 393 else: - 394 new_content[t][i, j] = self.content[t + i + j][0] - 395 - 396 return Corr(new_content) + 197 return x_list, y_list, y_err_list + 198 + 199 def symmetric(self): + 200 """ Symmetrize the correlator around x0=0.""" + 201 if self.N != 1: + 202 raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.') + 203 if self.T % 2 != 0: + 204 raise Exception("Can not symmetrize odd T") + 205 + 206 if self.content[0] is not None: + 207 if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0: + 208 warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning) + 209 + 210 newcontent = [self.content[0]] + 211 for t in range(1, self.T): + 212 if (self.content[t] is None) or (self.content[self.T - t] is None): + 213 newcontent.append(None) + 214 else: + 215 newcontent.append(0.5 * (self.content[t] + self.content[self.T - t])) + 216 if (all([x is None for x in newcontent])): + 217 raise Exception("Corr could not be symmetrized: No redundant values") + 218 return Corr(newcontent, prange=self.prange) + 219 + 220 def anti_symmetric(self): + 221 """Anti-symmetrize the correlator around x0=0.""" + 222 if self.N != 1: + 223 raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.') + 224 if self.T % 2 != 0: + 225 raise Exception("Can not symmetrize odd T") + 226 + 227 test = 1 * self + 228 test.gamma_method() + 229 if not all([o.is_zero_within_error(3) for o in test.content[0]]): + 230 warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning) + 231 + 232 newcontent = [self.content[0]] + 233 for t in range(1, self.T): + 234 if (self.content[t] is None) or (self.content[self.T - t] is None): + 235 newcontent.append(None) + 236 else: + 237 newcontent.append(0.5 * (self.content[t] - self.content[self.T - t])) + 238 if (all([x is None for x in newcontent])): + 239 raise Exception("Corr could not be symmetrized: No redundant values") + 240 return Corr(newcontent, prange=self.prange) + 241 + 242 def is_matrix_symmetric(self): + 243 """Checks whether a correlator matrices is symmetric on every timeslice.""" + 244 if self.N == 1: + 245 raise Exception("Only works for correlator matrices.") + 246 for t in range(self.T): + 247 if self[t] is None: + 248 continue + 249 for i in range(self.N): + 250 for j in range(i + 1, self.N): + 251 if self[t][i, j] is self[t][j, i]: + 252 continue + 253 if hash(self[t][i, j]) != hash(self[t][j, i]): + 254 return False + 255 return True + 256 + 257 def matrix_symmetric(self): + 258 """Symmetrizes the correlator matrices on every timeslice.""" + 259 if self.N == 1: + 260 raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.") + 261 if self.is_matrix_symmetric(): + 262 return 1.0 * self + 263 else: + 264 transposed = [None if _check_for_none(self, G) else G.T for G in self.content] + 265 return 0.5 * (Corr(transposed) + self) + 266 + 267 def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs): + 268 r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors. + 269 + 270 The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the + 271 largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing + 272 ```python + 273 C.GEVP(t0=2)[0] # Ground state vector(s) + 274 C.GEVP(t0=2)[:3] # Vectors for the lowest three states + 275 ``` + 276 + 277 Parameters + 278 ---------- + 279 t0 : int + 280 The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$ + 281 ts : int + 282 fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None. + 283 If sort="Eigenvector" it gives a reference point for the sorting method. + 284 sort : string + 285 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. + 286 - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice. + 287 - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state. + 288 The reference state is identified by its eigenvalue at $t=t_s$. + 289 + 290 Other Parameters + 291 ---------------- + 292 state : int + 293 Returns only the vector(s) for a specified state. The lowest state is zero. + 294 ''' + 295 + 296 if self.N == 1: + 297 raise Exception("GEVP methods only works on correlator matrices and not single correlators.") + 298 if ts is not None: + 299 if (ts <= t0): + 300 raise Exception("ts has to be larger than t0.") + 301 + 302 if "sorted_list" in kwargs: + 303 warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning) + 304 sort = kwargs.get("sorted_list") + 305 + 306 if self.is_matrix_symmetric(): + 307 symmetric_corr = self + 308 else: + 309 symmetric_corr = self.matrix_symmetric() + 310 + 311 G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0]) + 312 np.linalg.cholesky(G0) # Check if matrix G0 is positive-semidefinite. + 313 + 314 if sort is None: + 315 if (ts is None): + 316 raise Exception("ts is required if sort=None.") + 317 if (self.content[t0] is None) or (self.content[ts] is None): + 318 raise Exception("Corr not defined at t0/ts.") + 319 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts]) + 320 reordered_vecs = _GEVP_solver(Gt, G0) + 321 + 322 elif sort in ["Eigenvalue", "Eigenvector"]: + 323 if sort == "Eigenvalue" and ts is not None: + 324 warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning) + 325 all_vecs = [None] * (t0 + 1) + 326 for t in range(t0 + 1, self.T): + 327 try: + 328 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t]) + 329 all_vecs.append(_GEVP_solver(Gt, G0)) + 330 except Exception: + 331 all_vecs.append(None) + 332 if sort == "Eigenvector": + 333 if ts is None: + 334 raise Exception("ts is required for the Eigenvector sorting method.") + 335 all_vecs = _sort_vectors(all_vecs, ts) + 336 + 337 reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)] + 338 else: + 339 raise Exception("Unkown value for 'sort'.") + 340 + 341 if "state" in kwargs: + 342 return reordered_vecs[kwargs.get("state")] + 343 else: + 344 return reordered_vecs + 345 + 346 def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"): + 347 """Determines the eigenvalue of the GEVP by solving and projecting the correlator + 348 + 349 Parameters + 350 ---------- + 351 state : int + 352 The state one is interested in ordered by energy. The lowest state is zero. + 353 + 354 All other parameters are identical to the ones of Corr.GEVP. + 355 """ + 356 vec = self.GEVP(t0, ts=ts, sort=sort)[state] + 357 return self.projected(vec) + 358 + 359 def Hankel(self, N, periodic=False): + 360 """Constructs an NxN Hankel matrix + 361 + 362 C(t) c(t+1) ... c(t+n-1) + 363 C(t+1) c(t+2) ... c(t+n) + 364 ................. + 365 C(t+(n-1)) c(t+n) ... c(t+2(n-1)) + 366 + 367 Parameters + 368 ---------- + 369 N : int + 370 Dimension of the Hankel matrix + 371 periodic : bool, optional + 372 determines whether the matrix is extended periodically + 373 """ + 374 + 375 if self.N != 1: + 376 raise Exception("Multi-operator Prony not implemented!") + 377 + 378 array = np.empty([N, N], dtype="object") + 379 new_content = [] + 380 for t in range(self.T): + 381 new_content.append(array.copy()) + 382 + 383 def wrap(i): + 384 while i >= self.T: + 385 i -= self.T + 386 return i + 387 + 388 for t in range(self.T): + 389 for i in range(N): + 390 for j in range(N): + 391 if periodic: + 392 new_content[t][i, j] = self.content[wrap(t + i + j)][0] + 393 elif (t + i + j) >= self.T: + 394 new_content[t] = None + 395 else: + 396 new_content[t][i, j] = self.content[t + i + j][0] 397 - 398 def roll(self, dt): - 399 """Periodically shift the correlator by dt timeslices - 400 - 401 Parameters - 402 ---------- - 403 dt : int - 404 number of timeslices - 405 """ - 406 return Corr(list(np.roll(np.array(self.content, dtype=object), dt))) - 407 - 408 def reverse(self): - 409 """Reverse the time ordering of the Corr""" - 410 return Corr(self.content[:: -1]) - 411 - 412 def thin(self, spacing=2, offset=0): - 413 """Thin out a correlator to suppress correlations - 414 - 415 Parameters - 416 ---------- - 417 spacing : int - 418 Keep only every 'spacing'th entry of the correlator - 419 offset : int - 420 Offset the equal spacing - 421 """ - 422 new_content = [] - 423 for t in range(self.T): - 424 if (offset + t) % spacing != 0: - 425 new_content.append(None) - 426 else: - 427 new_content.append(self.content[t]) - 428 return Corr(new_content) - 429 - 430 def correlate(self, partner): - 431 """Correlate the correlator with another correlator or Obs - 432 - 433 Parameters - 434 ---------- - 435 partner : Obs or Corr - 436 partner to correlate the correlator with. - 437 Can either be an Obs which is correlated with all entries of the - 438 correlator or a Corr of same length. - 439 """ - 440 if self.N != 1: - 441 raise Exception("Only one-dimensional correlators can be safely correlated.") - 442 new_content = [] - 443 for x0, t_slice in enumerate(self.content): - 444 if _check_for_none(self, t_slice): - 445 new_content.append(None) - 446 else: - 447 if isinstance(partner, Corr): - 448 if _check_for_none(partner, partner.content[x0]): - 449 new_content.append(None) - 450 else: - 451 new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice])) - 452 elif isinstance(partner, Obs): # Should this include CObs? - 453 new_content.append(np.array([correlate(o, partner) for o in t_slice])) - 454 else: - 455 raise Exception("Can only correlate with an Obs or a Corr.") - 456 - 457 return Corr(new_content) + 398 return Corr(new_content) + 399 + 400 def roll(self, dt): + 401 """Periodically shift the correlator by dt timeslices + 402 + 403 Parameters + 404 ---------- + 405 dt : int + 406 number of timeslices + 407 """ + 408 return Corr(list(np.roll(np.array(self.content, dtype=object), dt))) + 409 + 410 def reverse(self): + 411 """Reverse the time ordering of the Corr""" + 412 return Corr(self.content[:: -1]) + 413 + 414 def thin(self, spacing=2, offset=0): + 415 """Thin out a correlator to suppress correlations + 416 + 417 Parameters + 418 ---------- + 419 spacing : int + 420 Keep only every 'spacing'th entry of the correlator + 421 offset : int + 422 Offset the equal spacing + 423 """ + 424 new_content = [] + 425 for t in range(self.T): + 426 if (offset + t) % spacing != 0: + 427 new_content.append(None) + 428 else: + 429 new_content.append(self.content[t]) + 430 return Corr(new_content) + 431 + 432 def correlate(self, partner): + 433 """Correlate the correlator with another correlator or Obs + 434 + 435 Parameters + 436 ---------- + 437 partner : Obs or Corr + 438 partner to correlate the correlator with. + 439 Can either be an Obs which is correlated with all entries of the + 440 correlator or a Corr of same length. + 441 """ + 442 if self.N != 1: + 443 raise Exception("Only one-dimensional correlators can be safely correlated.") + 444 new_content = [] + 445 for x0, t_slice in enumerate(self.content): + 446 if _check_for_none(self, t_slice): + 447 new_content.append(None) + 448 else: + 449 if isinstance(partner, Corr): + 450 if _check_for_none(partner, partner.content[x0]): + 451 new_content.append(None) + 452 else: + 453 new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice])) + 454 elif isinstance(partner, Obs): # Should this include CObs? + 455 new_content.append(np.array([correlate(o, partner) for o in t_slice])) + 456 else: + 457 raise Exception("Can only correlate with an Obs or a Corr.") 458 - 459 def reweight(self, weight, **kwargs): - 460 """Reweight the correlator. - 461 - 462 Parameters - 463 ---------- - 464 weight : Obs - 465 Reweighting factor. An Observable that has to be defined on a superset of the - 466 configurations in obs[i].idl for all i. - 467 all_configs : bool - 468 if True, the reweighted observables are normalized by the average of - 469 the reweighting factor on all configurations in weight.idl and not - 470 on the configurations in obs[i].idl. - 471 """ - 472 if self.N != 1: - 473 raise Exception("Reweighting only implemented for one-dimensional correlators.") - 474 new_content = [] - 475 for t_slice in self.content: - 476 if _check_for_none(self, t_slice): - 477 new_content.append(None) - 478 else: - 479 new_content.append(np.array(reweight(weight, t_slice, **kwargs))) - 480 return Corr(new_content) - 481 - 482 def T_symmetry(self, partner, parity=+1): - 483 """Return the time symmetry average of the correlator and its partner - 484 - 485 Parameters - 486 ---------- - 487 partner : Corr - 488 Time symmetry partner of the Corr - 489 partity : int - 490 Parity quantum number of the correlator, can be +1 or -1 - 491 """ - 492 if self.N != 1: - 493 raise Exception("T_symmetry only implemented for one-dimensional correlators.") - 494 if not isinstance(partner, Corr): - 495 raise Exception("T partner has to be a Corr object.") - 496 if parity not in [+1, -1]: - 497 raise Exception("Parity has to be +1 or -1.") - 498 T_partner = parity * partner.reverse() - 499 - 500 t_slices = [] - 501 test = (self - T_partner) - 502 test.gamma_method() - 503 for x0, t_slice in enumerate(test.content): - 504 if t_slice is not None: - 505 if not t_slice[0].is_zero_within_error(5): - 506 t_slices.append(x0) - 507 if t_slices: - 508 warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning) - 509 - 510 return (self + T_partner) / 2 + 459 return Corr(new_content) + 460 + 461 def reweight(self, weight, **kwargs): + 462 """Reweight the correlator. + 463 + 464 Parameters + 465 ---------- + 466 weight : Obs + 467 Reweighting factor. An Observable that has to be defined on a superset of the + 468 configurations in obs[i].idl for all i. + 469 all_configs : bool + 470 if True, the reweighted observables are normalized by the average of + 471 the reweighting factor on all configurations in weight.idl and not + 472 on the configurations in obs[i].idl. + 473 """ + 474 if self.N != 1: + 475 raise Exception("Reweighting only implemented for one-dimensional correlators.") + 476 new_content = [] + 477 for t_slice in self.content: + 478 if _check_for_none(self, t_slice): + 479 new_content.append(None) + 480 else: + 481 new_content.append(np.array(reweight(weight, t_slice, **kwargs))) + 482 return Corr(new_content) + 483 + 484 def T_symmetry(self, partner, parity=+1): + 485 """Return the time symmetry average of the correlator and its partner + 486 + 487 Parameters + 488 ---------- + 489 partner : Corr + 490 Time symmetry partner of the Corr + 491 partity : int + 492 Parity quantum number of the correlator, can be +1 or -1 + 493 """ + 494 if self.N != 1: + 495 raise Exception("T_symmetry only implemented for one-dimensional correlators.") + 496 if not isinstance(partner, Corr): + 497 raise Exception("T partner has to be a Corr object.") + 498 if parity not in [+1, -1]: + 499 raise Exception("Parity has to be +1 or -1.") + 500 T_partner = parity * partner.reverse() + 501 + 502 t_slices = [] + 503 test = (self - T_partner) + 504 test.gamma_method() + 505 for x0, t_slice in enumerate(test.content): + 506 if t_slice is not None: + 507 if not t_slice[0].is_zero_within_error(5): + 508 t_slices.append(x0) + 509 if t_slices: + 510 warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning) 511 - 512 def deriv(self, variant="symmetric"): - 513 """Return the first derivative of the correlator with respect to x0. - 514 - 515 Parameters - 516 ---------- - 517 variant : str - 518 decides which definition of the finite differences derivative is used. - 519 Available choice: symmetric, forward, backward, improved, log, default: symmetric - 520 """ - 521 if self.N != 1: - 522 raise Exception("deriv only implemented for one-dimensional correlators.") - 523 if variant == "symmetric": - 524 newcontent = [] - 525 for t in range(1, self.T - 1): - 526 if (self.content[t - 1] is None) or (self.content[t + 1] is None): - 527 newcontent.append(None) - 528 else: - 529 newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1])) - 530 if (all([x is None for x in newcontent])): - 531 raise Exception('Derivative is undefined at all timeslices') - 532 return Corr(newcontent, padding=[1, 1]) - 533 elif variant == "forward": - 534 newcontent = [] - 535 for t in range(self.T - 1): - 536 if (self.content[t] is None) or (self.content[t + 1] is None): - 537 newcontent.append(None) - 538 else: - 539 newcontent.append(self.content[t + 1] - self.content[t]) - 540 if (all([x is None for x in newcontent])): - 541 raise Exception("Derivative is undefined at all timeslices") - 542 return Corr(newcontent, padding=[0, 1]) - 543 elif variant == "backward": - 544 newcontent = [] - 545 for t in range(1, self.T): - 546 if (self.content[t - 1] is None) or (self.content[t] is None): - 547 newcontent.append(None) - 548 else: - 549 newcontent.append(self.content[t] - self.content[t - 1]) - 550 if (all([x is None for x in newcontent])): - 551 raise Exception("Derivative is undefined at all timeslices") - 552 return Corr(newcontent, padding=[1, 0]) - 553 elif variant == "improved": - 554 newcontent = [] - 555 for t in range(2, self.T - 2): - 556 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): - 557 newcontent.append(None) - 558 else: - 559 newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2])) - 560 if (all([x is None for x in newcontent])): - 561 raise Exception('Derivative is undefined at all timeslices') - 562 return Corr(newcontent, padding=[2, 2]) - 563 elif variant == 'log': - 564 newcontent = [] - 565 for t in range(self.T): - 566 if (self.content[t] is None) or (self.content[t] <= 0): - 567 newcontent.append(None) - 568 else: - 569 newcontent.append(np.log(self.content[t])) - 570 if (all([x is None for x in newcontent])): - 571 raise Exception("Log is undefined at all timeslices") - 572 logcorr = Corr(newcontent) - 573 return self * logcorr.deriv('symmetric') - 574 else: - 575 raise Exception("Unknown variant.") - 576 - 577 def second_deriv(self, variant="symmetric"): - 578 """Return the second derivative of the correlator with respect to x0. - 579 - 580 Parameters - 581 ---------- - 582 variant : str - 583 decides which definition of the finite differences derivative is used. - 584 Available choice: symmetric, improved, log, default: symmetric - 585 """ - 586 if self.N != 1: - 587 raise Exception("second_deriv only implemented for one-dimensional correlators.") - 588 if variant == "symmetric": - 589 newcontent = [] - 590 for t in range(1, self.T - 1): - 591 if (self.content[t - 1] is None) or (self.content[t + 1] is None): - 592 newcontent.append(None) - 593 else: - 594 newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1])) - 595 if (all([x is None for x in newcontent])): - 596 raise Exception("Derivative is undefined at all timeslices") - 597 return Corr(newcontent, padding=[1, 1]) - 598 elif variant == "improved": - 599 newcontent = [] - 600 for t in range(2, self.T - 2): - 601 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): - 602 newcontent.append(None) - 603 else: - 604 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])) - 605 if (all([x is None for x in newcontent])): - 606 raise Exception("Derivative is undefined at all timeslices") - 607 return Corr(newcontent, padding=[2, 2]) - 608 elif variant == 'log': - 609 newcontent = [] - 610 for t in range(self.T): - 611 if (self.content[t] is None) or (self.content[t] <= 0): - 612 newcontent.append(None) - 613 else: - 614 newcontent.append(np.log(self.content[t])) - 615 if (all([x is None for x in newcontent])): - 616 raise Exception("Log is undefined at all timeslices") - 617 logcorr = Corr(newcontent) - 618 return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2) - 619 else: - 620 raise Exception("Unknown variant.") - 621 - 622 def m_eff(self, variant='log', guess=1.0): - 623 """Returns the effective mass of the correlator as correlator object - 624 - 625 Parameters - 626 ---------- - 627 variant : str - 628 log : uses the standard effective mass log(C(t) / C(t+1)) - 629 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. - 630 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. - 631 See, e.g., arXiv:1205.5380 - 632 arccosh : Uses the explicit form of the symmetrized correlator (not recommended) - 633 logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2 - 634 guess : float - 635 guess for the root finder, only relevant for the root variant - 636 """ - 637 if self.N != 1: - 638 raise Exception('Correlator must be projected before getting m_eff') - 639 if variant == 'log': - 640 newcontent = [] - 641 for t in range(self.T - 1): - 642 if ((self.content[t] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): - 643 newcontent.append(None) - 644 elif self.content[t][0].value / self.content[t + 1][0].value < 0: + 512 return (self + T_partner) / 2 + 513 + 514 def deriv(self, variant="symmetric"): + 515 """Return the first derivative of the correlator with respect to x0. + 516 + 517 Parameters + 518 ---------- + 519 variant : str + 520 decides which definition of the finite differences derivative is used. + 521 Available choice: symmetric, forward, backward, improved, log, default: symmetric + 522 """ + 523 if self.N != 1: + 524 raise Exception("deriv only implemented for one-dimensional correlators.") + 525 if variant == "symmetric": + 526 newcontent = [] + 527 for t in range(1, self.T - 1): + 528 if (self.content[t - 1] is None) or (self.content[t + 1] is None): + 529 newcontent.append(None) + 530 else: + 531 newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1])) + 532 if (all([x is None for x in newcontent])): + 533 raise Exception('Derivative is undefined at all timeslices') + 534 return Corr(newcontent, padding=[1, 1]) + 535 elif variant == "forward": + 536 newcontent = [] + 537 for t in range(self.T - 1): + 538 if (self.content[t] is None) or (self.content[t + 1] is None): + 539 newcontent.append(None) + 540 else: + 541 newcontent.append(self.content[t + 1] - self.content[t]) + 542 if (all([x is None for x in newcontent])): + 543 raise Exception("Derivative is undefined at all timeslices") + 544 return Corr(newcontent, padding=[0, 1]) + 545 elif variant == "backward": + 546 newcontent = [] + 547 for t in range(1, self.T): + 548 if (self.content[t - 1] is None) or (self.content[t] is None): + 549 newcontent.append(None) + 550 else: + 551 newcontent.append(self.content[t] - self.content[t - 1]) + 552 if (all([x is None for x in newcontent])): + 553 raise Exception("Derivative is undefined at all timeslices") + 554 return Corr(newcontent, padding=[1, 0]) + 555 elif variant == "improved": + 556 newcontent = [] + 557 for t in range(2, self.T - 2): + 558 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): + 559 newcontent.append(None) + 560 else: + 561 newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2])) + 562 if (all([x is None for x in newcontent])): + 563 raise Exception('Derivative is undefined at all timeslices') + 564 return Corr(newcontent, padding=[2, 2]) + 565 elif variant == 'log': + 566 newcontent = [] + 567 for t in range(self.T): + 568 if (self.content[t] is None) or (self.content[t] <= 0): + 569 newcontent.append(None) + 570 else: + 571 newcontent.append(np.log(self.content[t])) + 572 if (all([x is None for x in newcontent])): + 573 raise Exception("Log is undefined at all timeslices") + 574 logcorr = Corr(newcontent) + 575 return self * logcorr.deriv('symmetric') + 576 else: + 577 raise Exception("Unknown variant.") + 578 + 579 def second_deriv(self, variant="symmetric"): + 580 """Return the second derivative of the correlator with respect to x0. + 581 + 582 Parameters + 583 ---------- + 584 variant : str + 585 decides which definition of the finite differences derivative is used. + 586 Available choice: symmetric, improved, log, default: symmetric + 587 """ + 588 if self.N != 1: + 589 raise Exception("second_deriv only implemented for one-dimensional correlators.") + 590 if variant == "symmetric": + 591 newcontent = [] + 592 for t in range(1, self.T - 1): + 593 if (self.content[t - 1] is None) or (self.content[t + 1] is None): + 594 newcontent.append(None) + 595 else: + 596 newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1])) + 597 if (all([x is None for x in newcontent])): + 598 raise Exception("Derivative is undefined at all timeslices") + 599 return Corr(newcontent, padding=[1, 1]) + 600 elif variant == "improved": + 601 newcontent = [] + 602 for t in range(2, self.T - 2): + 603 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): + 604 newcontent.append(None) + 605 else: + 606 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])) + 607 if (all([x is None for x in newcontent])): + 608 raise Exception("Derivative is undefined at all timeslices") + 609 return Corr(newcontent, padding=[2, 2]) + 610 elif variant == 'log': + 611 newcontent = [] + 612 for t in range(self.T): + 613 if (self.content[t] is None) or (self.content[t] <= 0): + 614 newcontent.append(None) + 615 else: + 616 newcontent.append(np.log(self.content[t])) + 617 if (all([x is None for x in newcontent])): + 618 raise Exception("Log is undefined at all timeslices") + 619 logcorr = Corr(newcontent) + 620 return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2) + 621 else: + 622 raise Exception("Unknown variant.") + 623 + 624 def m_eff(self, variant='log', guess=1.0): + 625 """Returns the effective mass of the correlator as correlator object + 626 + 627 Parameters + 628 ---------- + 629 variant : str + 630 log : uses the standard effective mass log(C(t) / C(t+1)) + 631 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. + 632 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. + 633 See, e.g., arXiv:1205.5380 + 634 arccosh : Uses the explicit form of the symmetrized correlator (not recommended) + 635 logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2 + 636 guess : float + 637 guess for the root finder, only relevant for the root variant + 638 """ + 639 if self.N != 1: + 640 raise Exception('Correlator must be projected before getting m_eff') + 641 if variant == 'log': + 642 newcontent = [] + 643 for t in range(self.T - 1): + 644 if ((self.content[t] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): 645 newcontent.append(None) - 646 else: - 647 newcontent.append(self.content[t] / self.content[t + 1]) - 648 if (all([x is None for x in newcontent])): - 649 raise Exception('m_eff is undefined at all timeslices') - 650 - 651 return np.log(Corr(newcontent, padding=[0, 1])) + 646 elif self.content[t][0].value / self.content[t + 1][0].value < 0: + 647 newcontent.append(None) + 648 else: + 649 newcontent.append(self.content[t] / self.content[t + 1]) + 650 if (all([x is None for x in newcontent])): + 651 raise Exception('m_eff is undefined at all timeslices') 652 - 653 elif variant == 'logsym': - 654 newcontent = [] - 655 for t in range(1, self.T - 1): - 656 if ((self.content[t - 1] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): - 657 newcontent.append(None) - 658 elif self.content[t - 1][0].value / self.content[t + 1][0].value < 0: + 653 return np.log(Corr(newcontent, padding=[0, 1])) + 654 + 655 elif variant == 'logsym': + 656 newcontent = [] + 657 for t in range(1, self.T - 1): + 658 if ((self.content[t - 1] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): 659 newcontent.append(None) - 660 else: - 661 newcontent.append(self.content[t - 1] / self.content[t + 1]) - 662 if (all([x is None for x in newcontent])): - 663 raise Exception('m_eff is undefined at all timeslices') - 664 - 665 return np.log(Corr(newcontent, padding=[1, 1])) / 2 + 660 elif self.content[t - 1][0].value / self.content[t + 1][0].value < 0: + 661 newcontent.append(None) + 662 else: + 663 newcontent.append(self.content[t - 1] / self.content[t + 1]) + 664 if (all([x is None for x in newcontent])): + 665 raise Exception('m_eff is undefined at all timeslices') 666 - 667 elif variant in ['periodic', 'cosh', 'sinh']: - 668 if variant in ['periodic', 'cosh']: - 669 func = anp.cosh - 670 else: - 671 func = anp.sinh - 672 - 673 def root_function(x, d): - 674 return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d - 675 - 676 newcontent = [] - 677 for t in range(self.T - 1): - 678 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 1][0].value == 0): - 679 newcontent.append(None) - 680 # Fill the two timeslices in the middle of the lattice with their predecessors - 681 elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]: - 682 newcontent.append(newcontent[-1]) - 683 elif self.content[t][0].value / self.content[t + 1][0].value < 0: - 684 newcontent.append(None) - 685 else: - 686 newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess))) - 687 if (all([x is None for x in newcontent])): - 688 raise Exception('m_eff is undefined at all timeslices') - 689 - 690 return Corr(newcontent, padding=[0, 1]) + 667 return np.log(Corr(newcontent, padding=[1, 1])) / 2 + 668 + 669 elif variant in ['periodic', 'cosh', 'sinh']: + 670 if variant in ['periodic', 'cosh']: + 671 func = anp.cosh + 672 else: + 673 func = anp.sinh + 674 + 675 def root_function(x, d): + 676 return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d + 677 + 678 newcontent = [] + 679 for t in range(self.T - 1): + 680 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 1][0].value == 0): + 681 newcontent.append(None) + 682 # Fill the two timeslices in the middle of the lattice with their predecessors + 683 elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]: + 684 newcontent.append(newcontent[-1]) + 685 elif self.content[t][0].value / self.content[t + 1][0].value < 0: + 686 newcontent.append(None) + 687 else: + 688 newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess))) + 689 if (all([x is None for x in newcontent])): + 690 raise Exception('m_eff is undefined at all timeslices') 691 - 692 elif variant == 'arccosh': - 693 newcontent = [] - 694 for t in range(1, self.T - 1): - 695 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None) or (self.content[t][0].value == 0): - 696 newcontent.append(None) - 697 else: - 698 newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t])) - 699 if (all([x is None for x in newcontent])): - 700 raise Exception("m_eff is undefined at all timeslices") - 701 return np.arccosh(Corr(newcontent, padding=[1, 1])) - 702 - 703 else: - 704 raise Exception('Unknown variant.') - 705 - 706 def fit(self, function, fitrange=None, silent=False, **kwargs): - 707 r'''Fits function to the data - 708 - 709 Parameters - 710 ---------- - 711 function : obj - 712 function to fit to the data. See fits.least_squares for details. - 713 fitrange : list - 714 Two element list containing the timeslices on which the fit is supposed to start and stop. - 715 Caution: This range is inclusive as opposed to standard python indexing. - 716 `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6. - 717 If not specified, self.prange or all timeslices are used. - 718 silent : bool - 719 Decides whether output is printed to the standard output. - 720 ''' - 721 if self.N != 1: - 722 raise Exception("Correlator must be projected before fitting") - 723 - 724 if fitrange is None: - 725 if self.prange: - 726 fitrange = self.prange - 727 else: - 728 fitrange = [0, self.T - 1] - 729 else: - 730 if not isinstance(fitrange, list): - 731 raise Exception("fitrange has to be a list with two elements") - 732 if len(fitrange) != 2: - 733 raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]") - 734 - 735 xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] - 736 ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] - 737 result = least_squares(xs, ys, function, silent=silent, **kwargs) - 738 return result - 739 - 740 def plateau(self, plateau_range=None, method="fit", auto_gamma=False): - 741 """ Extract a plateau value from a Corr object - 742 - 743 Parameters - 744 ---------- - 745 plateau_range : list - 746 list with two entries, indicating the first and the last timeslice - 747 of the plateau region. - 748 method : str - 749 method to extract the plateau. - 750 'fit' fits a constant to the plateau region - 751 'avg', 'average' or 'mean' just average over the given timeslices. - 752 auto_gamma : bool - 753 apply gamma_method with default parameters to the Corr. Defaults to None - 754 """ - 755 if not plateau_range: - 756 if self.prange: - 757 plateau_range = self.prange - 758 else: - 759 raise Exception("no plateau range provided") - 760 if self.N != 1: - 761 raise Exception("Correlator must be projected before getting a plateau.") - 762 if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])): - 763 raise Exception("plateau is undefined at all timeslices in plateaurange.") - 764 if auto_gamma: - 765 self.gamma_method() - 766 if method == "fit": - 767 def const_func(a, t): - 768 return a[0] - 769 return self.fit(const_func, plateau_range)[0] - 770 elif method in ["avg", "average", "mean"]: - 771 returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None]) - 772 return returnvalue - 773 - 774 else: - 775 raise Exception("Unsupported plateau method: " + method) - 776 - 777 def set_prange(self, prange): - 778 """Sets the attribute prange of the Corr object.""" - 779 if not len(prange) == 2: - 780 raise Exception("prange must be a list or array with two values") - 781 if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))): - 782 raise Exception("Start and end point must be integers") - 783 if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]): - 784 raise Exception("Start and end point must define a range in the interval 0,T") - 785 - 786 self.prange = prange - 787 return - 788 - 789 def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None): - 790 """Plots the correlator using the tag of the correlator as label if available. - 791 - 792 Parameters - 793 ---------- - 794 x_range : list - 795 list of two values, determining the range of the x-axis e.g. [4, 8]. - 796 comp : Corr or list of Corr - 797 Correlator or list of correlators which are plotted for comparison. - 798 The tags of these correlators are used as labels if available. - 799 logscale : bool - 800 Sets y-axis to logscale. - 801 plateau : Obs - 802 Plateau value to be visualized in the figure. - 803 fit_res : Fit_result - 804 Fit_result object to be visualized. - 805 ylabel : str - 806 Label for the y-axis. - 807 save : str - 808 path to file in which the figure should be saved. - 809 auto_gamma : bool - 810 Apply the gamma method with standard parameters to all correlators and plateau values before plotting. - 811 hide_sigma : float - 812 Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors. - 813 references : list - 814 List of floating point values that are displayed as horizontal lines for reference. - 815 title : string - 816 Optional title of the figure. - 817 """ - 818 if self.N != 1: - 819 raise Exception("Correlator must be projected before plotting") - 820 - 821 if auto_gamma: - 822 self.gamma_method() - 823 - 824 if x_range is None: - 825 x_range = [0, self.T - 1] - 826 - 827 fig = plt.figure() - 828 ax1 = fig.add_subplot(111) - 829 - 830 x, y, y_err = self.plottable() - 831 if hide_sigma: - 832 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 - 833 else: - 834 hide_from = None - 835 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag) - 836 if logscale: - 837 ax1.set_yscale('log') - 838 else: - 839 if y_range is None: - 840 try: - 841 y_min = min([(x[0].value - x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) - 842 y_max = max([(x[0].value + x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) - 843 ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)]) - 844 except Exception: - 845 pass - 846 else: - 847 ax1.set_ylim(y_range) - 848 if comp: - 849 if isinstance(comp, (Corr, list)): - 850 for corr in comp if isinstance(comp, list) else [comp]: - 851 if auto_gamma: - 852 corr.gamma_method() - 853 x, y, y_err = corr.plottable() - 854 if hide_sigma: - 855 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 - 856 else: - 857 hide_from = None - 858 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor']) - 859 else: - 860 raise Exception("'comp' must be a correlator or a list of correlators.") - 861 - 862 if plateau: - 863 if isinstance(plateau, Obs): - 864 if auto_gamma: - 865 plateau.gamma_method() - 866 ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau)) - 867 ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-') - 868 else: - 869 raise Exception("'plateau' must be an Obs") - 870 - 871 if references: - 872 if isinstance(references, list): - 873 for ref in references: - 874 ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--') - 875 else: - 876 raise Exception("'references' must be a list of floating pint values.") - 877 - 878 if self.prange: - 879 ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',') - 880 ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',') - 881 - 882 if fit_res: - 883 x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05) - 884 ax1.plot(x_samples, - 885 fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples), - 886 ls='-', marker=',', lw=2) - 887 - 888 ax1.set_xlabel(r'$x_0 / a$') - 889 if ylabel: - 890 ax1.set_ylabel(ylabel) - 891 ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5]) - 892 - 893 handles, labels = ax1.get_legend_handles_labels() - 894 if labels: - 895 ax1.legend() - 896 - 897 if title: - 898 plt.title(title) - 899 - 900 plt.draw() + 692 return Corr(newcontent, padding=[0, 1]) + 693 + 694 elif variant == 'arccosh': + 695 newcontent = [] + 696 for t in range(1, self.T - 1): + 697 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None) or (self.content[t][0].value == 0): + 698 newcontent.append(None) + 699 else: + 700 newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t])) + 701 if (all([x is None for x in newcontent])): + 702 raise Exception("m_eff is undefined at all timeslices") + 703 return np.arccosh(Corr(newcontent, padding=[1, 1])) + 704 + 705 else: + 706 raise Exception('Unknown variant.') + 707 + 708 def fit(self, function, fitrange=None, silent=False, **kwargs): + 709 r'''Fits function to the data + 710 + 711 Parameters + 712 ---------- + 713 function : obj + 714 function to fit to the data. See fits.least_squares for details. + 715 fitrange : list + 716 Two element list containing the timeslices on which the fit is supposed to start and stop. + 717 Caution: This range is inclusive as opposed to standard python indexing. + 718 `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6. + 719 If not specified, self.prange or all timeslices are used. + 720 silent : bool + 721 Decides whether output is printed to the standard output. + 722 ''' + 723 if self.N != 1: + 724 raise Exception("Correlator must be projected before fitting") + 725 + 726 if fitrange is None: + 727 if self.prange: + 728 fitrange = self.prange + 729 else: + 730 fitrange = [0, self.T - 1] + 731 else: + 732 if not isinstance(fitrange, list): + 733 raise Exception("fitrange has to be a list with two elements") + 734 if len(fitrange) != 2: + 735 raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]") + 736 + 737 xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] + 738 ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] + 739 result = least_squares(xs, ys, function, silent=silent, **kwargs) + 740 return result + 741 + 742 def plateau(self, plateau_range=None, method="fit", auto_gamma=False): + 743 """ Extract a plateau value from a Corr object + 744 + 745 Parameters + 746 ---------- + 747 plateau_range : list + 748 list with two entries, indicating the first and the last timeslice + 749 of the plateau region. + 750 method : str + 751 method to extract the plateau. + 752 'fit' fits a constant to the plateau region + 753 'avg', 'average' or 'mean' just average over the given timeslices. + 754 auto_gamma : bool + 755 apply gamma_method with default parameters to the Corr. Defaults to None + 756 """ + 757 if not plateau_range: + 758 if self.prange: + 759 plateau_range = self.prange + 760 else: + 761 raise Exception("no plateau range provided") + 762 if self.N != 1: + 763 raise Exception("Correlator must be projected before getting a plateau.") + 764 if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])): + 765 raise Exception("plateau is undefined at all timeslices in plateaurange.") + 766 if auto_gamma: + 767 self.gamma_method() + 768 if method == "fit": + 769 def const_func(a, t): + 770 return a[0] + 771 return self.fit(const_func, plateau_range)[0] + 772 elif method in ["avg", "average", "mean"]: + 773 returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None]) + 774 return returnvalue + 775 + 776 else: + 777 raise Exception("Unsupported plateau method: " + method) + 778 + 779 def set_prange(self, prange): + 780 """Sets the attribute prange of the Corr object.""" + 781 if not len(prange) == 2: + 782 raise Exception("prange must be a list or array with two values") + 783 if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))): + 784 raise Exception("Start and end point must be integers") + 785 if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]): + 786 raise Exception("Start and end point must define a range in the interval 0,T") + 787 + 788 self.prange = prange + 789 return + 790 + 791 def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None): + 792 """Plots the correlator using the tag of the correlator as label if available. + 793 + 794 Parameters + 795 ---------- + 796 x_range : list + 797 list of two values, determining the range of the x-axis e.g. [4, 8]. + 798 comp : Corr or list of Corr + 799 Correlator or list of correlators which are plotted for comparison. + 800 The tags of these correlators are used as labels if available. + 801 logscale : bool + 802 Sets y-axis to logscale. + 803 plateau : Obs + 804 Plateau value to be visualized in the figure. + 805 fit_res : Fit_result + 806 Fit_result object to be visualized. + 807 ylabel : str + 808 Label for the y-axis. + 809 save : str + 810 path to file in which the figure should be saved. + 811 auto_gamma : bool + 812 Apply the gamma method with standard parameters to all correlators and plateau values before plotting. + 813 hide_sigma : float + 814 Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors. + 815 references : list + 816 List of floating point values that are displayed as horizontal lines for reference. + 817 title : string + 818 Optional title of the figure. + 819 """ + 820 if self.N != 1: + 821 raise Exception("Correlator must be projected before plotting") + 822 + 823 if auto_gamma: + 824 self.gamma_method() + 825 + 826 if x_range is None: + 827 x_range = [0, self.T - 1] + 828 + 829 fig = plt.figure() + 830 ax1 = fig.add_subplot(111) + 831 + 832 x, y, y_err = self.plottable() + 833 if hide_sigma: + 834 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 + 835 else: + 836 hide_from = None + 837 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag) + 838 if logscale: + 839 ax1.set_yscale('log') + 840 else: + 841 if y_range is None: + 842 try: + 843 y_min = min([(x[0].value - x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) + 844 y_max = max([(x[0].value + x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) + 845 ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)]) + 846 except Exception: + 847 pass + 848 else: + 849 ax1.set_ylim(y_range) + 850 if comp: + 851 if isinstance(comp, (Corr, list)): + 852 for corr in comp if isinstance(comp, list) else [comp]: + 853 if auto_gamma: + 854 corr.gamma_method() + 855 x, y, y_err = corr.plottable() + 856 if hide_sigma: + 857 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 + 858 else: + 859 hide_from = None + 860 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor']) + 861 else: + 862 raise Exception("'comp' must be a correlator or a list of correlators.") + 863 + 864 if plateau: + 865 if isinstance(plateau, Obs): + 866 if auto_gamma: + 867 plateau.gamma_method() + 868 ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau)) + 869 ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-') + 870 else: + 871 raise Exception("'plateau' must be an Obs") + 872 + 873 if references: + 874 if isinstance(references, list): + 875 for ref in references: + 876 ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--') + 877 else: + 878 raise Exception("'references' must be a list of floating pint values.") + 879 + 880 if self.prange: + 881 ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',') + 882 ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',') + 883 + 884 if fit_res: + 885 x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05) + 886 ax1.plot(x_samples, + 887 fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples), + 888 ls='-', marker=',', lw=2) + 889 + 890 ax1.set_xlabel(r'$x_0 / a$') + 891 if ylabel: + 892 ax1.set_ylabel(ylabel) + 893 ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5]) + 894 + 895 handles, labels = ax1.get_legend_handles_labels() + 896 if labels: + 897 ax1.legend() + 898 + 899 if title: + 900 plt.title(title) 901 - 902 if save: - 903 if isinstance(save, str): - 904 fig.savefig(save, bbox_inches='tight') - 905 else: - 906 raise Exception("'save' has to be a string.") - 907 - 908 def spaghetti_plot(self, logscale=True): - 909 """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations. - 910 - 911 Parameters - 912 ---------- - 913 logscale : bool - 914 Determines whether the scale of the y-axis is logarithmic or standard. - 915 """ - 916 if self.N != 1: - 917 raise Exception("Correlator needs to be projected first.") - 918 - 919 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])) - 920 x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None] - 921 - 922 for name in mc_names: - 923 data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T - 924 - 925 fig = plt.figure() - 926 ax = fig.add_subplot(111) - 927 for dat in data: - 928 ax.plot(x0_vals, dat, ls='-', marker='') - 929 - 930 if logscale is True: - 931 ax.set_yscale('log') - 932 - 933 ax.set_xlabel(r'$x_0 / a$') - 934 plt.title(name) - 935 plt.draw() - 936 - 937 def dump(self, filename, datatype="json.gz", **kwargs): - 938 """Dumps the Corr into a file of chosen type - 939 Parameters - 940 ---------- - 941 filename : str - 942 Name of the file to be saved. - 943 datatype : str - 944 Format of the exported file. Supported formats include - 945 "json.gz" and "pickle" - 946 path : str - 947 specifies a custom path for the file (default '.') - 948 """ - 949 if datatype == "json.gz": - 950 from .input.json import dump_to_json - 951 if 'path' in kwargs: - 952 file_name = kwargs.get('path') + '/' + filename - 953 else: - 954 file_name = filename - 955 dump_to_json(self, file_name) - 956 elif datatype == "pickle": - 957 dump_object(self, filename, **kwargs) - 958 else: - 959 raise Exception("Unknown datatype " + str(datatype)) - 960 - 961 def print(self, print_range=None): - 962 print(self.__repr__(print_range)) - 963 - 964 def __repr__(self, print_range=None): - 965 if print_range is None: - 966 print_range = [0, None] - 967 - 968 content_string = "" - 969 content_string += "Corr T=" + str(self.T) + " N=" + str(self.N) + "\n" # +" filled with"+ str(type(self.content[0][0])) there should be a good solution here - 970 - 971 if self.tag is not None: - 972 content_string += "Description: " + self.tag + "\n" - 973 if self.N != 1: - 974 return content_string - 975 if isinstance(self[0], CObs): + 902 plt.draw() + 903 + 904 if save: + 905 if isinstance(save, str): + 906 fig.savefig(save, bbox_inches='tight') + 907 else: + 908 raise Exception("'save' has to be a string.") + 909 + 910 def spaghetti_plot(self, logscale=True): + 911 """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations. + 912 + 913 Parameters + 914 ---------- + 915 logscale : bool + 916 Determines whether the scale of the y-axis is logarithmic or standard. + 917 """ + 918 if self.N != 1: + 919 raise Exception("Correlator needs to be projected first.") + 920 + 921 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])) + 922 x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None] + 923 + 924 for name in mc_names: + 925 data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T + 926 + 927 fig = plt.figure() + 928 ax = fig.add_subplot(111) + 929 for dat in data: + 930 ax.plot(x0_vals, dat, ls='-', marker='') + 931 + 932 if logscale is True: + 933 ax.set_yscale('log') + 934 + 935 ax.set_xlabel(r'$x_0 / a$') + 936 plt.title(name) + 937 plt.draw() + 938 + 939 def dump(self, filename, datatype="json.gz", **kwargs): + 940 """Dumps the Corr into a file of chosen type + 941 Parameters + 942 ---------- + 943 filename : str + 944 Name of the file to be saved. + 945 datatype : str + 946 Format of the exported file. Supported formats include + 947 "json.gz" and "pickle" + 948 path : str + 949 specifies a custom path for the file (default '.') + 950 """ + 951 if datatype == "json.gz": + 952 from .input.json import dump_to_json + 953 if 'path' in kwargs: + 954 file_name = kwargs.get('path') + '/' + filename + 955 else: + 956 file_name = filename + 957 dump_to_json(self, file_name) + 958 elif datatype == "pickle": + 959 dump_object(self, filename, **kwargs) + 960 else: + 961 raise Exception("Unknown datatype " + str(datatype)) + 962 + 963 def print(self, print_range=None): + 964 print(self.__repr__(print_range)) + 965 + 966 def __repr__(self, print_range=None): + 967 if print_range is None: + 968 print_range = [0, None] + 969 + 970 content_string = "" + 971 content_string += "Corr T=" + str(self.T) + " N=" + str(self.N) + "\n" # +" filled with"+ str(type(self.content[0][0])) there should be a good solution here + 972 + 973 if self.tag is not None: + 974 content_string += "Description: " + self.tag + "\n" + 975 if self.N != 1: 976 return content_string - 977 - 978 if print_range[1]: - 979 print_range[1] += 1 - 980 content_string += 'x0/a\tCorr(x0/a)\n------------------\n' - 981 for i, sub_corr in enumerate(self.content[print_range[0]:print_range[1]]): - 982 if sub_corr is None: - 983 content_string += str(i + print_range[0]) + '\n' - 984 else: - 985 content_string += str(i + print_range[0]) - 986 for element in sub_corr: - 987 content_string += '\t' + ' ' * int(element >= 0) + str(element) - 988 content_string += '\n' - 989 return content_string - 990 - 991 def __str__(self): - 992 return self.__repr__() - 993 - 994 # We define the basic operations, that can be performed with correlators. - 995 # While */+- get defined here, they only work for Corr*Obs and not Obs*Corr. - 996 # This is because Obs*Corr checks Obs.__mul__ first and does not catch an exception. - 997 # One could try and tell Obs to check if the y in __mul__ is a Corr and - 998 - 999 def __add__(self, y): -1000 if isinstance(y, Corr): -1001 if ((self.N != y.N) or (self.T != y.T)): -1002 raise Exception("Addition of Corrs with different shape") -1003 newcontent = [] -1004 for t in range(self.T): -1005 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): -1006 newcontent.append(None) -1007 else: -1008 newcontent.append(self.content[t] + y.content[t]) -1009 return Corr(newcontent) -1010 -1011 elif isinstance(y, (Obs, int, float, CObs)): -1012 newcontent = [] -1013 for t in range(self.T): -1014 if _check_for_none(self, self.content[t]): -1015 newcontent.append(None) -1016 else: -1017 newcontent.append(self.content[t] + y) -1018 return Corr(newcontent, prange=self.prange) -1019 elif isinstance(y, np.ndarray): -1020 if y.shape == (self.T,): -1021 return Corr(list((np.array(self.content).T + y).T)) -1022 else: -1023 raise ValueError("operands could not be broadcast together") -1024 else: -1025 raise TypeError("Corr + wrong type") -1026 -1027 def __mul__(self, y): -1028 if isinstance(y, Corr): -1029 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): -1030 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") -1031 newcontent = [] -1032 for t in range(self.T): -1033 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): -1034 newcontent.append(None) -1035 else: -1036 newcontent.append(self.content[t] * y.content[t]) -1037 return Corr(newcontent) -1038 -1039 elif isinstance(y, (Obs, int, float, CObs)): -1040 newcontent = [] -1041 for t in range(self.T): -1042 if _check_for_none(self, self.content[t]): -1043 newcontent.append(None) -1044 else: -1045 newcontent.append(self.content[t] * y) -1046 return Corr(newcontent, prange=self.prange) -1047 elif isinstance(y, np.ndarray): -1048 if y.shape == (self.T,): -1049 return Corr(list((np.array(self.content).T * y).T)) -1050 else: -1051 raise ValueError("operands could not be broadcast together") -1052 else: -1053 raise TypeError("Corr * wrong type") -1054 -1055 def __truediv__(self, y): -1056 if isinstance(y, Corr): -1057 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): -1058 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") -1059 newcontent = [] -1060 for t in range(self.T): -1061 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): -1062 newcontent.append(None) -1063 else: -1064 newcontent.append(self.content[t] / y.content[t]) -1065 for t in range(self.T): -1066 if _check_for_none(self, newcontent[t]): -1067 continue -1068 if np.isnan(np.sum(newcontent[t]).value): -1069 newcontent[t] = None -1070 -1071 if all([item is None for item in newcontent]): -1072 raise Exception("Division returns completely undefined correlator") -1073 return Corr(newcontent) -1074 -1075 elif isinstance(y, (Obs, CObs)): -1076 if isinstance(y, Obs): -1077 if y.value == 0: -1078 raise Exception('Division by zero will return undefined correlator') -1079 if isinstance(y, CObs): -1080 if y.is_zero(): -1081 raise Exception('Division by zero will return undefined correlator') -1082 -1083 newcontent = [] -1084 for t in range(self.T): -1085 if _check_for_none(self, self.content[t]): -1086 newcontent.append(None) -1087 else: -1088 newcontent.append(self.content[t] / y) -1089 return Corr(newcontent, prange=self.prange) -1090 -1091 elif isinstance(y, (int, float)): -1092 if y == 0: -1093 raise Exception('Division by zero will return undefined correlator') -1094 newcontent = [] -1095 for t in range(self.T): -1096 if _check_for_none(self, self.content[t]): -1097 newcontent.append(None) -1098 else: -1099 newcontent.append(self.content[t] / y) -1100 return Corr(newcontent, prange=self.prange) -1101 elif isinstance(y, np.ndarray): -1102 if y.shape == (self.T,): -1103 return Corr(list((np.array(self.content).T / y).T)) -1104 else: -1105 raise ValueError("operands could not be broadcast together") -1106 else: -1107 raise TypeError('Corr / wrong type') -1108 -1109 def __neg__(self): -1110 newcontent = [None if _check_for_none(self, item) else -1. * item for item in self.content] -1111 return Corr(newcontent, prange=self.prange) -1112 -1113 def __sub__(self, y): -1114 return self + (-y) -1115 -1116 def __pow__(self, y): -1117 if isinstance(y, (Obs, int, float, CObs)): -1118 newcontent = [None if _check_for_none(self, item) else item**y for item in self.content] -1119 return Corr(newcontent, prange=self.prange) -1120 else: -1121 raise TypeError('Type of exponent not supported') -1122 -1123 def __abs__(self): -1124 newcontent = [None if _check_for_none(self, item) else np.abs(item) for item in self.content] -1125 return Corr(newcontent, prange=self.prange) -1126 -1127 # The numpy functions: -1128 def sqrt(self): -1129 return self ** 0.5 -1130 -1131 def log(self): -1132 newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content] -1133 return Corr(newcontent, prange=self.prange) -1134 -1135 def exp(self): -1136 newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content] -1137 return Corr(newcontent, prange=self.prange) -1138 -1139 def _apply_func_to_corr(self, func): -1140 newcontent = [None if _check_for_none(self, item) else func(item) for item in self.content] -1141 for t in range(self.T): -1142 if _check_for_none(self, newcontent[t]): -1143 continue -1144 tmp_sum = np.sum(newcontent[t]) -1145 if hasattr(tmp_sum, "value"): -1146 if np.isnan(tmp_sum.value): -1147 newcontent[t] = None -1148 if all([item is None for item in newcontent]): -1149 raise Exception('Operation returns undefined correlator') -1150 return Corr(newcontent) -1151 -1152 def sin(self): -1153 return self._apply_func_to_corr(np.sin) -1154 -1155 def cos(self): -1156 return self._apply_func_to_corr(np.cos) -1157 -1158 def tan(self): -1159 return self._apply_func_to_corr(np.tan) -1160 -1161 def sinh(self): -1162 return self._apply_func_to_corr(np.sinh) -1163 -1164 def cosh(self): -1165 return self._apply_func_to_corr(np.cosh) -1166 -1167 def tanh(self): -1168 return self._apply_func_to_corr(np.tanh) -1169 -1170 def arcsin(self): -1171 return self._apply_func_to_corr(np.arcsin) -1172 -1173 def arccos(self): -1174 return self._apply_func_to_corr(np.arccos) -1175 -1176 def arctan(self): -1177 return self._apply_func_to_corr(np.arctan) -1178 -1179 def arcsinh(self): -1180 return self._apply_func_to_corr(np.arcsinh) -1181 -1182 def arccosh(self): -1183 return self._apply_func_to_corr(np.arccosh) -1184 -1185 def arctanh(self): -1186 return self._apply_func_to_corr(np.arctanh) -1187 -1188 # Right hand side operations (require tweak in main module to work) -1189 def __radd__(self, y): -1190 return self + y -1191 -1192 def __rsub__(self, y): -1193 return -self + y -1194 -1195 def __rmul__(self, y): -1196 return self * y -1197 -1198 def __rtruediv__(self, y): -1199 return (self / y) ** (-1) -1200 -1201 @property -1202 def real(self): -1203 def return_real(obs_OR_cobs): -1204 if isinstance(obs_OR_cobs.flatten()[0], CObs): -1205 return np.vectorize(lambda x: x.real)(obs_OR_cobs) -1206 else: -1207 return obs_OR_cobs -1208 -1209 return self._apply_func_to_corr(return_real) + 977 if isinstance(self[0], CObs): + 978 return content_string + 979 + 980 if print_range[1]: + 981 print_range[1] += 1 + 982 content_string += 'x0/a\tCorr(x0/a)\n------------------\n' + 983 for i, sub_corr in enumerate(self.content[print_range[0]:print_range[1]]): + 984 if sub_corr is None: + 985 content_string += str(i + print_range[0]) + '\n' + 986 else: + 987 content_string += str(i + print_range[0]) + 988 for element in sub_corr: + 989 content_string += '\t' + ' ' * int(element >= 0) + str(element) + 990 content_string += '\n' + 991 return content_string + 992 + 993 def __str__(self): + 994 return self.__repr__() + 995 + 996 # We define the basic operations, that can be performed with correlators. + 997 # While */+- get defined here, they only work for Corr*Obs and not Obs*Corr. + 998 # This is because Obs*Corr checks Obs.__mul__ first and does not catch an exception. + 999 # One could try and tell Obs to check if the y in __mul__ is a Corr and +1000 +1001 def __add__(self, y): +1002 if isinstance(y, Corr): +1003 if ((self.N != y.N) or (self.T != y.T)): +1004 raise Exception("Addition of Corrs with different shape") +1005 newcontent = [] +1006 for t in range(self.T): +1007 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): +1008 newcontent.append(None) +1009 else: +1010 newcontent.append(self.content[t] + y.content[t]) +1011 return Corr(newcontent) +1012 +1013 elif isinstance(y, (Obs, int, float, CObs)): +1014 newcontent = [] +1015 for t in range(self.T): +1016 if _check_for_none(self, self.content[t]): +1017 newcontent.append(None) +1018 else: +1019 newcontent.append(self.content[t] + y) +1020 return Corr(newcontent, prange=self.prange) +1021 elif isinstance(y, np.ndarray): +1022 if y.shape == (self.T,): +1023 return Corr(list((np.array(self.content).T + y).T)) +1024 else: +1025 raise ValueError("operands could not be broadcast together") +1026 else: +1027 raise TypeError("Corr + wrong type") +1028 +1029 def __mul__(self, y): +1030 if isinstance(y, Corr): +1031 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): +1032 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") +1033 newcontent = [] +1034 for t in range(self.T): +1035 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): +1036 newcontent.append(None) +1037 else: +1038 newcontent.append(self.content[t] * y.content[t]) +1039 return Corr(newcontent) +1040 +1041 elif isinstance(y, (Obs, int, float, CObs)): +1042 newcontent = [] +1043 for t in range(self.T): +1044 if _check_for_none(self, self.content[t]): +1045 newcontent.append(None) +1046 else: +1047 newcontent.append(self.content[t] * y) +1048 return Corr(newcontent, prange=self.prange) +1049 elif isinstance(y, np.ndarray): +1050 if y.shape == (self.T,): +1051 return Corr(list((np.array(self.content).T * y).T)) +1052 else: +1053 raise ValueError("operands could not be broadcast together") +1054 else: +1055 raise TypeError("Corr * wrong type") +1056 +1057 def __truediv__(self, y): +1058 if isinstance(y, Corr): +1059 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): +1060 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") +1061 newcontent = [] +1062 for t in range(self.T): +1063 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): +1064 newcontent.append(None) +1065 else: +1066 newcontent.append(self.content[t] / y.content[t]) +1067 for t in range(self.T): +1068 if _check_for_none(self, newcontent[t]): +1069 continue +1070 if np.isnan(np.sum(newcontent[t]).value): +1071 newcontent[t] = None +1072 +1073 if all([item is None for item in newcontent]): +1074 raise Exception("Division returns completely undefined correlator") +1075 return Corr(newcontent) +1076 +1077 elif isinstance(y, (Obs, CObs)): +1078 if isinstance(y, Obs): +1079 if y.value == 0: +1080 raise Exception('Division by zero will return undefined correlator') +1081 if isinstance(y, CObs): +1082 if y.is_zero(): +1083 raise Exception('Division by zero will return undefined correlator') +1084 +1085 newcontent = [] +1086 for t in range(self.T): +1087 if _check_for_none(self, self.content[t]): +1088 newcontent.append(None) +1089 else: +1090 newcontent.append(self.content[t] / y) +1091 return Corr(newcontent, prange=self.prange) +1092 +1093 elif isinstance(y, (int, float)): +1094 if y == 0: +1095 raise Exception('Division by zero will return undefined correlator') +1096 newcontent = [] +1097 for t in range(self.T): +1098 if _check_for_none(self, self.content[t]): +1099 newcontent.append(None) +1100 else: +1101 newcontent.append(self.content[t] / y) +1102 return Corr(newcontent, prange=self.prange) +1103 elif isinstance(y, np.ndarray): +1104 if y.shape == (self.T,): +1105 return Corr(list((np.array(self.content).T / y).T)) +1106 else: +1107 raise ValueError("operands could not be broadcast together") +1108 else: +1109 raise TypeError('Corr / wrong type') +1110 +1111 def __neg__(self): +1112 newcontent = [None if _check_for_none(self, item) else -1. * item for item in self.content] +1113 return Corr(newcontent, prange=self.prange) +1114 +1115 def __sub__(self, y): +1116 return self + (-y) +1117 +1118 def __pow__(self, y): +1119 if isinstance(y, (Obs, int, float, CObs)): +1120 newcontent = [None if _check_for_none(self, item) else item**y for item in self.content] +1121 return Corr(newcontent, prange=self.prange) +1122 else: +1123 raise TypeError('Type of exponent not supported') +1124 +1125 def __abs__(self): +1126 newcontent = [None if _check_for_none(self, item) else np.abs(item) for item in self.content] +1127 return Corr(newcontent, prange=self.prange) +1128 +1129 # The numpy functions: +1130 def sqrt(self): +1131 return self ** 0.5 +1132 +1133 def log(self): +1134 newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content] +1135 return Corr(newcontent, prange=self.prange) +1136 +1137 def exp(self): +1138 newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content] +1139 return Corr(newcontent, prange=self.prange) +1140 +1141 def _apply_func_to_corr(self, func): +1142 newcontent = [None if _check_for_none(self, item) else func(item) for item in self.content] +1143 for t in range(self.T): +1144 if _check_for_none(self, newcontent[t]): +1145 continue +1146 tmp_sum = np.sum(newcontent[t]) +1147 if hasattr(tmp_sum, "value"): +1148 if np.isnan(tmp_sum.value): +1149 newcontent[t] = None +1150 if all([item is None for item in newcontent]): +1151 raise Exception('Operation returns undefined correlator') +1152 return Corr(newcontent) +1153 +1154 def sin(self): +1155 return self._apply_func_to_corr(np.sin) +1156 +1157 def cos(self): +1158 return self._apply_func_to_corr(np.cos) +1159 +1160 def tan(self): +1161 return self._apply_func_to_corr(np.tan) +1162 +1163 def sinh(self): +1164 return self._apply_func_to_corr(np.sinh) +1165 +1166 def cosh(self): +1167 return self._apply_func_to_corr(np.cosh) +1168 +1169 def tanh(self): +1170 return self._apply_func_to_corr(np.tanh) +1171 +1172 def arcsin(self): +1173 return self._apply_func_to_corr(np.arcsin) +1174 +1175 def arccos(self): +1176 return self._apply_func_to_corr(np.arccos) +1177 +1178 def arctan(self): +1179 return self._apply_func_to_corr(np.arctan) +1180 +1181 def arcsinh(self): +1182 return self._apply_func_to_corr(np.arcsinh) +1183 +1184 def arccosh(self): +1185 return self._apply_func_to_corr(np.arccosh) +1186 +1187 def arctanh(self): +1188 return self._apply_func_to_corr(np.arctanh) +1189 +1190 # Right hand side operations (require tweak in main module to work) +1191 def __radd__(self, y): +1192 return self + y +1193 +1194 def __rsub__(self, y): +1195 return -self + y +1196 +1197 def __rmul__(self, y): +1198 return self * y +1199 +1200 def __rtruediv__(self, y): +1201 return (self / y) ** (-1) +1202 +1203 @property +1204 def real(self): +1205 def return_real(obs_OR_cobs): +1206 if isinstance(obs_OR_cobs.flatten()[0], CObs): +1207 return np.vectorize(lambda x: x.real)(obs_OR_cobs) +1208 else: +1209 return obs_OR_cobs 1210 -1211 @property -1212 def imag(self): -1213 def return_imag(obs_OR_cobs): -1214 if isinstance(obs_OR_cobs.flatten()[0], CObs): -1215 return np.vectorize(lambda x: x.imag)(obs_OR_cobs) -1216 else: -1217 return obs_OR_cobs * 0 # So it stays the right type -1218 -1219 return self._apply_func_to_corr(return_imag) +1211 return self._apply_func_to_corr(return_real) +1212 +1213 @property +1214 def imag(self): +1215 def return_imag(obs_OR_cobs): +1216 if isinstance(obs_OR_cobs.flatten()[0], CObs): +1217 return np.vectorize(lambda x: x.imag)(obs_OR_cobs) +1218 else: +1219 return obs_OR_cobs * 0 # So it stays the right type 1220 -1221 def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None): -1222 r''' Project large correlation matrix to lowest states -1223 -1224 This method can be used to reduce the size of an (N x N) correlation matrix -1225 to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise -1226 is still small. -1227 -1228 Parameters -1229 ---------- -1230 Ntrunc: int -1231 Rank of the target matrix. -1232 tproj: int -1233 Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method. -1234 The default value is 3. -1235 t0proj: int -1236 Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly -1237 discouraged for O(a) improved theories, since the correctness of the procedure -1238 cannot be granted in this case. The default value is 2. -1239 basematrix : Corr -1240 Correlation matrix that is used to determine the eigenvectors of the -1241 lowest states based on a GEVP. basematrix is taken to be the Corr itself if -1242 is is not specified. -1243 -1244 Notes -1245 ----- -1246 We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving -1247 the GEVP $$C(t) v_n(t, t_0) = \lambda_n(t, t_0) C(t_0) v_n(t, t_0)$$ where $t \equiv t_\mathrm{proj}$ -1248 and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the -1249 resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via -1250 $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large -1251 correlation matrix and to remove some noise that is added by irrelevant operators. -1252 This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated -1253 bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$. -1254 ''' -1255 -1256 if self.N == 1: -1257 raise Exception('Method cannot be applied to one-dimensional correlators.') -1258 if basematrix is None: -1259 basematrix = self -1260 if Ntrunc >= basematrix.N: -1261 raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N)) -1262 if basematrix.N != self.N: -1263 raise Exception('basematrix and targetmatrix have to be of the same size.') -1264 -1265 evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc] +1221 return self._apply_func_to_corr(return_imag) +1222 +1223 def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None): +1224 r''' Project large correlation matrix to lowest states +1225 +1226 This method can be used to reduce the size of an (N x N) correlation matrix +1227 to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise +1228 is still small. +1229 +1230 Parameters +1231 ---------- +1232 Ntrunc: int +1233 Rank of the target matrix. +1234 tproj: int +1235 Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method. +1236 The default value is 3. +1237 t0proj: int +1238 Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly +1239 discouraged for O(a) improved theories, since the correctness of the procedure +1240 cannot be granted in this case. The default value is 2. +1241 basematrix : Corr +1242 Correlation matrix that is used to determine the eigenvectors of the +1243 lowest states based on a GEVP. basematrix is taken to be the Corr itself if +1244 is is not specified. +1245 +1246 Notes +1247 ----- +1248 We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving +1249 the GEVP $$C(t) v_n(t, t_0) = \lambda_n(t, t_0) C(t_0) v_n(t, t_0)$$ where $t \equiv t_\mathrm{proj}$ +1250 and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the +1251 resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via +1252 $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large +1253 correlation matrix and to remove some noise that is added by irrelevant operators. +1254 This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated +1255 bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$. +1256 ''' +1257 +1258 if self.N == 1: +1259 raise Exception('Method cannot be applied to one-dimensional correlators.') +1260 if basematrix is None: +1261 basematrix = self +1262 if Ntrunc >= basematrix.N: +1263 raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N)) +1264 if basematrix.N != self.N: +1265 raise Exception('basematrix and targetmatrix have to be of the same size.') 1266 -1267 tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object) -1268 rmat = [] -1269 for t in range(basematrix.T): -1270 for i in range(Ntrunc): -1271 for j in range(Ntrunc): -1272 tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j] -1273 rmat.append(np.copy(tmpmat)) -1274 -1275 newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)] -1276 return Corr(newcontent) -1277 -1278 -1279def _sort_vectors(vec_set, ts): -1280 """Helper function used to find a set of Eigenvectors consistent over all timeslices""" -1281 reference_sorting = np.array(vec_set[ts]) -1282 N = reference_sorting.shape[0] -1283 sorted_vec_set = [] -1284 for t in range(len(vec_set)): -1285 if vec_set[t] is None: -1286 sorted_vec_set.append(None) -1287 elif not t == ts: -1288 perms = [list(o) for o in permutations([i for i in range(N)], N)] -1289 best_score = 0 -1290 for perm in perms: -1291 current_score = 1 -1292 for k in range(N): -1293 new_sorting = reference_sorting.copy() -1294 new_sorting[perm[k], :] = vec_set[t][k] -1295 current_score *= abs(np.linalg.det(new_sorting)) -1296 if current_score > best_score: -1297 best_score = current_score -1298 best_perm = perm -1299 sorted_vec_set.append([vec_set[t][k] for k in best_perm]) -1300 else: -1301 sorted_vec_set.append(vec_set[t]) -1302 -1303 return sorted_vec_set +1267 evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc] +1268 +1269 tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object) +1270 rmat = [] +1271 for t in range(basematrix.T): +1272 for i in range(Ntrunc): +1273 for j in range(Ntrunc): +1274 tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j] +1275 rmat.append(np.copy(tmpmat)) +1276 +1277 newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)] +1278 return Corr(newcontent) +1279 +1280 +1281def _sort_vectors(vec_set, ts): +1282 """Helper function used to find a set of Eigenvectors consistent over all timeslices""" +1283 reference_sorting = np.array(vec_set[ts]) +1284 N = reference_sorting.shape[0] +1285 sorted_vec_set = [] +1286 for t in range(len(vec_set)): +1287 if vec_set[t] is None: +1288 sorted_vec_set.append(None) +1289 elif not t == ts: +1290 perms = [list(o) for o in permutations([i for i in range(N)], N)] +1291 best_score = 0 +1292 for perm in perms: +1293 current_score = 1 +1294 for k in range(N): +1295 new_sorting = reference_sorting.copy() +1296 new_sorting[perm[k], :] = vec_set[t][k] +1297 current_score *= abs(np.linalg.det(new_sorting)) +1298 if current_score > best_score: +1299 best_score = current_score +1300 best_perm = perm +1301 sorted_vec_set.append([vec_set[t][k] for k in best_perm]) +1302 else: +1303 sorted_vec_set.append(vec_set[t]) 1304 -1305 -1306def _check_for_none(corr, entry): -1307 """Checks if entry for correlator corr is None""" -1308 return len(list(filter(None, np.asarray(entry).flatten()))) < corr.N ** 2 -1309 -1310 -1311def _GEVP_solver(Gt, G0): -1312 """Helper function for solving the GEVP and sorting the eigenvectors. -1313 -1314 The helper function assumes that both provided matrices are symmetric and -1315 only processes the lower triangular part of both matrices. In case the matrices -1316 are not symmetric the upper triangular parts are effectively discarded.""" -1317 return scipy.linalg.eigh(Gt, G0, lower=True)[1].T[::-1] +1305 return sorted_vec_set +1306 +1307 +1308def _check_for_none(corr, entry): +1309 """Checks if entry for correlator corr is None""" +1310 return len(list(filter(None, np.asarray(entry).flatten()))) < corr.N ** 2 +1311 +1312 +1313def _GEVP_solver(Gt, G0): +1314 """Helper function for solving the GEVP and sorting the eigenvectors. +1315 +1316 The helper function assumes that both provided matrices are symmetric and +1317 only processes the lower triangular part of both matrices. In case the matrices +1318 are not symmetric the upper triangular parts are effectively discarded.""" +1319 return scipy.linalg.eigh(Gt, G0, lower=True)[1].T[::-1] @@ -1559,1257 +1561,1259 @@ 24 25 """ 26 - 27 def __init__(self, data_input, padding=[0, 0], prange=None): - 28 """ Initialize a Corr object. - 29 - 30 Parameters - 31 ---------- - 32 data_input : list or array - 33 list of Obs or list of arrays of Obs or array of Corrs - 34 padding : list, optional - 35 List with two entries where the first labels the padding - 36 at the front of the correlator and the second the padding - 37 at the back. - 38 prange : list, optional - 39 List containing the first and last timeslice of the plateau - 40 region indentified for this correlator. - 41 """ - 42 - 43 if isinstance(data_input, np.ndarray): + 27 __slots__ = ["content", "N", "T", "tag", "prange"] + 28 + 29 def __init__(self, data_input, padding=[0, 0], prange=None): + 30 """ Initialize a Corr object. + 31 + 32 Parameters + 33 ---------- + 34 data_input : list or array + 35 list of Obs or list of arrays of Obs or array of Corrs + 36 padding : list, optional + 37 List with two entries where the first labels the padding + 38 at the front of the correlator and the second the padding + 39 at the back. + 40 prange : list, optional + 41 List containing the first and last timeslice of the plateau + 42 region indentified for this correlator. + 43 """ 44 - 45 # This only works, if the array fulfills the conditions below - 46 if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]: - 47 raise Exception("Incompatible array shape") - 48 if not all([isinstance(item, Corr) for item in data_input.flatten()]): - 49 raise Exception("If the input is an array, its elements must be of type pe.Corr") - 50 if not all([item.N == 1 for item in data_input.flatten()]): - 51 raise Exception("Can only construct matrix correlator from single valued correlators") - 52 if not len(set([item.T for item in data_input.flatten()])) == 1: - 53 raise Exception("All input Correlators must be defined over the same timeslices.") - 54 - 55 T = data_input[0, 0].T - 56 N = data_input.shape[0] - 57 input_as_list = [] - 58 for t in range(T): - 59 if any([(item.content[t] is None) for item in data_input.flatten()]): - 60 if not all([(item.content[t] is None) for item in data_input.flatten()]): - 61 warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning) - 62 input_as_list.append(None) - 63 else: - 64 array_at_timeslace = np.empty([N, N], dtype="object") - 65 for i in range(N): - 66 for j in range(N): - 67 array_at_timeslace[i, j] = data_input[i, j][t] - 68 input_as_list.append(array_at_timeslace) - 69 data_input = input_as_list - 70 - 71 if isinstance(data_input, list): + 45 if isinstance(data_input, np.ndarray): + 46 + 47 # This only works, if the array fulfills the conditions below + 48 if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]: + 49 raise Exception("Incompatible array shape") + 50 if not all([isinstance(item, Corr) for item in data_input.flatten()]): + 51 raise Exception("If the input is an array, its elements must be of type pe.Corr") + 52 if not all([item.N == 1 for item in data_input.flatten()]): + 53 raise Exception("Can only construct matrix correlator from single valued correlators") + 54 if not len(set([item.T for item in data_input.flatten()])) == 1: + 55 raise Exception("All input Correlators must be defined over the same timeslices.") + 56 + 57 T = data_input[0, 0].T + 58 N = data_input.shape[0] + 59 input_as_list = [] + 60 for t in range(T): + 61 if any([(item.content[t] is None) for item in data_input.flatten()]): + 62 if not all([(item.content[t] is None) for item in data_input.flatten()]): + 63 warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning) + 64 input_as_list.append(None) + 65 else: + 66 array_at_timeslace = np.empty([N, N], dtype="object") + 67 for i in range(N): + 68 for j in range(N): + 69 array_at_timeslace[i, j] = data_input[i, j][t] + 70 input_as_list.append(array_at_timeslace) + 71 data_input = input_as_list 72 - 73 if all([isinstance(item, (Obs, CObs)) or item is None for item in data_input]): - 74 _assert_equal_properties([o for o in data_input if o is not None]) - 75 self.content = [np.asarray([item]) if item is not None else None for item in data_input] - 76 self.N = 1 - 77 - 78 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]): - 79 self.content = data_input - 80 noNull = [a for a in self.content if not (a is None)] # To check if the matrices are correct for all undefined elements - 81 self.N = noNull[0].shape[0] - 82 if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]: - 83 raise Exception("Smearing matrices are not NxN") - 84 if (not all([item.shape == noNull[0].shape for item in noNull])): - 85 raise Exception("Items in data_input are not of identical shape." + str(noNull)) - 86 else: - 87 raise Exception("data_input contains item of wrong type") - 88 else: - 89 raise Exception("Data input was not given as list or correct array") - 90 - 91 self.tag = None + 73 if isinstance(data_input, list): + 74 + 75 if all([isinstance(item, (Obs, CObs)) or item is None for item in data_input]): + 76 _assert_equal_properties([o for o in data_input if o is not None]) + 77 self.content = [np.asarray([item]) if item is not None else None for item in data_input] + 78 self.N = 1 + 79 + 80 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]): + 81 self.content = data_input + 82 noNull = [a for a in self.content if not (a is None)] # To check if the matrices are correct for all undefined elements + 83 self.N = noNull[0].shape[0] + 84 if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]: + 85 raise Exception("Smearing matrices are not NxN") + 86 if (not all([item.shape == noNull[0].shape for item in noNull])): + 87 raise Exception("Items in data_input are not of identical shape." + str(noNull)) + 88 else: + 89 raise Exception("data_input contains item of wrong type") + 90 else: + 91 raise Exception("Data input was not given as list or correct array") 92 - 93 # An undefined timeslice is represented by the None object - 94 self.content = [None] * padding[0] + self.content + [None] * padding[1] - 95 self.T = len(self.content) - 96 self.prange = prange - 97 - 98 def __getitem__(self, idx): - 99 """Return the content of timeslice idx""" - 100 if self.content[idx] is None: - 101 return None - 102 elif len(self.content[idx]) == 1: - 103 return self.content[idx][0] - 104 else: - 105 return self.content[idx] - 106 - 107 @property - 108 def reweighted(self): - 109 bool_array = np.array([list(map(lambda x: x.reweighted, o)) for o in [x for x in self.content if x is not None]]) - 110 if np.all(bool_array == 1): - 111 return True - 112 elif np.all(bool_array == 0): - 113 return False - 114 else: - 115 raise Exception("Reweighting status of correlator corrupted.") - 116 - 117 def gamma_method(self, **kwargs): - 118 """Apply the gamma method to the content of the Corr.""" - 119 for item in self.content: - 120 if not (item is None): - 121 if self.N == 1: - 122 item[0].gamma_method(**kwargs) - 123 else: - 124 for i in range(self.N): - 125 for j in range(self.N): - 126 item[i, j].gamma_method(**kwargs) - 127 - 128 gm = gamma_method + 93 self.tag = None + 94 + 95 # An undefined timeslice is represented by the None object + 96 self.content = [None] * padding[0] + self.content + [None] * padding[1] + 97 self.T = len(self.content) + 98 self.prange = prange + 99 + 100 def __getitem__(self, idx): + 101 """Return the content of timeslice idx""" + 102 if self.content[idx] is None: + 103 return None + 104 elif len(self.content[idx]) == 1: + 105 return self.content[idx][0] + 106 else: + 107 return self.content[idx] + 108 + 109 @property + 110 def reweighted(self): + 111 bool_array = np.array([list(map(lambda x: x.reweighted, o)) for o in [x for x in self.content if x is not None]]) + 112 if np.all(bool_array == 1): + 113 return True + 114 elif np.all(bool_array == 0): + 115 return False + 116 else: + 117 raise Exception("Reweighting status of correlator corrupted.") + 118 + 119 def gamma_method(self, **kwargs): + 120 """Apply the gamma method to the content of the Corr.""" + 121 for item in self.content: + 122 if not (item is None): + 123 if self.N == 1: + 124 item[0].gamma_method(**kwargs) + 125 else: + 126 for i in range(self.N): + 127 for j in range(self.N): + 128 item[i, j].gamma_method(**kwargs) 129 - 130 def projected(self, vector_l=None, vector_r=None, normalize=False): - 131 """We need to project the Correlator with a Vector to get a single value at each timeslice. - 132 - 133 The method can use one or two vectors. - 134 If two are specified it returns v1@G@v2 (the order might be very important.) - 135 By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to - 136 """ - 137 if self.N == 1: - 138 raise Exception("Trying to project a Corr, that already has N=1.") - 139 - 140 if vector_l is None: - 141 vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.]) - 142 elif (vector_r is None): - 143 vector_r = vector_l - 144 if isinstance(vector_l, list) and not isinstance(vector_r, list): - 145 if len(vector_l) != self.T: - 146 raise Exception("Length of vector list must be equal to T") - 147 vector_r = [vector_r] * self.T - 148 if isinstance(vector_r, list) and not isinstance(vector_l, list): - 149 if len(vector_r) != self.T: - 150 raise Exception("Length of vector list must be equal to T") - 151 vector_l = [vector_l] * self.T - 152 - 153 if not isinstance(vector_l, list): - 154 if not vector_l.shape == vector_r.shape == (self.N,): - 155 raise Exception("Vectors are of wrong shape!") - 156 if normalize: - 157 vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r) - 158 newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content] - 159 - 160 else: - 161 # There are no checks here yet. There are so many possible scenarios, where this can go wrong. - 162 if normalize: - 163 for t in range(self.T): - 164 vector_l[t], vector_r[t] = vector_l[t] / np.sqrt((vector_l[t] @ vector_l[t])), vector_r[t] / np.sqrt(vector_r[t] @ vector_r[t]) - 165 - 166 newcontent = [None if (_check_for_none(self, self.content[t]) or vector_l[t] is None or vector_r[t] is None) else np.asarray([vector_l[t].T @ self.content[t] @ vector_r[t]]) for t in range(self.T)] - 167 return Corr(newcontent) - 168 - 169 def item(self, i, j): - 170 """Picks the element [i,j] from every matrix and returns a correlator containing one Obs per timeslice. - 171 - 172 Parameters - 173 ---------- - 174 i : int - 175 First index to be picked. - 176 j : int - 177 Second index to be picked. - 178 """ - 179 if self.N == 1: - 180 raise Exception("Trying to pick item from projected Corr") - 181 newcontent = [None if (item is None) else item[i, j] for item in self.content] - 182 return Corr(newcontent) - 183 - 184 def plottable(self): - 185 """Outputs the correlator in a plotable format. - 186 - 187 Outputs three lists containing the timeslice index, the value on each - 188 timeslice and the error on each timeslice. - 189 """ - 190 if self.N != 1: - 191 raise Exception("Can only make Corr[N=1] plottable") - 192 x_list = [x for x in range(self.T) if not self.content[x] is None] - 193 y_list = [y[0].value for y in self.content if y is not None] - 194 y_err_list = [y[0].dvalue for y in self.content if y is not None] - 195 - 196 return x_list, y_list, y_err_list + 130 gm = gamma_method + 131 + 132 def projected(self, vector_l=None, vector_r=None, normalize=False): + 133 """We need to project the Correlator with a Vector to get a single value at each timeslice. + 134 + 135 The method can use one or two vectors. + 136 If two are specified it returns v1@G@v2 (the order might be very important.) + 137 By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to + 138 """ + 139 if self.N == 1: + 140 raise Exception("Trying to project a Corr, that already has N=1.") + 141 + 142 if vector_l is None: + 143 vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.]) + 144 elif (vector_r is None): + 145 vector_r = vector_l + 146 if isinstance(vector_l, list) and not isinstance(vector_r, list): + 147 if len(vector_l) != self.T: + 148 raise Exception("Length of vector list must be equal to T") + 149 vector_r = [vector_r] * self.T + 150 if isinstance(vector_r, list) and not isinstance(vector_l, list): + 151 if len(vector_r) != self.T: + 152 raise Exception("Length of vector list must be equal to T") + 153 vector_l = [vector_l] * self.T + 154 + 155 if not isinstance(vector_l, list): + 156 if not vector_l.shape == vector_r.shape == (self.N,): + 157 raise Exception("Vectors are of wrong shape!") + 158 if normalize: + 159 vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r) + 160 newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content] + 161 + 162 else: + 163 # There are no checks here yet. There are so many possible scenarios, where this can go wrong. + 164 if normalize: + 165 for t in range(self.T): + 166 vector_l[t], vector_r[t] = vector_l[t] / np.sqrt((vector_l[t] @ vector_l[t])), vector_r[t] / np.sqrt(vector_r[t] @ vector_r[t]) + 167 + 168 newcontent = [None if (_check_for_none(self, self.content[t]) or vector_l[t] is None or vector_r[t] is None) else np.asarray([vector_l[t].T @ self.content[t] @ vector_r[t]]) for t in range(self.T)] + 169 return Corr(newcontent) + 170 + 171 def item(self, i, j): + 172 """Picks the element [i,j] from every matrix and returns a correlator containing one Obs per timeslice. + 173 + 174 Parameters + 175 ---------- + 176 i : int + 177 First index to be picked. + 178 j : int + 179 Second index to be picked. + 180 """ + 181 if self.N == 1: + 182 raise Exception("Trying to pick item from projected Corr") + 183 newcontent = [None if (item is None) else item[i, j] for item in self.content] + 184 return Corr(newcontent) + 185 + 186 def plottable(self): + 187 """Outputs the correlator in a plotable format. + 188 + 189 Outputs three lists containing the timeslice index, the value on each + 190 timeslice and the error on each timeslice. + 191 """ + 192 if self.N != 1: + 193 raise Exception("Can only make Corr[N=1] plottable") + 194 x_list = [x for x in range(self.T) if not self.content[x] is None] + 195 y_list = [y[0].value for y in self.content if y is not None] + 196 y_err_list = [y[0].dvalue for y in self.content if y is not None] 197 - 198 def symmetric(self): - 199 """ Symmetrize the correlator around x0=0.""" - 200 if self.N != 1: - 201 raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.') - 202 if self.T % 2 != 0: - 203 raise Exception("Can not symmetrize odd T") - 204 - 205 if self.content[0] is not None: - 206 if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0: - 207 warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning) - 208 - 209 newcontent = [self.content[0]] - 210 for t in range(1, self.T): - 211 if (self.content[t] is None) or (self.content[self.T - t] is None): - 212 newcontent.append(None) - 213 else: - 214 newcontent.append(0.5 * (self.content[t] + self.content[self.T - t])) - 215 if (all([x is None for x in newcontent])): - 216 raise Exception("Corr could not be symmetrized: No redundant values") - 217 return Corr(newcontent, prange=self.prange) - 218 - 219 def anti_symmetric(self): - 220 """Anti-symmetrize the correlator around x0=0.""" - 221 if self.N != 1: - 222 raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.') - 223 if self.T % 2 != 0: - 224 raise Exception("Can not symmetrize odd T") - 225 - 226 test = 1 * self - 227 test.gamma_method() - 228 if not all([o.is_zero_within_error(3) for o in test.content[0]]): - 229 warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning) - 230 - 231 newcontent = [self.content[0]] - 232 for t in range(1, self.T): - 233 if (self.content[t] is None) or (self.content[self.T - t] is None): - 234 newcontent.append(None) - 235 else: - 236 newcontent.append(0.5 * (self.content[t] - self.content[self.T - t])) - 237 if (all([x is None for x in newcontent])): - 238 raise Exception("Corr could not be symmetrized: No redundant values") - 239 return Corr(newcontent, prange=self.prange) - 240 - 241 def is_matrix_symmetric(self): - 242 """Checks whether a correlator matrices is symmetric on every timeslice.""" - 243 if self.N == 1: - 244 raise Exception("Only works for correlator matrices.") - 245 for t in range(self.T): - 246 if self[t] is None: - 247 continue - 248 for i in range(self.N): - 249 for j in range(i + 1, self.N): - 250 if self[t][i, j] is self[t][j, i]: - 251 continue - 252 if hash(self[t][i, j]) != hash(self[t][j, i]): - 253 return False - 254 return True - 255 - 256 def matrix_symmetric(self): - 257 """Symmetrizes the correlator matrices on every timeslice.""" - 258 if self.N == 1: - 259 raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.") - 260 if self.is_matrix_symmetric(): - 261 return 1.0 * self - 262 else: - 263 transposed = [None if _check_for_none(self, G) else G.T for G in self.content] - 264 return 0.5 * (Corr(transposed) + self) - 265 - 266 def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs): - 267 r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors. - 268 - 269 The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the - 270 largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing - 271 ```python - 272 C.GEVP(t0=2)[0] # Ground state vector(s) - 273 C.GEVP(t0=2)[:3] # Vectors for the lowest three states - 274 ``` - 275 - 276 Parameters - 277 ---------- - 278 t0 : int - 279 The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$ - 280 ts : int - 281 fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None. - 282 If sort="Eigenvector" it gives a reference point for the sorting method. - 283 sort : string - 284 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. - 285 - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice. - 286 - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state. - 287 The reference state is identified by its eigenvalue at $t=t_s$. - 288 - 289 Other Parameters - 290 ---------------- - 291 state : int - 292 Returns only the vector(s) for a specified state. The lowest state is zero. - 293 ''' - 294 - 295 if self.N == 1: - 296 raise Exception("GEVP methods only works on correlator matrices and not single correlators.") - 297 if ts is not None: - 298 if (ts <= t0): - 299 raise Exception("ts has to be larger than t0.") - 300 - 301 if "sorted_list" in kwargs: - 302 warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning) - 303 sort = kwargs.get("sorted_list") - 304 - 305 if self.is_matrix_symmetric(): - 306 symmetric_corr = self - 307 else: - 308 symmetric_corr = self.matrix_symmetric() - 309 - 310 G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0]) - 311 np.linalg.cholesky(G0) # Check if matrix G0 is positive-semidefinite. - 312 - 313 if sort is None: - 314 if (ts is None): - 315 raise Exception("ts is required if sort=None.") - 316 if (self.content[t0] is None) or (self.content[ts] is None): - 317 raise Exception("Corr not defined at t0/ts.") - 318 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts]) - 319 reordered_vecs = _GEVP_solver(Gt, G0) - 320 - 321 elif sort in ["Eigenvalue", "Eigenvector"]: - 322 if sort == "Eigenvalue" and ts is not None: - 323 warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning) - 324 all_vecs = [None] * (t0 + 1) - 325 for t in range(t0 + 1, self.T): - 326 try: - 327 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t]) - 328 all_vecs.append(_GEVP_solver(Gt, G0)) - 329 except Exception: - 330 all_vecs.append(None) - 331 if sort == "Eigenvector": - 332 if ts is None: - 333 raise Exception("ts is required for the Eigenvector sorting method.") - 334 all_vecs = _sort_vectors(all_vecs, ts) - 335 - 336 reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)] - 337 else: - 338 raise Exception("Unkown value for 'sort'.") - 339 - 340 if "state" in kwargs: - 341 return reordered_vecs[kwargs.get("state")] - 342 else: - 343 return reordered_vecs - 344 - 345 def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"): - 346 """Determines the eigenvalue of the GEVP by solving and projecting the correlator - 347 - 348 Parameters - 349 ---------- - 350 state : int - 351 The state one is interested in ordered by energy. The lowest state is zero. - 352 - 353 All other parameters are identical to the ones of Corr.GEVP. - 354 """ - 355 vec = self.GEVP(t0, ts=ts, sort=sort)[state] - 356 return self.projected(vec) - 357 - 358 def Hankel(self, N, periodic=False): - 359 """Constructs an NxN Hankel matrix - 360 - 361 C(t) c(t+1) ... c(t+n-1) - 362 C(t+1) c(t+2) ... c(t+n) - 363 ................. - 364 C(t+(n-1)) c(t+n) ... c(t+2(n-1)) - 365 - 366 Parameters - 367 ---------- - 368 N : int - 369 Dimension of the Hankel matrix - 370 periodic : bool, optional - 371 determines whether the matrix is extended periodically - 372 """ - 373 - 374 if self.N != 1: - 375 raise Exception("Multi-operator Prony not implemented!") - 376 - 377 array = np.empty([N, N], dtype="object") - 378 new_content = [] - 379 for t in range(self.T): - 380 new_content.append(array.copy()) - 381 - 382 def wrap(i): - 383 while i >= self.T: - 384 i -= self.T - 385 return i - 386 - 387 for t in range(self.T): - 388 for i in range(N): - 389 for j in range(N): - 390 if periodic: - 391 new_content[t][i, j] = self.content[wrap(t + i + j)][0] - 392 elif (t + i + j) >= self.T: - 393 new_content[t] = None - 394 else: - 395 new_content[t][i, j] = self.content[t + i + j][0] - 396 - 397 return Corr(new_content) + 198 return x_list, y_list, y_err_list + 199 + 200 def symmetric(self): + 201 """ Symmetrize the correlator around x0=0.""" + 202 if self.N != 1: + 203 raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.') + 204 if self.T % 2 != 0: + 205 raise Exception("Can not symmetrize odd T") + 206 + 207 if self.content[0] is not None: + 208 if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0: + 209 warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning) + 210 + 211 newcontent = [self.content[0]] + 212 for t in range(1, self.T): + 213 if (self.content[t] is None) or (self.content[self.T - t] is None): + 214 newcontent.append(None) + 215 else: + 216 newcontent.append(0.5 * (self.content[t] + self.content[self.T - t])) + 217 if (all([x is None for x in newcontent])): + 218 raise Exception("Corr could not be symmetrized: No redundant values") + 219 return Corr(newcontent, prange=self.prange) + 220 + 221 def anti_symmetric(self): + 222 """Anti-symmetrize the correlator around x0=0.""" + 223 if self.N != 1: + 224 raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.') + 225 if self.T % 2 != 0: + 226 raise Exception("Can not symmetrize odd T") + 227 + 228 test = 1 * self + 229 test.gamma_method() + 230 if not all([o.is_zero_within_error(3) for o in test.content[0]]): + 231 warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning) + 232 + 233 newcontent = [self.content[0]] + 234 for t in range(1, self.T): + 235 if (self.content[t] is None) or (self.content[self.T - t] is None): + 236 newcontent.append(None) + 237 else: + 238 newcontent.append(0.5 * (self.content[t] - self.content[self.T - t])) + 239 if (all([x is None for x in newcontent])): + 240 raise Exception("Corr could not be symmetrized: No redundant values") + 241 return Corr(newcontent, prange=self.prange) + 242 + 243 def is_matrix_symmetric(self): + 244 """Checks whether a correlator matrices is symmetric on every timeslice.""" + 245 if self.N == 1: + 246 raise Exception("Only works for correlator matrices.") + 247 for t in range(self.T): + 248 if self[t] is None: + 249 continue + 250 for i in range(self.N): + 251 for j in range(i + 1, self.N): + 252 if self[t][i, j] is self[t][j, i]: + 253 continue + 254 if hash(self[t][i, j]) != hash(self[t][j, i]): + 255 return False + 256 return True + 257 + 258 def matrix_symmetric(self): + 259 """Symmetrizes the correlator matrices on every timeslice.""" + 260 if self.N == 1: + 261 raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.") + 262 if self.is_matrix_symmetric(): + 263 return 1.0 * self + 264 else: + 265 transposed = [None if _check_for_none(self, G) else G.T for G in self.content] + 266 return 0.5 * (Corr(transposed) + self) + 267 + 268 def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs): + 269 r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors. + 270 + 271 The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the + 272 largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing + 273 ```python + 274 C.GEVP(t0=2)[0] # Ground state vector(s) + 275 C.GEVP(t0=2)[:3] # Vectors for the lowest three states + 276 ``` + 277 + 278 Parameters + 279 ---------- + 280 t0 : int + 281 The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$ + 282 ts : int + 283 fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None. + 284 If sort="Eigenvector" it gives a reference point for the sorting method. + 285 sort : string + 286 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. + 287 - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice. + 288 - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state. + 289 The reference state is identified by its eigenvalue at $t=t_s$. + 290 + 291 Other Parameters + 292 ---------------- + 293 state : int + 294 Returns only the vector(s) for a specified state. The lowest state is zero. + 295 ''' + 296 + 297 if self.N == 1: + 298 raise Exception("GEVP methods only works on correlator matrices and not single correlators.") + 299 if ts is not None: + 300 if (ts <= t0): + 301 raise Exception("ts has to be larger than t0.") + 302 + 303 if "sorted_list" in kwargs: + 304 warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning) + 305 sort = kwargs.get("sorted_list") + 306 + 307 if self.is_matrix_symmetric(): + 308 symmetric_corr = self + 309 else: + 310 symmetric_corr = self.matrix_symmetric() + 311 + 312 G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0]) + 313 np.linalg.cholesky(G0) # Check if matrix G0 is positive-semidefinite. + 314 + 315 if sort is None: + 316 if (ts is None): + 317 raise Exception("ts is required if sort=None.") + 318 if (self.content[t0] is None) or (self.content[ts] is None): + 319 raise Exception("Corr not defined at t0/ts.") + 320 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts]) + 321 reordered_vecs = _GEVP_solver(Gt, G0) + 322 + 323 elif sort in ["Eigenvalue", "Eigenvector"]: + 324 if sort == "Eigenvalue" and ts is not None: + 325 warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning) + 326 all_vecs = [None] * (t0 + 1) + 327 for t in range(t0 + 1, self.T): + 328 try: + 329 Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t]) + 330 all_vecs.append(_GEVP_solver(Gt, G0)) + 331 except Exception: + 332 all_vecs.append(None) + 333 if sort == "Eigenvector": + 334 if ts is None: + 335 raise Exception("ts is required for the Eigenvector sorting method.") + 336 all_vecs = _sort_vectors(all_vecs, ts) + 337 + 338 reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)] + 339 else: + 340 raise Exception("Unkown value for 'sort'.") + 341 + 342 if "state" in kwargs: + 343 return reordered_vecs[kwargs.get("state")] + 344 else: + 345 return reordered_vecs + 346 + 347 def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"): + 348 """Determines the eigenvalue of the GEVP by solving and projecting the correlator + 349 + 350 Parameters + 351 ---------- + 352 state : int + 353 The state one is interested in ordered by energy. The lowest state is zero. + 354 + 355 All other parameters are identical to the ones of Corr.GEVP. + 356 """ + 357 vec = self.GEVP(t0, ts=ts, sort=sort)[state] + 358 return self.projected(vec) + 359 + 360 def Hankel(self, N, periodic=False): + 361 """Constructs an NxN Hankel matrix + 362 + 363 C(t) c(t+1) ... c(t+n-1) + 364 C(t+1) c(t+2) ... c(t+n) + 365 ................. + 366 C(t+(n-1)) c(t+n) ... c(t+2(n-1)) + 367 + 368 Parameters + 369 ---------- + 370 N : int + 371 Dimension of the Hankel matrix + 372 periodic : bool, optional + 373 determines whether the matrix is extended periodically + 374 """ + 375 + 376 if self.N != 1: + 377 raise Exception("Multi-operator Prony not implemented!") + 378 + 379 array = np.empty([N, N], dtype="object") + 380 new_content = [] + 381 for t in range(self.T): + 382 new_content.append(array.copy()) + 383 + 384 def wrap(i): + 385 while i >= self.T: + 386 i -= self.T + 387 return i + 388 + 389 for t in range(self.T): + 390 for i in range(N): + 391 for j in range(N): + 392 if periodic: + 393 new_content[t][i, j] = self.content[wrap(t + i + j)][0] + 394 elif (t + i + j) >= self.T: + 395 new_content[t] = None + 396 else: + 397 new_content[t][i, j] = self.content[t + i + j][0] 398 - 399 def roll(self, dt): - 400 """Periodically shift the correlator by dt timeslices - 401 - 402 Parameters - 403 ---------- - 404 dt : int - 405 number of timeslices - 406 """ - 407 return Corr(list(np.roll(np.array(self.content, dtype=object), dt))) - 408 - 409 def reverse(self): - 410 """Reverse the time ordering of the Corr""" - 411 return Corr(self.content[:: -1]) - 412 - 413 def thin(self, spacing=2, offset=0): - 414 """Thin out a correlator to suppress correlations - 415 - 416 Parameters - 417 ---------- - 418 spacing : int - 419 Keep only every 'spacing'th entry of the correlator - 420 offset : int - 421 Offset the equal spacing - 422 """ - 423 new_content = [] - 424 for t in range(self.T): - 425 if (offset + t) % spacing != 0: - 426 new_content.append(None) - 427 else: - 428 new_content.append(self.content[t]) - 429 return Corr(new_content) - 430 - 431 def correlate(self, partner): - 432 """Correlate the correlator with another correlator or Obs - 433 - 434 Parameters - 435 ---------- - 436 partner : Obs or Corr - 437 partner to correlate the correlator with. - 438 Can either be an Obs which is correlated with all entries of the - 439 correlator or a Corr of same length. - 440 """ - 441 if self.N != 1: - 442 raise Exception("Only one-dimensional correlators can be safely correlated.") - 443 new_content = [] - 444 for x0, t_slice in enumerate(self.content): - 445 if _check_for_none(self, t_slice): - 446 new_content.append(None) - 447 else: - 448 if isinstance(partner, Corr): - 449 if _check_for_none(partner, partner.content[x0]): - 450 new_content.append(None) - 451 else: - 452 new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice])) - 453 elif isinstance(partner, Obs): # Should this include CObs? - 454 new_content.append(np.array([correlate(o, partner) for o in t_slice])) - 455 else: - 456 raise Exception("Can only correlate with an Obs or a Corr.") - 457 - 458 return Corr(new_content) + 399 return Corr(new_content) + 400 + 401 def roll(self, dt): + 402 """Periodically shift the correlator by dt timeslices + 403 + 404 Parameters + 405 ---------- + 406 dt : int + 407 number of timeslices + 408 """ + 409 return Corr(list(np.roll(np.array(self.content, dtype=object), dt))) + 410 + 411 def reverse(self): + 412 """Reverse the time ordering of the Corr""" + 413 return Corr(self.content[:: -1]) + 414 + 415 def thin(self, spacing=2, offset=0): + 416 """Thin out a correlator to suppress correlations + 417 + 418 Parameters + 419 ---------- + 420 spacing : int + 421 Keep only every 'spacing'th entry of the correlator + 422 offset : int + 423 Offset the equal spacing + 424 """ + 425 new_content = [] + 426 for t in range(self.T): + 427 if (offset + t) % spacing != 0: + 428 new_content.append(None) + 429 else: + 430 new_content.append(self.content[t]) + 431 return Corr(new_content) + 432 + 433 def correlate(self, partner): + 434 """Correlate the correlator with another correlator or Obs + 435 + 436 Parameters + 437 ---------- + 438 partner : Obs or Corr + 439 partner to correlate the correlator with. + 440 Can either be an Obs which is correlated with all entries of the + 441 correlator or a Corr of same length. + 442 """ + 443 if self.N != 1: + 444 raise Exception("Only one-dimensional correlators can be safely correlated.") + 445 new_content = [] + 446 for x0, t_slice in enumerate(self.content): + 447 if _check_for_none(self, t_slice): + 448 new_content.append(None) + 449 else: + 450 if isinstance(partner, Corr): + 451 if _check_for_none(partner, partner.content[x0]): + 452 new_content.append(None) + 453 else: + 454 new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice])) + 455 elif isinstance(partner, Obs): # Should this include CObs? + 456 new_content.append(np.array([correlate(o, partner) for o in t_slice])) + 457 else: + 458 raise Exception("Can only correlate with an Obs or a Corr.") 459 - 460 def reweight(self, weight, **kwargs): - 461 """Reweight the correlator. - 462 - 463 Parameters - 464 ---------- - 465 weight : Obs - 466 Reweighting factor. An Observable that has to be defined on a superset of the - 467 configurations in obs[i].idl for all i. - 468 all_configs : bool - 469 if True, the reweighted observables are normalized by the average of - 470 the reweighting factor on all configurations in weight.idl and not - 471 on the configurations in obs[i].idl. - 472 """ - 473 if self.N != 1: - 474 raise Exception("Reweighting only implemented for one-dimensional correlators.") - 475 new_content = [] - 476 for t_slice in self.content: - 477 if _check_for_none(self, t_slice): - 478 new_content.append(None) - 479 else: - 480 new_content.append(np.array(reweight(weight, t_slice, **kwargs))) - 481 return Corr(new_content) - 482 - 483 def T_symmetry(self, partner, parity=+1): - 484 """Return the time symmetry average of the correlator and its partner - 485 - 486 Parameters - 487 ---------- - 488 partner : Corr - 489 Time symmetry partner of the Corr - 490 partity : int - 491 Parity quantum number of the correlator, can be +1 or -1 - 492 """ - 493 if self.N != 1: - 494 raise Exception("T_symmetry only implemented for one-dimensional correlators.") - 495 if not isinstance(partner, Corr): - 496 raise Exception("T partner has to be a Corr object.") - 497 if parity not in [+1, -1]: - 498 raise Exception("Parity has to be +1 or -1.") - 499 T_partner = parity * partner.reverse() - 500 - 501 t_slices = [] - 502 test = (self - T_partner) - 503 test.gamma_method() - 504 for x0, t_slice in enumerate(test.content): - 505 if t_slice is not None: - 506 if not t_slice[0].is_zero_within_error(5): - 507 t_slices.append(x0) - 508 if t_slices: - 509 warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning) - 510 - 511 return (self + T_partner) / 2 + 460 return Corr(new_content) + 461 + 462 def reweight(self, weight, **kwargs): + 463 """Reweight the correlator. + 464 + 465 Parameters + 466 ---------- + 467 weight : Obs + 468 Reweighting factor. An Observable that has to be defined on a superset of the + 469 configurations in obs[i].idl for all i. + 470 all_configs : bool + 471 if True, the reweighted observables are normalized by the average of + 472 the reweighting factor on all configurations in weight.idl and not + 473 on the configurations in obs[i].idl. + 474 """ + 475 if self.N != 1: + 476 raise Exception("Reweighting only implemented for one-dimensional correlators.") + 477 new_content = [] + 478 for t_slice in self.content: + 479 if _check_for_none(self, t_slice): + 480 new_content.append(None) + 481 else: + 482 new_content.append(np.array(reweight(weight, t_slice, **kwargs))) + 483 return Corr(new_content) + 484 + 485 def T_symmetry(self, partner, parity=+1): + 486 """Return the time symmetry average of the correlator and its partner + 487 + 488 Parameters + 489 ---------- + 490 partner : Corr + 491 Time symmetry partner of the Corr + 492 partity : int + 493 Parity quantum number of the correlator, can be +1 or -1 + 494 """ + 495 if self.N != 1: + 496 raise Exception("T_symmetry only implemented for one-dimensional correlators.") + 497 if not isinstance(partner, Corr): + 498 raise Exception("T partner has to be a Corr object.") + 499 if parity not in [+1, -1]: + 500 raise Exception("Parity has to be +1 or -1.") + 501 T_partner = parity * partner.reverse() + 502 + 503 t_slices = [] + 504 test = (self - T_partner) + 505 test.gamma_method() + 506 for x0, t_slice in enumerate(test.content): + 507 if t_slice is not None: + 508 if not t_slice[0].is_zero_within_error(5): + 509 t_slices.append(x0) + 510 if t_slices: + 511 warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning) 512 - 513 def deriv(self, variant="symmetric"): - 514 """Return the first derivative of the correlator with respect to x0. - 515 - 516 Parameters - 517 ---------- - 518 variant : str - 519 decides which definition of the finite differences derivative is used. - 520 Available choice: symmetric, forward, backward, improved, log, default: symmetric - 521 """ - 522 if self.N != 1: - 523 raise Exception("deriv only implemented for one-dimensional correlators.") - 524 if variant == "symmetric": - 525 newcontent = [] - 526 for t in range(1, self.T - 1): - 527 if (self.content[t - 1] is None) or (self.content[t + 1] is None): - 528 newcontent.append(None) - 529 else: - 530 newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1])) - 531 if (all([x is None for x in newcontent])): - 532 raise Exception('Derivative is undefined at all timeslices') - 533 return Corr(newcontent, padding=[1, 1]) - 534 elif variant == "forward": - 535 newcontent = [] - 536 for t in range(self.T - 1): - 537 if (self.content[t] is None) or (self.content[t + 1] is None): - 538 newcontent.append(None) - 539 else: - 540 newcontent.append(self.content[t + 1] - self.content[t]) - 541 if (all([x is None for x in newcontent])): - 542 raise Exception("Derivative is undefined at all timeslices") - 543 return Corr(newcontent, padding=[0, 1]) - 544 elif variant == "backward": - 545 newcontent = [] - 546 for t in range(1, self.T): - 547 if (self.content[t - 1] is None) or (self.content[t] is None): - 548 newcontent.append(None) - 549 else: - 550 newcontent.append(self.content[t] - self.content[t - 1]) - 551 if (all([x is None for x in newcontent])): - 552 raise Exception("Derivative is undefined at all timeslices") - 553 return Corr(newcontent, padding=[1, 0]) - 554 elif variant == "improved": - 555 newcontent = [] - 556 for t in range(2, self.T - 2): - 557 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): - 558 newcontent.append(None) - 559 else: - 560 newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2])) - 561 if (all([x is None for x in newcontent])): - 562 raise Exception('Derivative is undefined at all timeslices') - 563 return Corr(newcontent, padding=[2, 2]) - 564 elif variant == 'log': - 565 newcontent = [] - 566 for t in range(self.T): - 567 if (self.content[t] is None) or (self.content[t] <= 0): - 568 newcontent.append(None) - 569 else: - 570 newcontent.append(np.log(self.content[t])) - 571 if (all([x is None for x in newcontent])): - 572 raise Exception("Log is undefined at all timeslices") - 573 logcorr = Corr(newcontent) - 574 return self * logcorr.deriv('symmetric') - 575 else: - 576 raise Exception("Unknown variant.") - 577 - 578 def second_deriv(self, variant="symmetric"): - 579 """Return the second derivative of the correlator with respect to x0. - 580 - 581 Parameters - 582 ---------- - 583 variant : str - 584 decides which definition of the finite differences derivative is used. - 585 Available choice: symmetric, improved, log, default: symmetric - 586 """ - 587 if self.N != 1: - 588 raise Exception("second_deriv only implemented for one-dimensional correlators.") - 589 if variant == "symmetric": - 590 newcontent = [] - 591 for t in range(1, self.T - 1): - 592 if (self.content[t - 1] is None) or (self.content[t + 1] is None): - 593 newcontent.append(None) - 594 else: - 595 newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1])) - 596 if (all([x is None for x in newcontent])): - 597 raise Exception("Derivative is undefined at all timeslices") - 598 return Corr(newcontent, padding=[1, 1]) - 599 elif variant == "improved": - 600 newcontent = [] - 601 for t in range(2, self.T - 2): - 602 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): - 603 newcontent.append(None) - 604 else: - 605 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])) - 606 if (all([x is None for x in newcontent])): - 607 raise Exception("Derivative is undefined at all timeslices") - 608 return Corr(newcontent, padding=[2, 2]) - 609 elif variant == 'log': - 610 newcontent = [] - 611 for t in range(self.T): - 612 if (self.content[t] is None) or (self.content[t] <= 0): - 613 newcontent.append(None) - 614 else: - 615 newcontent.append(np.log(self.content[t])) - 616 if (all([x is None for x in newcontent])): - 617 raise Exception("Log is undefined at all timeslices") - 618 logcorr = Corr(newcontent) - 619 return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2) - 620 else: - 621 raise Exception("Unknown variant.") - 622 - 623 def m_eff(self, variant='log', guess=1.0): - 624 """Returns the effective mass of the correlator as correlator object - 625 - 626 Parameters - 627 ---------- - 628 variant : str - 629 log : uses the standard effective mass log(C(t) / C(t+1)) - 630 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. - 631 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. - 632 See, e.g., arXiv:1205.5380 - 633 arccosh : Uses the explicit form of the symmetrized correlator (not recommended) - 634 logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2 - 635 guess : float - 636 guess for the root finder, only relevant for the root variant - 637 """ - 638 if self.N != 1: - 639 raise Exception('Correlator must be projected before getting m_eff') - 640 if variant == 'log': - 641 newcontent = [] - 642 for t in range(self.T - 1): - 643 if ((self.content[t] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): - 644 newcontent.append(None) - 645 elif self.content[t][0].value / self.content[t + 1][0].value < 0: + 513 return (self + T_partner) / 2 + 514 + 515 def deriv(self, variant="symmetric"): + 516 """Return the first derivative of the correlator with respect to x0. + 517 + 518 Parameters + 519 ---------- + 520 variant : str + 521 decides which definition of the finite differences derivative is used. + 522 Available choice: symmetric, forward, backward, improved, log, default: symmetric + 523 """ + 524 if self.N != 1: + 525 raise Exception("deriv only implemented for one-dimensional correlators.") + 526 if variant == "symmetric": + 527 newcontent = [] + 528 for t in range(1, self.T - 1): + 529 if (self.content[t - 1] is None) or (self.content[t + 1] is None): + 530 newcontent.append(None) + 531 else: + 532 newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1])) + 533 if (all([x is None for x in newcontent])): + 534 raise Exception('Derivative is undefined at all timeslices') + 535 return Corr(newcontent, padding=[1, 1]) + 536 elif variant == "forward": + 537 newcontent = [] + 538 for t in range(self.T - 1): + 539 if (self.content[t] is None) or (self.content[t + 1] is None): + 540 newcontent.append(None) + 541 else: + 542 newcontent.append(self.content[t + 1] - self.content[t]) + 543 if (all([x is None for x in newcontent])): + 544 raise Exception("Derivative is undefined at all timeslices") + 545 return Corr(newcontent, padding=[0, 1]) + 546 elif variant == "backward": + 547 newcontent = [] + 548 for t in range(1, self.T): + 549 if (self.content[t - 1] is None) or (self.content[t] is None): + 550 newcontent.append(None) + 551 else: + 552 newcontent.append(self.content[t] - self.content[t - 1]) + 553 if (all([x is None for x in newcontent])): + 554 raise Exception("Derivative is undefined at all timeslices") + 555 return Corr(newcontent, padding=[1, 0]) + 556 elif variant == "improved": + 557 newcontent = [] + 558 for t in range(2, self.T - 2): + 559 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): + 560 newcontent.append(None) + 561 else: + 562 newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2])) + 563 if (all([x is None for x in newcontent])): + 564 raise Exception('Derivative is undefined at all timeslices') + 565 return Corr(newcontent, padding=[2, 2]) + 566 elif variant == 'log': + 567 newcontent = [] + 568 for t in range(self.T): + 569 if (self.content[t] is None) or (self.content[t] <= 0): + 570 newcontent.append(None) + 571 else: + 572 newcontent.append(np.log(self.content[t])) + 573 if (all([x is None for x in newcontent])): + 574 raise Exception("Log is undefined at all timeslices") + 575 logcorr = Corr(newcontent) + 576 return self * logcorr.deriv('symmetric') + 577 else: + 578 raise Exception("Unknown variant.") + 579 + 580 def second_deriv(self, variant="symmetric"): + 581 """Return the second derivative of the correlator with respect to x0. + 582 + 583 Parameters + 584 ---------- + 585 variant : str + 586 decides which definition of the finite differences derivative is used. + 587 Available choice: symmetric, improved, log, default: symmetric + 588 """ + 589 if self.N != 1: + 590 raise Exception("second_deriv only implemented for one-dimensional correlators.") + 591 if variant == "symmetric": + 592 newcontent = [] + 593 for t in range(1, self.T - 1): + 594 if (self.content[t - 1] is None) or (self.content[t + 1] is None): + 595 newcontent.append(None) + 596 else: + 597 newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1])) + 598 if (all([x is None for x in newcontent])): + 599 raise Exception("Derivative is undefined at all timeslices") + 600 return Corr(newcontent, padding=[1, 1]) + 601 elif variant == "improved": + 602 newcontent = [] + 603 for t in range(2, self.T - 2): + 604 if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None): + 605 newcontent.append(None) + 606 else: + 607 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])) + 608 if (all([x is None for x in newcontent])): + 609 raise Exception("Derivative is undefined at all timeslices") + 610 return Corr(newcontent, padding=[2, 2]) + 611 elif variant == 'log': + 612 newcontent = [] + 613 for t in range(self.T): + 614 if (self.content[t] is None) or (self.content[t] <= 0): + 615 newcontent.append(None) + 616 else: + 617 newcontent.append(np.log(self.content[t])) + 618 if (all([x is None for x in newcontent])): + 619 raise Exception("Log is undefined at all timeslices") + 620 logcorr = Corr(newcontent) + 621 return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2) + 622 else: + 623 raise Exception("Unknown variant.") + 624 + 625 def m_eff(self, variant='log', guess=1.0): + 626 """Returns the effective mass of the correlator as correlator object + 627 + 628 Parameters + 629 ---------- + 630 variant : str + 631 log : uses the standard effective mass log(C(t) / C(t+1)) + 632 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. + 633 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. + 634 See, e.g., arXiv:1205.5380 + 635 arccosh : Uses the explicit form of the symmetrized correlator (not recommended) + 636 logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2 + 637 guess : float + 638 guess for the root finder, only relevant for the root variant + 639 """ + 640 if self.N != 1: + 641 raise Exception('Correlator must be projected before getting m_eff') + 642 if variant == 'log': + 643 newcontent = [] + 644 for t in range(self.T - 1): + 645 if ((self.content[t] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): 646 newcontent.append(None) - 647 else: - 648 newcontent.append(self.content[t] / self.content[t + 1]) - 649 if (all([x is None for x in newcontent])): - 650 raise Exception('m_eff is undefined at all timeslices') - 651 - 652 return np.log(Corr(newcontent, padding=[0, 1])) + 647 elif self.content[t][0].value / self.content[t + 1][0].value < 0: + 648 newcontent.append(None) + 649 else: + 650 newcontent.append(self.content[t] / self.content[t + 1]) + 651 if (all([x is None for x in newcontent])): + 652 raise Exception('m_eff is undefined at all timeslices') 653 - 654 elif variant == 'logsym': - 655 newcontent = [] - 656 for t in range(1, self.T - 1): - 657 if ((self.content[t - 1] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): - 658 newcontent.append(None) - 659 elif self.content[t - 1][0].value / self.content[t + 1][0].value < 0: + 654 return np.log(Corr(newcontent, padding=[0, 1])) + 655 + 656 elif variant == 'logsym': + 657 newcontent = [] + 658 for t in range(1, self.T - 1): + 659 if ((self.content[t - 1] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0): 660 newcontent.append(None) - 661 else: - 662 newcontent.append(self.content[t - 1] / self.content[t + 1]) - 663 if (all([x is None for x in newcontent])): - 664 raise Exception('m_eff is undefined at all timeslices') - 665 - 666 return np.log(Corr(newcontent, padding=[1, 1])) / 2 + 661 elif self.content[t - 1][0].value / self.content[t + 1][0].value < 0: + 662 newcontent.append(None) + 663 else: + 664 newcontent.append(self.content[t - 1] / self.content[t + 1]) + 665 if (all([x is None for x in newcontent])): + 666 raise Exception('m_eff is undefined at all timeslices') 667 - 668 elif variant in ['periodic', 'cosh', 'sinh']: - 669 if variant in ['periodic', 'cosh']: - 670 func = anp.cosh - 671 else: - 672 func = anp.sinh - 673 - 674 def root_function(x, d): - 675 return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d - 676 - 677 newcontent = [] - 678 for t in range(self.T - 1): - 679 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 1][0].value == 0): - 680 newcontent.append(None) - 681 # Fill the two timeslices in the middle of the lattice with their predecessors - 682 elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]: - 683 newcontent.append(newcontent[-1]) - 684 elif self.content[t][0].value / self.content[t + 1][0].value < 0: - 685 newcontent.append(None) - 686 else: - 687 newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess))) - 688 if (all([x is None for x in newcontent])): - 689 raise Exception('m_eff is undefined at all timeslices') - 690 - 691 return Corr(newcontent, padding=[0, 1]) + 668 return np.log(Corr(newcontent, padding=[1, 1])) / 2 + 669 + 670 elif variant in ['periodic', 'cosh', 'sinh']: + 671 if variant in ['periodic', 'cosh']: + 672 func = anp.cosh + 673 else: + 674 func = anp.sinh + 675 + 676 def root_function(x, d): + 677 return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d + 678 + 679 newcontent = [] + 680 for t in range(self.T - 1): + 681 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 1][0].value == 0): + 682 newcontent.append(None) + 683 # Fill the two timeslices in the middle of the lattice with their predecessors + 684 elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]: + 685 newcontent.append(newcontent[-1]) + 686 elif self.content[t][0].value / self.content[t + 1][0].value < 0: + 687 newcontent.append(None) + 688 else: + 689 newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess))) + 690 if (all([x is None for x in newcontent])): + 691 raise Exception('m_eff is undefined at all timeslices') 692 - 693 elif variant == 'arccosh': - 694 newcontent = [] - 695 for t in range(1, self.T - 1): - 696 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None) or (self.content[t][0].value == 0): - 697 newcontent.append(None) - 698 else: - 699 newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t])) - 700 if (all([x is None for x in newcontent])): - 701 raise Exception("m_eff is undefined at all timeslices") - 702 return np.arccosh(Corr(newcontent, padding=[1, 1])) - 703 - 704 else: - 705 raise Exception('Unknown variant.') - 706 - 707 def fit(self, function, fitrange=None, silent=False, **kwargs): - 708 r'''Fits function to the data - 709 - 710 Parameters - 711 ---------- - 712 function : obj - 713 function to fit to the data. See fits.least_squares for details. - 714 fitrange : list - 715 Two element list containing the timeslices on which the fit is supposed to start and stop. - 716 Caution: This range is inclusive as opposed to standard python indexing. - 717 `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6. - 718 If not specified, self.prange or all timeslices are used. - 719 silent : bool - 720 Decides whether output is printed to the standard output. - 721 ''' - 722 if self.N != 1: - 723 raise Exception("Correlator must be projected before fitting") - 724 - 725 if fitrange is None: - 726 if self.prange: - 727 fitrange = self.prange - 728 else: - 729 fitrange = [0, self.T - 1] - 730 else: - 731 if not isinstance(fitrange, list): - 732 raise Exception("fitrange has to be a list with two elements") - 733 if len(fitrange) != 2: - 734 raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]") - 735 - 736 xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] - 737 ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] - 738 result = least_squares(xs, ys, function, silent=silent, **kwargs) - 739 return result - 740 - 741 def plateau(self, plateau_range=None, method="fit", auto_gamma=False): - 742 """ Extract a plateau value from a Corr object - 743 - 744 Parameters - 745 ---------- - 746 plateau_range : list - 747 list with two entries, indicating the first and the last timeslice - 748 of the plateau region. - 749 method : str - 750 method to extract the plateau. - 751 'fit' fits a constant to the plateau region - 752 'avg', 'average' or 'mean' just average over the given timeslices. - 753 auto_gamma : bool - 754 apply gamma_method with default parameters to the Corr. Defaults to None - 755 """ - 756 if not plateau_range: - 757 if self.prange: - 758 plateau_range = self.prange - 759 else: - 760 raise Exception("no plateau range provided") - 761 if self.N != 1: - 762 raise Exception("Correlator must be projected before getting a plateau.") - 763 if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])): - 764 raise Exception("plateau is undefined at all timeslices in plateaurange.") - 765 if auto_gamma: - 766 self.gamma_method() - 767 if method == "fit": - 768 def const_func(a, t): - 769 return a[0] - 770 return self.fit(const_func, plateau_range)[0] - 771 elif method in ["avg", "average", "mean"]: - 772 returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None]) - 773 return returnvalue - 774 - 775 else: - 776 raise Exception("Unsupported plateau method: " + method) - 777 - 778 def set_prange(self, prange): - 779 """Sets the attribute prange of the Corr object.""" - 780 if not len(prange) == 2: - 781 raise Exception("prange must be a list or array with two values") - 782 if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))): - 783 raise Exception("Start and end point must be integers") - 784 if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]): - 785 raise Exception("Start and end point must define a range in the interval 0,T") - 786 - 787 self.prange = prange - 788 return - 789 - 790 def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None): - 791 """Plots the correlator using the tag of the correlator as label if available. - 792 - 793 Parameters - 794 ---------- - 795 x_range : list - 796 list of two values, determining the range of the x-axis e.g. [4, 8]. - 797 comp : Corr or list of Corr - 798 Correlator or list of correlators which are plotted for comparison. - 799 The tags of these correlators are used as labels if available. - 800 logscale : bool - 801 Sets y-axis to logscale. - 802 plateau : Obs - 803 Plateau value to be visualized in the figure. - 804 fit_res : Fit_result - 805 Fit_result object to be visualized. - 806 ylabel : str - 807 Label for the y-axis. - 808 save : str - 809 path to file in which the figure should be saved. - 810 auto_gamma : bool - 811 Apply the gamma method with standard parameters to all correlators and plateau values before plotting. - 812 hide_sigma : float - 813 Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors. - 814 references : list - 815 List of floating point values that are displayed as horizontal lines for reference. - 816 title : string - 817 Optional title of the figure. - 818 """ - 819 if self.N != 1: - 820 raise Exception("Correlator must be projected before plotting") - 821 - 822 if auto_gamma: - 823 self.gamma_method() - 824 - 825 if x_range is None: - 826 x_range = [0, self.T - 1] - 827 - 828 fig = plt.figure() - 829 ax1 = fig.add_subplot(111) - 830 - 831 x, y, y_err = self.plottable() - 832 if hide_sigma: - 833 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 - 834 else: - 835 hide_from = None - 836 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag) - 837 if logscale: - 838 ax1.set_yscale('log') - 839 else: - 840 if y_range is None: - 841 try: - 842 y_min = min([(x[0].value - x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) - 843 y_max = max([(x[0].value + x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) - 844 ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)]) - 845 except Exception: - 846 pass - 847 else: - 848 ax1.set_ylim(y_range) - 849 if comp: - 850 if isinstance(comp, (Corr, list)): - 851 for corr in comp if isinstance(comp, list) else [comp]: - 852 if auto_gamma: - 853 corr.gamma_method() - 854 x, y, y_err = corr.plottable() - 855 if hide_sigma: - 856 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 - 857 else: - 858 hide_from = None - 859 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor']) - 860 else: - 861 raise Exception("'comp' must be a correlator or a list of correlators.") - 862 - 863 if plateau: - 864 if isinstance(plateau, Obs): - 865 if auto_gamma: - 866 plateau.gamma_method() - 867 ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau)) - 868 ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-') - 869 else: - 870 raise Exception("'plateau' must be an Obs") - 871 - 872 if references: - 873 if isinstance(references, list): - 874 for ref in references: - 875 ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--') - 876 else: - 877 raise Exception("'references' must be a list of floating pint values.") - 878 - 879 if self.prange: - 880 ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',') - 881 ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',') - 882 - 883 if fit_res: - 884 x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05) - 885 ax1.plot(x_samples, - 886 fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples), - 887 ls='-', marker=',', lw=2) - 888 - 889 ax1.set_xlabel(r'$x_0 / a$') - 890 if ylabel: - 891 ax1.set_ylabel(ylabel) - 892 ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5]) - 893 - 894 handles, labels = ax1.get_legend_handles_labels() - 895 if labels: - 896 ax1.legend() - 897 - 898 if title: - 899 plt.title(title) - 900 - 901 plt.draw() + 693 return Corr(newcontent, padding=[0, 1]) + 694 + 695 elif variant == 'arccosh': + 696 newcontent = [] + 697 for t in range(1, self.T - 1): + 698 if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None) or (self.content[t][0].value == 0): + 699 newcontent.append(None) + 700 else: + 701 newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t])) + 702 if (all([x is None for x in newcontent])): + 703 raise Exception("m_eff is undefined at all timeslices") + 704 return np.arccosh(Corr(newcontent, padding=[1, 1])) + 705 + 706 else: + 707 raise Exception('Unknown variant.') + 708 + 709 def fit(self, function, fitrange=None, silent=False, **kwargs): + 710 r'''Fits function to the data + 711 + 712 Parameters + 713 ---------- + 714 function : obj + 715 function to fit to the data. See fits.least_squares for details. + 716 fitrange : list + 717 Two element list containing the timeslices on which the fit is supposed to start and stop. + 718 Caution: This range is inclusive as opposed to standard python indexing. + 719 `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6. + 720 If not specified, self.prange or all timeslices are used. + 721 silent : bool + 722 Decides whether output is printed to the standard output. + 723 ''' + 724 if self.N != 1: + 725 raise Exception("Correlator must be projected before fitting") + 726 + 727 if fitrange is None: + 728 if self.prange: + 729 fitrange = self.prange + 730 else: + 731 fitrange = [0, self.T - 1] + 732 else: + 733 if not isinstance(fitrange, list): + 734 raise Exception("fitrange has to be a list with two elements") + 735 if len(fitrange) != 2: + 736 raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]") + 737 + 738 xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] + 739 ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None] + 740 result = least_squares(xs, ys, function, silent=silent, **kwargs) + 741 return result + 742 + 743 def plateau(self, plateau_range=None, method="fit", auto_gamma=False): + 744 """ Extract a plateau value from a Corr object + 745 + 746 Parameters + 747 ---------- + 748 plateau_range : list + 749 list with two entries, indicating the first and the last timeslice + 750 of the plateau region. + 751 method : str + 752 method to extract the plateau. + 753 'fit' fits a constant to the plateau region + 754 'avg', 'average' or 'mean' just average over the given timeslices. + 755 auto_gamma : bool + 756 apply gamma_method with default parameters to the Corr. Defaults to None + 757 """ + 758 if not plateau_range: + 759 if self.prange: + 760 plateau_range = self.prange + 761 else: + 762 raise Exception("no plateau range provided") + 763 if self.N != 1: + 764 raise Exception("Correlator must be projected before getting a plateau.") + 765 if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])): + 766 raise Exception("plateau is undefined at all timeslices in plateaurange.") + 767 if auto_gamma: + 768 self.gamma_method() + 769 if method == "fit": + 770 def const_func(a, t): + 771 return a[0] + 772 return self.fit(const_func, plateau_range)[0] + 773 elif method in ["avg", "average", "mean"]: + 774 returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None]) + 775 return returnvalue + 776 + 777 else: + 778 raise Exception("Unsupported plateau method: " + method) + 779 + 780 def set_prange(self, prange): + 781 """Sets the attribute prange of the Corr object.""" + 782 if not len(prange) == 2: + 783 raise Exception("prange must be a list or array with two values") + 784 if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))): + 785 raise Exception("Start and end point must be integers") + 786 if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]): + 787 raise Exception("Start and end point must define a range in the interval 0,T") + 788 + 789 self.prange = prange + 790 return + 791 + 792 def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None): + 793 """Plots the correlator using the tag of the correlator as label if available. + 794 + 795 Parameters + 796 ---------- + 797 x_range : list + 798 list of two values, determining the range of the x-axis e.g. [4, 8]. + 799 comp : Corr or list of Corr + 800 Correlator or list of correlators which are plotted for comparison. + 801 The tags of these correlators are used as labels if available. + 802 logscale : bool + 803 Sets y-axis to logscale. + 804 plateau : Obs + 805 Plateau value to be visualized in the figure. + 806 fit_res : Fit_result + 807 Fit_result object to be visualized. + 808 ylabel : str + 809 Label for the y-axis. + 810 save : str + 811 path to file in which the figure should be saved. + 812 auto_gamma : bool + 813 Apply the gamma method with standard parameters to all correlators and plateau values before plotting. + 814 hide_sigma : float + 815 Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors. + 816 references : list + 817 List of floating point values that are displayed as horizontal lines for reference. + 818 title : string + 819 Optional title of the figure. + 820 """ + 821 if self.N != 1: + 822 raise Exception("Correlator must be projected before plotting") + 823 + 824 if auto_gamma: + 825 self.gamma_method() + 826 + 827 if x_range is None: + 828 x_range = [0, self.T - 1] + 829 + 830 fig = plt.figure() + 831 ax1 = fig.add_subplot(111) + 832 + 833 x, y, y_err = self.plottable() + 834 if hide_sigma: + 835 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 + 836 else: + 837 hide_from = None + 838 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag) + 839 if logscale: + 840 ax1.set_yscale('log') + 841 else: + 842 if y_range is None: + 843 try: + 844 y_min = min([(x[0].value - x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) + 845 y_max = max([(x[0].value + x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)]) + 846 ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)]) + 847 except Exception: + 848 pass + 849 else: + 850 ax1.set_ylim(y_range) + 851 if comp: + 852 if isinstance(comp, (Corr, list)): + 853 for corr in comp if isinstance(comp, list) else [comp]: + 854 if auto_gamma: + 855 corr.gamma_method() + 856 x, y, y_err = corr.plottable() + 857 if hide_sigma: + 858 hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1 + 859 else: + 860 hide_from = None + 861 ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor']) + 862 else: + 863 raise Exception("'comp' must be a correlator or a list of correlators.") + 864 + 865 if plateau: + 866 if isinstance(plateau, Obs): + 867 if auto_gamma: + 868 plateau.gamma_method() + 869 ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau)) + 870 ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-') + 871 else: + 872 raise Exception("'plateau' must be an Obs") + 873 + 874 if references: + 875 if isinstance(references, list): + 876 for ref in references: + 877 ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--') + 878 else: + 879 raise Exception("'references' must be a list of floating pint values.") + 880 + 881 if self.prange: + 882 ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',') + 883 ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',') + 884 + 885 if fit_res: + 886 x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05) + 887 ax1.plot(x_samples, + 888 fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples), + 889 ls='-', marker=',', lw=2) + 890 + 891 ax1.set_xlabel(r'$x_0 / a$') + 892 if ylabel: + 893 ax1.set_ylabel(ylabel) + 894 ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5]) + 895 + 896 handles, labels = ax1.get_legend_handles_labels() + 897 if labels: + 898 ax1.legend() + 899 + 900 if title: + 901 plt.title(title) 902 - 903 if save: - 904 if isinstance(save, str): - 905 fig.savefig(save, bbox_inches='tight') - 906 else: - 907 raise Exception("'save' has to be a string.") - 908 - 909 def spaghetti_plot(self, logscale=True): - 910 """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations. - 911 - 912 Parameters - 913 ---------- - 914 logscale : bool - 915 Determines whether the scale of the y-axis is logarithmic or standard. - 916 """ - 917 if self.N != 1: - 918 raise Exception("Correlator needs to be projected first.") - 919 - 920 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])) - 921 x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None] - 922 - 923 for name in mc_names: - 924 data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T - 925 - 926 fig = plt.figure() - 927 ax = fig.add_subplot(111) - 928 for dat in data: - 929 ax.plot(x0_vals, dat, ls='-', marker='') - 930 - 931 if logscale is True: - 932 ax.set_yscale('log') - 933 - 934 ax.set_xlabel(r'$x_0 / a$') - 935 plt.title(name) - 936 plt.draw() - 937 - 938 def dump(self, filename, datatype="json.gz", **kwargs): - 939 """Dumps the Corr into a file of chosen type - 940 Parameters - 941 ---------- - 942 filename : str - 943 Name of the file to be saved. - 944 datatype : str - 945 Format of the exported file. Supported formats include - 946 "json.gz" and "pickle" - 947 path : str - 948 specifies a custom path for the file (default '.') - 949 """ - 950 if datatype == "json.gz": - 951 from .input.json import dump_to_json - 952 if 'path' in kwargs: - 953 file_name = kwargs.get('path') + '/' + filename - 954 else: - 955 file_name = filename - 956 dump_to_json(self, file_name) - 957 elif datatype == "pickle": - 958 dump_object(self, filename, **kwargs) - 959 else: - 960 raise Exception("Unknown datatype " + str(datatype)) - 961 - 962 def print(self, print_range=None): - 963 print(self.__repr__(print_range)) - 964 - 965 def __repr__(self, print_range=None): - 966 if print_range is None: - 967 print_range = [0, None] - 968 - 969 content_string = "" - 970 content_string += "Corr T=" + str(self.T) + " N=" + str(self.N) + "\n" # +" filled with"+ str(type(self.content[0][0])) there should be a good solution here - 971 - 972 if self.tag is not None: - 973 content_string += "Description: " + self.tag + "\n" - 974 if self.N != 1: - 975 return content_string - 976 if isinstance(self[0], CObs): + 903 plt.draw() + 904 + 905 if save: + 906 if isinstance(save, str): + 907 fig.savefig(save, bbox_inches='tight') + 908 else: + 909 raise Exception("'save' has to be a string.") + 910 + 911 def spaghetti_plot(self, logscale=True): + 912 """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations. + 913 + 914 Parameters + 915 ---------- + 916 logscale : bool + 917 Determines whether the scale of the y-axis is logarithmic or standard. + 918 """ + 919 if self.N != 1: + 920 raise Exception("Correlator needs to be projected first.") + 921 + 922 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])) + 923 x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None] + 924 + 925 for name in mc_names: + 926 data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T + 927 + 928 fig = plt.figure() + 929 ax = fig.add_subplot(111) + 930 for dat in data: + 931 ax.plot(x0_vals, dat, ls='-', marker='') + 932 + 933 if logscale is True: + 934 ax.set_yscale('log') + 935 + 936 ax.set_xlabel(r'$x_0 / a$') + 937 plt.title(name) + 938 plt.draw() + 939 + 940 def dump(self, filename, datatype="json.gz", **kwargs): + 941 """Dumps the Corr into a file of chosen type + 942 Parameters + 943 ---------- + 944 filename : str + 945 Name of the file to be saved. + 946 datatype : str + 947 Format of the exported file. Supported formats include + 948 "json.gz" and "pickle" + 949 path : str + 950 specifies a custom path for the file (default '.') + 951 """ + 952 if datatype == "json.gz": + 953 from .input.json import dump_to_json + 954 if 'path' in kwargs: + 955 file_name = kwargs.get('path') + '/' + filename + 956 else: + 957 file_name = filename + 958 dump_to_json(self, file_name) + 959 elif datatype == "pickle": + 960 dump_object(self, filename, **kwargs) + 961 else: + 962 raise Exception("Unknown datatype " + str(datatype)) + 963 + 964 def print(self, print_range=None): + 965 print(self.__repr__(print_range)) + 966 + 967 def __repr__(self, print_range=None): + 968 if print_range is None: + 969 print_range = [0, None] + 970 + 971 content_string = "" + 972 content_string += "Corr T=" + str(self.T) + " N=" + str(self.N) + "\n" # +" filled with"+ str(type(self.content[0][0])) there should be a good solution here + 973 + 974 if self.tag is not None: + 975 content_string += "Description: " + self.tag + "\n" + 976 if self.N != 1: 977 return content_string - 978 - 979 if print_range[1]: - 980 print_range[1] += 1 - 981 content_string += 'x0/a\tCorr(x0/a)\n------------------\n' - 982 for i, sub_corr in enumerate(self.content[print_range[0]:print_range[1]]): - 983 if sub_corr is None: - 984 content_string += str(i + print_range[0]) + '\n' - 985 else: - 986 content_string += str(i + print_range[0]) - 987 for element in sub_corr: - 988 content_string += '\t' + ' ' * int(element >= 0) + str(element) - 989 content_string += '\n' - 990 return content_string - 991 - 992 def __str__(self): - 993 return self.__repr__() - 994 - 995 # We define the basic operations, that can be performed with correlators. - 996 # While */+- get defined here, they only work for Corr*Obs and not Obs*Corr. - 997 # This is because Obs*Corr checks Obs.__mul__ first and does not catch an exception. - 998 # One could try and tell Obs to check if the y in __mul__ is a Corr and - 999 -1000 def __add__(self, y): -1001 if isinstance(y, Corr): -1002 if ((self.N != y.N) or (self.T != y.T)): -1003 raise Exception("Addition of Corrs with different shape") -1004 newcontent = [] -1005 for t in range(self.T): -1006 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): -1007 newcontent.append(None) -1008 else: -1009 newcontent.append(self.content[t] + y.content[t]) -1010 return Corr(newcontent) -1011 -1012 elif isinstance(y, (Obs, int, float, CObs)): -1013 newcontent = [] -1014 for t in range(self.T): -1015 if _check_for_none(self, self.content[t]): -1016 newcontent.append(None) -1017 else: -1018 newcontent.append(self.content[t] + y) -1019 return Corr(newcontent, prange=self.prange) -1020 elif isinstance(y, np.ndarray): -1021 if y.shape == (self.T,): -1022 return Corr(list((np.array(self.content).T + y).T)) -1023 else: -1024 raise ValueError("operands could not be broadcast together") -1025 else: -1026 raise TypeError("Corr + wrong type") -1027 -1028 def __mul__(self, y): -1029 if isinstance(y, Corr): -1030 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): -1031 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") -1032 newcontent = [] -1033 for t in range(self.T): -1034 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): -1035 newcontent.append(None) -1036 else: -1037 newcontent.append(self.content[t] * y.content[t]) -1038 return Corr(newcontent) -1039 -1040 elif isinstance(y, (Obs, int, float, CObs)): -1041 newcontent = [] -1042 for t in range(self.T): -1043 if _check_for_none(self, self.content[t]): -1044 newcontent.append(None) -1045 else: -1046 newcontent.append(self.content[t] * y) -1047 return Corr(newcontent, prange=self.prange) -1048 elif isinstance(y, np.ndarray): -1049 if y.shape == (self.T,): -1050 return Corr(list((np.array(self.content).T * y).T)) -1051 else: -1052 raise ValueError("operands could not be broadcast together") -1053 else: -1054 raise TypeError("Corr * wrong type") -1055 -1056 def __truediv__(self, y): -1057 if isinstance(y, Corr): -1058 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): -1059 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") -1060 newcontent = [] -1061 for t in range(self.T): -1062 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): -1063 newcontent.append(None) -1064 else: -1065 newcontent.append(self.content[t] / y.content[t]) -1066 for t in range(self.T): -1067 if _check_for_none(self, newcontent[t]): -1068 continue -1069 if np.isnan(np.sum(newcontent[t]).value): -1070 newcontent[t] = None -1071 -1072 if all([item is None for item in newcontent]): -1073 raise Exception("Division returns completely undefined correlator") -1074 return Corr(newcontent) -1075 -1076 elif isinstance(y, (Obs, CObs)): -1077 if isinstance(y, Obs): -1078 if y.value == 0: -1079 raise Exception('Division by zero will return undefined correlator') -1080 if isinstance(y, CObs): -1081 if y.is_zero(): -1082 raise Exception('Division by zero will return undefined correlator') -1083 -1084 newcontent = [] -1085 for t in range(self.T): -1086 if _check_for_none(self, self.content[t]): -1087 newcontent.append(None) -1088 else: -1089 newcontent.append(self.content[t] / y) -1090 return Corr(newcontent, prange=self.prange) -1091 -1092 elif isinstance(y, (int, float)): -1093 if y == 0: -1094 raise Exception('Division by zero will return undefined correlator') -1095 newcontent = [] -1096 for t in range(self.T): -1097 if _check_for_none(self, self.content[t]): -1098 newcontent.append(None) -1099 else: -1100 newcontent.append(self.content[t] / y) -1101 return Corr(newcontent, prange=self.prange) -1102 elif isinstance(y, np.ndarray): -1103 if y.shape == (self.T,): -1104 return Corr(list((np.array(self.content).T / y).T)) -1105 else: -1106 raise ValueError("operands could not be broadcast together") -1107 else: -1108 raise TypeError('Corr / wrong type') -1109 -1110 def __neg__(self): -1111 newcontent = [None if _check_for_none(self, item) else -1. * item for item in self.content] -1112 return Corr(newcontent, prange=self.prange) -1113 -1114 def __sub__(self, y): -1115 return self + (-y) -1116 -1117 def __pow__(self, y): -1118 if isinstance(y, (Obs, int, float, CObs)): -1119 newcontent = [None if _check_for_none(self, item) else item**y for item in self.content] -1120 return Corr(newcontent, prange=self.prange) -1121 else: -1122 raise TypeError('Type of exponent not supported') -1123 -1124 def __abs__(self): -1125 newcontent = [None if _check_for_none(self, item) else np.abs(item) for item in self.content] -1126 return Corr(newcontent, prange=self.prange) -1127 -1128 # The numpy functions: -1129 def sqrt(self): -1130 return self ** 0.5 -1131 -1132 def log(self): -1133 newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content] -1134 return Corr(newcontent, prange=self.prange) -1135 -1136 def exp(self): -1137 newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content] -1138 return Corr(newcontent, prange=self.prange) -1139 -1140 def _apply_func_to_corr(self, func): -1141 newcontent = [None if _check_for_none(self, item) else func(item) for item in self.content] -1142 for t in range(self.T): -1143 if _check_for_none(self, newcontent[t]): -1144 continue -1145 tmp_sum = np.sum(newcontent[t]) -1146 if hasattr(tmp_sum, "value"): -1147 if np.isnan(tmp_sum.value): -1148 newcontent[t] = None -1149 if all([item is None for item in newcontent]): -1150 raise Exception('Operation returns undefined correlator') -1151 return Corr(newcontent) -1152 -1153 def sin(self): -1154 return self._apply_func_to_corr(np.sin) -1155 -1156 def cos(self): -1157 return self._apply_func_to_corr(np.cos) -1158 -1159 def tan(self): -1160 return self._apply_func_to_corr(np.tan) -1161 -1162 def sinh(self): -1163 return self._apply_func_to_corr(np.sinh) -1164 -1165 def cosh(self): -1166 return self._apply_func_to_corr(np.cosh) -1167 -1168 def tanh(self): -1169 return self._apply_func_to_corr(np.tanh) -1170 -1171 def arcsin(self): -1172 return self._apply_func_to_corr(np.arcsin) -1173 -1174 def arccos(self): -1175 return self._apply_func_to_corr(np.arccos) -1176 -1177 def arctan(self): -1178 return self._apply_func_to_corr(np.arctan) -1179 -1180 def arcsinh(self): -1181 return self._apply_func_to_corr(np.arcsinh) -1182 -1183 def arccosh(self): -1184 return self._apply_func_to_corr(np.arccosh) -1185 -1186 def arctanh(self): -1187 return self._apply_func_to_corr(np.arctanh) -1188 -1189 # Right hand side operations (require tweak in main module to work) -1190 def __radd__(self, y): -1191 return self + y -1192 -1193 def __rsub__(self, y): -1194 return -self + y -1195 -1196 def __rmul__(self, y): -1197 return self * y -1198 -1199 def __rtruediv__(self, y): -1200 return (self / y) ** (-1) -1201 -1202 @property -1203 def real(self): -1204 def return_real(obs_OR_cobs): -1205 if isinstance(obs_OR_cobs.flatten()[0], CObs): -1206 return np.vectorize(lambda x: x.real)(obs_OR_cobs) -1207 else: -1208 return obs_OR_cobs -1209 -1210 return self._apply_func_to_corr(return_real) + 978 if isinstance(self[0], CObs): + 979 return content_string + 980 + 981 if print_range[1]: + 982 print_range[1] += 1 + 983 content_string += 'x0/a\tCorr(x0/a)\n------------------\n' + 984 for i, sub_corr in enumerate(self.content[print_range[0]:print_range[1]]): + 985 if sub_corr is None: + 986 content_string += str(i + print_range[0]) + '\n' + 987 else: + 988 content_string += str(i + print_range[0]) + 989 for element in sub_corr: + 990 content_string += '\t' + ' ' * int(element >= 0) + str(element) + 991 content_string += '\n' + 992 return content_string + 993 + 994 def __str__(self): + 995 return self.__repr__() + 996 + 997 # We define the basic operations, that can be performed with correlators. + 998 # While */+- get defined here, they only work for Corr*Obs and not Obs*Corr. + 999 # This is because Obs*Corr checks Obs.__mul__ first and does not catch an exception. +1000 # One could try and tell Obs to check if the y in __mul__ is a Corr and +1001 +1002 def __add__(self, y): +1003 if isinstance(y, Corr): +1004 if ((self.N != y.N) or (self.T != y.T)): +1005 raise Exception("Addition of Corrs with different shape") +1006 newcontent = [] +1007 for t in range(self.T): +1008 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): +1009 newcontent.append(None) +1010 else: +1011 newcontent.append(self.content[t] + y.content[t]) +1012 return Corr(newcontent) +1013 +1014 elif isinstance(y, (Obs, int, float, CObs)): +1015 newcontent = [] +1016 for t in range(self.T): +1017 if _check_for_none(self, self.content[t]): +1018 newcontent.append(None) +1019 else: +1020 newcontent.append(self.content[t] + y) +1021 return Corr(newcontent, prange=self.prange) +1022 elif isinstance(y, np.ndarray): +1023 if y.shape == (self.T,): +1024 return Corr(list((np.array(self.content).T + y).T)) +1025 else: +1026 raise ValueError("operands could not be broadcast together") +1027 else: +1028 raise TypeError("Corr + wrong type") +1029 +1030 def __mul__(self, y): +1031 if isinstance(y, Corr): +1032 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): +1033 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") +1034 newcontent = [] +1035 for t in range(self.T): +1036 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): +1037 newcontent.append(None) +1038 else: +1039 newcontent.append(self.content[t] * y.content[t]) +1040 return Corr(newcontent) +1041 +1042 elif isinstance(y, (Obs, int, float, CObs)): +1043 newcontent = [] +1044 for t in range(self.T): +1045 if _check_for_none(self, self.content[t]): +1046 newcontent.append(None) +1047 else: +1048 newcontent.append(self.content[t] * y) +1049 return Corr(newcontent, prange=self.prange) +1050 elif isinstance(y, np.ndarray): +1051 if y.shape == (self.T,): +1052 return Corr(list((np.array(self.content).T * y).T)) +1053 else: +1054 raise ValueError("operands could not be broadcast together") +1055 else: +1056 raise TypeError("Corr * wrong type") +1057 +1058 def __truediv__(self, y): +1059 if isinstance(y, Corr): +1060 if not ((self.N == 1 or y.N == 1 or self.N == y.N) and self.T == y.T): +1061 raise Exception("Multiplication of Corr object requires N=N or N=1 and T=T") +1062 newcontent = [] +1063 for t in range(self.T): +1064 if _check_for_none(self, self.content[t]) or _check_for_none(y, y.content[t]): +1065 newcontent.append(None) +1066 else: +1067 newcontent.append(self.content[t] / y.content[t]) +1068 for t in range(self.T): +1069 if _check_for_none(self, newcontent[t]): +1070 continue +1071 if np.isnan(np.sum(newcontent[t]).value): +1072 newcontent[t] = None +1073 +1074 if all([item is None for item in newcontent]): +1075 raise Exception("Division returns completely undefined correlator") +1076 return Corr(newcontent) +1077 +1078 elif isinstance(y, (Obs, CObs)): +1079 if isinstance(y, Obs): +1080 if y.value == 0: +1081 raise Exception('Division by zero will return undefined correlator') +1082 if isinstance(y, CObs): +1083 if y.is_zero(): +1084 raise Exception('Division by zero will return undefined correlator') +1085 +1086 newcontent = [] +1087 for t in range(self.T): +1088 if _check_for_none(self, self.content[t]): +1089 newcontent.append(None) +1090 else: +1091 newcontent.append(self.content[t] / y) +1092 return Corr(newcontent, prange=self.prange) +1093 +1094 elif isinstance(y, (int, float)): +1095 if y == 0: +1096 raise Exception('Division by zero will return undefined correlator') +1097 newcontent = [] +1098 for t in range(self.T): +1099 if _check_for_none(self, self.content[t]): +1100 newcontent.append(None) +1101 else: +1102 newcontent.append(self.content[t] / y) +1103 return Corr(newcontent, prange=self.prange) +1104 elif isinstance(y, np.ndarray): +1105 if y.shape == (self.T,): +1106 return Corr(list((np.array(self.content).T / y).T)) +1107 else: +1108 raise ValueError("operands could not be broadcast together") +1109 else: +1110 raise TypeError('Corr / wrong type') +1111 +1112 def __neg__(self): +1113 newcontent = [None if _check_for_none(self, item) else -1. * item for item in self.content] +1114 return Corr(newcontent, prange=self.prange) +1115 +1116 def __sub__(self, y): +1117 return self + (-y) +1118 +1119 def __pow__(self, y): +1120 if isinstance(y, (Obs, int, float, CObs)): +1121 newcontent = [None if _check_for_none(self, item) else item**y for item in self.content] +1122 return Corr(newcontent, prange=self.prange) +1123 else: +1124 raise TypeError('Type of exponent not supported') +1125 +1126 def __abs__(self): +1127 newcontent = [None if _check_for_none(self, item) else np.abs(item) for item in self.content] +1128 return Corr(newcontent, prange=self.prange) +1129 +1130 # The numpy functions: +1131 def sqrt(self): +1132 return self ** 0.5 +1133 +1134 def log(self): +1135 newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content] +1136 return Corr(newcontent, prange=self.prange) +1137 +1138 def exp(self): +1139 newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content] +1140 return Corr(newcontent, prange=self.prange) +1141 +1142 def _apply_func_to_corr(self, func): +1143 newcontent = [None if _check_for_none(self, item) else func(item) for item in self.content] +1144 for t in range(self.T): +1145 if _check_for_none(self, newcontent[t]): +1146 continue +1147 tmp_sum = np.sum(newcontent[t]) +1148 if hasattr(tmp_sum, "value"): +1149 if np.isnan(tmp_sum.value): +1150 newcontent[t] = None +1151 if all([item is None for item in newcontent]): +1152 raise Exception('Operation returns undefined correlator') +1153 return Corr(newcontent) +1154 +1155 def sin(self): +1156 return self._apply_func_to_corr(np.sin) +1157 +1158 def cos(self): +1159 return self._apply_func_to_corr(np.cos) +1160 +1161 def tan(self): +1162 return self._apply_func_to_corr(np.tan) +1163 +1164 def sinh(self): +1165 return self._apply_func_to_corr(np.sinh) +1166 +1167 def cosh(self): +1168 return self._apply_func_to_corr(np.cosh) +1169 +1170 def tanh(self): +1171 return self._apply_func_to_corr(np.tanh) +1172 +1173 def arcsin(self): +1174 return self._apply_func_to_corr(np.arcsin) +1175 +1176 def arccos(self): +1177 return self._apply_func_to_corr(np.arccos) +1178 +1179 def arctan(self): +1180 return self._apply_func_to_corr(np.arctan) +1181 +1182 def arcsinh(self): +1183 return self._apply_func_to_corr(np.arcsinh) +1184 +1185 def arccosh(self): +1186 return self._apply_func_to_corr(np.arccosh) +1187 +1188 def arctanh(self): +1189 return self._apply_func_to_corr(np.arctanh) +1190 +1191 # Right hand side operations (require tweak in main module to work) +1192 def __radd__(self, y): +1193 return self + y +1194 +1195 def __rsub__(self, y): +1196 return -self + y +1197 +1198 def __rmul__(self, y): +1199 return self * y +1200 +1201 def __rtruediv__(self, y): +1202 return (self / y) ** (-1) +1203 +1204 @property +1205 def real(self): +1206 def return_real(obs_OR_cobs): +1207 if isinstance(obs_OR_cobs.flatten()[0], CObs): +1208 return np.vectorize(lambda x: x.real)(obs_OR_cobs) +1209 else: +1210 return obs_OR_cobs 1211 -1212 @property -1213 def imag(self): -1214 def return_imag(obs_OR_cobs): -1215 if isinstance(obs_OR_cobs.flatten()[0], CObs): -1216 return np.vectorize(lambda x: x.imag)(obs_OR_cobs) -1217 else: -1218 return obs_OR_cobs * 0 # So it stays the right type -1219 -1220 return self._apply_func_to_corr(return_imag) +1212 return self._apply_func_to_corr(return_real) +1213 +1214 @property +1215 def imag(self): +1216 def return_imag(obs_OR_cobs): +1217 if isinstance(obs_OR_cobs.flatten()[0], CObs): +1218 return np.vectorize(lambda x: x.imag)(obs_OR_cobs) +1219 else: +1220 return obs_OR_cobs * 0 # So it stays the right type 1221 -1222 def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None): -1223 r''' Project large correlation matrix to lowest states -1224 -1225 This method can be used to reduce the size of an (N x N) correlation matrix -1226 to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise -1227 is still small. -1228 -1229 Parameters -1230 ---------- -1231 Ntrunc: int -1232 Rank of the target matrix. -1233 tproj: int -1234 Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method. -1235 The default value is 3. -1236 t0proj: int -1237 Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly -1238 discouraged for O(a) improved theories, since the correctness of the procedure -1239 cannot be granted in this case. The default value is 2. -1240 basematrix : Corr -1241 Correlation matrix that is used to determine the eigenvectors of the -1242 lowest states based on a GEVP. basematrix is taken to be the Corr itself if -1243 is is not specified. -1244 -1245 Notes -1246 ----- -1247 We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving -1248 the GEVP $$C(t) v_n(t, t_0) = \lambda_n(t, t_0) C(t_0) v_n(t, t_0)$$ where $t \equiv t_\mathrm{proj}$ -1249 and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the -1250 resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via -1251 $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large -1252 correlation matrix and to remove some noise that is added by irrelevant operators. -1253 This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated -1254 bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$. -1255 ''' -1256 -1257 if self.N == 1: -1258 raise Exception('Method cannot be applied to one-dimensional correlators.') -1259 if basematrix is None: -1260 basematrix = self -1261 if Ntrunc >= basematrix.N: -1262 raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N)) -1263 if basematrix.N != self.N: -1264 raise Exception('basematrix and targetmatrix have to be of the same size.') -1265 -1266 evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc] +1222 return self._apply_func_to_corr(return_imag) +1223 +1224 def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None): +1225 r''' Project large correlation matrix to lowest states +1226 +1227 This method can be used to reduce the size of an (N x N) correlation matrix +1228 to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise +1229 is still small. +1230 +1231 Parameters +1232 ---------- +1233 Ntrunc: int +1234 Rank of the target matrix. +1235 tproj: int +1236 Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method. +1237 The default value is 3. +1238 t0proj: int +1239 Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly +1240 discouraged for O(a) improved theories, since the correctness of the procedure +1241 cannot be granted in this case. The default value is 2. +1242 basematrix : Corr +1243 Correlation matrix that is used to determine the eigenvectors of the +1244 lowest states based on a GEVP. basematrix is taken to be the Corr itself if +1245 is is not specified. +1246 +1247 Notes +1248 ----- +1249 We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving +1250 the GEVP $$C(t) v_n(t, t_0) = \lambda_n(t, t_0) C(t_0) v_n(t, t_0)$$ where $t \equiv t_\mathrm{proj}$ +1251 and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the +1252 resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via +1253 $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large +1254 correlation matrix and to remove some noise that is added by irrelevant operators. +1255 This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated +1256 bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$. +1257 ''' +1258 +1259 if self.N == 1: +1260 raise Exception('Method cannot be applied to one-dimensional correlators.') +1261 if basematrix is None: +1262 basematrix = self +1263 if Ntrunc >= basematrix.N: +1264 raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N)) +1265 if basematrix.N != self.N: +1266 raise Exception('basematrix and targetmatrix have to be of the same size.') 1267 -1268 tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object) -1269 rmat = [] -1270 for t in range(basematrix.T): -1271 for i in range(Ntrunc): -1272 for j in range(Ntrunc): -1273 tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j] -1274 rmat.append(np.copy(tmpmat)) -1275 -1276 newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)] -1277 return Corr(newcontent) +1268 evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc] +1269 +1270 tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object) +1271 rmat = [] +1272 for t in range(basematrix.T): +1273 for i in range(Ntrunc): +1274 for j in range(Ntrunc): +1275 tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j] +1276 rmat.append(np.copy(tmpmat)) +1277 +1278 newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)] +1279 return Corr(newcontent) @@ -2835,76 +2839,76 @@ matrix at every timeslice. Other dependency (eg. spatial) are not supported.

-
27    def __init__(self, data_input, padding=[0, 0], prange=None):
-28        """ Initialize a Corr object.
-29
-30        Parameters
-31        ----------
-32        data_input : list or array
-33            list of Obs or list of arrays of Obs or array of Corrs
-34        padding : list, optional
-35            List with two entries where the first labels the padding
-36            at the front of the correlator and the second the padding
-37            at the back.
-38        prange : list, optional
-39            List containing the first and last timeslice of the plateau
-40            region indentified for this correlator.
-41        """
-42
-43        if isinstance(data_input, np.ndarray):
+            
29    def __init__(self, data_input, padding=[0, 0], prange=None):
+30        """ Initialize a Corr object.
+31
+32        Parameters
+33        ----------
+34        data_input : list or array
+35            list of Obs or list of arrays of Obs or array of Corrs
+36        padding : list, optional
+37            List with two entries where the first labels the padding
+38            at the front of the correlator and the second the padding
+39            at the back.
+40        prange : list, optional
+41            List containing the first and last timeslice of the plateau
+42            region indentified for this correlator.
+43        """
 44
-45            # This only works, if the array fulfills the conditions below
-46            if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]:
-47                raise Exception("Incompatible array shape")
-48            if not all([isinstance(item, Corr) for item in data_input.flatten()]):
-49                raise Exception("If the input is an array, its elements must be of type pe.Corr")
-50            if not all([item.N == 1 for item in data_input.flatten()]):
-51                raise Exception("Can only construct matrix correlator from single valued correlators")
-52            if not len(set([item.T for item in data_input.flatten()])) == 1:
-53                raise Exception("All input Correlators must be defined over the same timeslices.")
-54
-55            T = data_input[0, 0].T
-56            N = data_input.shape[0]
-57            input_as_list = []
-58            for t in range(T):
-59                if any([(item.content[t] is None) for item in data_input.flatten()]):
-60                    if not all([(item.content[t] is None) for item in data_input.flatten()]):
-61                        warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning)
-62                    input_as_list.append(None)
-63                else:
-64                    array_at_timeslace = np.empty([N, N], dtype="object")
-65                    for i in range(N):
-66                        for j in range(N):
-67                            array_at_timeslace[i, j] = data_input[i, j][t]
-68                    input_as_list.append(array_at_timeslace)
-69            data_input = input_as_list
-70
-71        if isinstance(data_input, list):
+45        if isinstance(data_input, np.ndarray):
+46
+47            # This only works, if the array fulfills the conditions below
+48            if not len(data_input.shape) == 2 and data_input.shape[0] == data_input.shape[1]:
+49                raise Exception("Incompatible array shape")
+50            if not all([isinstance(item, Corr) for item in data_input.flatten()]):
+51                raise Exception("If the input is an array, its elements must be of type pe.Corr")
+52            if not all([item.N == 1 for item in data_input.flatten()]):
+53                raise Exception("Can only construct matrix correlator from single valued correlators")
+54            if not len(set([item.T for item in data_input.flatten()])) == 1:
+55                raise Exception("All input Correlators must be defined over the same timeslices.")
+56
+57            T = data_input[0, 0].T
+58            N = data_input.shape[0]
+59            input_as_list = []
+60            for t in range(T):
+61                if any([(item.content[t] is None) for item in data_input.flatten()]):
+62                    if not all([(item.content[t] is None) for item in data_input.flatten()]):
+63                        warnings.warn("Input ill-defined at different timeslices. Conversion leads to data loss!", RuntimeWarning)
+64                    input_as_list.append(None)
+65                else:
+66                    array_at_timeslace = np.empty([N, N], dtype="object")
+67                    for i in range(N):
+68                        for j in range(N):
+69                            array_at_timeslace[i, j] = data_input[i, j][t]
+70                    input_as_list.append(array_at_timeslace)
+71            data_input = input_as_list
 72
-73            if all([isinstance(item, (Obs, CObs)) or item is None for item in data_input]):
-74                _assert_equal_properties([o for o in data_input if o is not None])
-75                self.content = [np.asarray([item]) if item is not None else None for item in data_input]
-76                self.N = 1
-77
-78            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]):
-79                self.content = data_input
-80                noNull = [a for a in self.content if not (a is None)]  # To check if the matrices are correct for all undefined elements
-81                self.N = noNull[0].shape[0]
-82                if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]:
-83                    raise Exception("Smearing matrices are not NxN")
-84                if (not all([item.shape == noNull[0].shape for item in noNull])):
-85                    raise Exception("Items in data_input are not of identical shape." + str(noNull))
-86            else:
-87                raise Exception("data_input contains item of wrong type")
-88        else:
-89            raise Exception("Data input was not given as list or correct array")
-90
-91        self.tag = None
+73        if isinstance(data_input, list):
+74
+75            if all([isinstance(item, (Obs, CObs)) or item is None for item in data_input]):
+76                _assert_equal_properties([o for o in data_input if o is not None])
+77                self.content = [np.asarray([item]) if item is not None else None for item in data_input]
+78                self.N = 1
+79
+80            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]):
+81                self.content = data_input
+82                noNull = [a for a in self.content if not (a is None)]  # To check if the matrices are correct for all undefined elements
+83                self.N = noNull[0].shape[0]
+84                if self.N > 1 and noNull[0].shape[0] != noNull[0].shape[1]:
+85                    raise Exception("Smearing matrices are not NxN")
+86                if (not all([item.shape == noNull[0].shape for item in noNull])):
+87                    raise Exception("Items in data_input are not of identical shape." + str(noNull))
+88            else:
+89                raise Exception("data_input contains item of wrong type")
+90        else:
+91            raise Exception("Data input was not given as list or correct array")
 92
-93        # An undefined timeslice is represented by the None object
-94        self.content = [None] * padding[0] + self.content + [None] * padding[1]
-95        self.T = len(self.content)
-96        self.prange = prange
+93        self.tag = None
+94
+95        # An undefined timeslice is represented by the None object
+96        self.content = [None] * padding[0] + self.content + [None] * padding[1]
+97        self.T = len(self.content)
+98        self.prange = prange
 
@@ -2938,16 +2942,16 @@ region indentified for this correlator.
-
117    def gamma_method(self, **kwargs):
-118        """Apply the gamma method to the content of the Corr."""
-119        for item in self.content:
-120            if not (item is None):
-121                if self.N == 1:
-122                    item[0].gamma_method(**kwargs)
-123                else:
-124                    for i in range(self.N):
-125                        for j in range(self.N):
-126                            item[i, j].gamma_method(**kwargs)
+            
119    def gamma_method(self, **kwargs):
+120        """Apply the gamma method to the content of the Corr."""
+121        for item in self.content:
+122            if not (item is None):
+123                if self.N == 1:
+124                    item[0].gamma_method(**kwargs)
+125                else:
+126                    for i in range(self.N):
+127                        for j in range(self.N):
+128                            item[i, j].gamma_method(**kwargs)
 
@@ -2967,16 +2971,16 @@ region indentified for this correlator.
-
117    def gamma_method(self, **kwargs):
-118        """Apply the gamma method to the content of the Corr."""
-119        for item in self.content:
-120            if not (item is None):
-121                if self.N == 1:
-122                    item[0].gamma_method(**kwargs)
-123                else:
-124                    for i in range(self.N):
-125                        for j in range(self.N):
-126                            item[i, j].gamma_method(**kwargs)
+            
119    def gamma_method(self, **kwargs):
+120        """Apply the gamma method to the content of the Corr."""
+121        for item in self.content:
+122            if not (item is None):
+123                if self.N == 1:
+124                    item[0].gamma_method(**kwargs)
+125                else:
+126                    for i in range(self.N):
+127                        for j in range(self.N):
+128                            item[i, j].gamma_method(**kwargs)
 
@@ -2996,44 +3000,44 @@ region indentified for this correlator.
-
130    def projected(self, vector_l=None, vector_r=None, normalize=False):
-131        """We need to project the Correlator with a Vector to get a single value at each timeslice.
-132
-133        The method can use one or two vectors.
-134        If two are specified it returns v1@G@v2 (the order might be very important.)
-135        By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to
-136        """
-137        if self.N == 1:
-138            raise Exception("Trying to project a Corr, that already has N=1.")
-139
-140        if vector_l is None:
-141            vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.])
-142        elif (vector_r is None):
-143            vector_r = vector_l
-144        if isinstance(vector_l, list) and not isinstance(vector_r, list):
-145            if len(vector_l) != self.T:
-146                raise Exception("Length of vector list must be equal to T")
-147            vector_r = [vector_r] * self.T
-148        if isinstance(vector_r, list) and not isinstance(vector_l, list):
-149            if len(vector_r) != self.T:
-150                raise Exception("Length of vector list must be equal to T")
-151            vector_l = [vector_l] * self.T
-152
-153        if not isinstance(vector_l, list):
-154            if not vector_l.shape == vector_r.shape == (self.N,):
-155                raise Exception("Vectors are of wrong shape!")
-156            if normalize:
-157                vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r)
-158            newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content]
-159
-160        else:
-161            # There are no checks here yet. There are so many possible scenarios, where this can go wrong.
-162            if normalize:
-163                for t in range(self.T):
-164                    vector_l[t], vector_r[t] = vector_l[t] / np.sqrt((vector_l[t] @ vector_l[t])), vector_r[t] / np.sqrt(vector_r[t] @ vector_r[t])
-165
-166            newcontent = [None if (_check_for_none(self, self.content[t]) or vector_l[t] is None or vector_r[t] is None) else np.asarray([vector_l[t].T @ self.content[t] @ vector_r[t]]) for t in range(self.T)]
-167        return Corr(newcontent)
+            
132    def projected(self, vector_l=None, vector_r=None, normalize=False):
+133        """We need to project the Correlator with a Vector to get a single value at each timeslice.
+134
+135        The method can use one or two vectors.
+136        If two are specified it returns v1@G@v2 (the order might be very important.)
+137        By default it will return the lowest source, which usually means unsmeared-unsmeared (0,0), but it does not have to
+138        """
+139        if self.N == 1:
+140            raise Exception("Trying to project a Corr, that already has N=1.")
+141
+142        if vector_l is None:
+143            vector_l, vector_r = np.asarray([1.] + (self.N - 1) * [0.]), np.asarray([1.] + (self.N - 1) * [0.])
+144        elif (vector_r is None):
+145            vector_r = vector_l
+146        if isinstance(vector_l, list) and not isinstance(vector_r, list):
+147            if len(vector_l) != self.T:
+148                raise Exception("Length of vector list must be equal to T")
+149            vector_r = [vector_r] * self.T
+150        if isinstance(vector_r, list) and not isinstance(vector_l, list):
+151            if len(vector_r) != self.T:
+152                raise Exception("Length of vector list must be equal to T")
+153            vector_l = [vector_l] * self.T
+154
+155        if not isinstance(vector_l, list):
+156            if not vector_l.shape == vector_r.shape == (self.N,):
+157                raise Exception("Vectors are of wrong shape!")
+158            if normalize:
+159                vector_l, vector_r = vector_l / np.sqrt((vector_l @ vector_l)), vector_r / np.sqrt(vector_r @ vector_r)
+160            newcontent = [None if _check_for_none(self, item) else np.asarray([vector_l.T @ item @ vector_r]) for item in self.content]
+161
+162        else:
+163            # There are no checks here yet. There are so many possible scenarios, where this can go wrong.
+164            if normalize:
+165                for t in range(self.T):
+166                    vector_l[t], vector_r[t] = vector_l[t] / np.sqrt((vector_l[t] @ vector_l[t])), vector_r[t] / np.sqrt(vector_r[t] @ vector_r[t])
+167
+168            newcontent = [None if (_check_for_none(self, self.content[t]) or vector_l[t] is None or vector_r[t] is None) else np.asarray([vector_l[t].T @ self.content[t] @ vector_r[t]]) for t in range(self.T)]
+169        return Corr(newcontent)
 
@@ -3057,20 +3061,20 @@ By default it will return the lowest source, which usually means unsmeared-unsme
-
169    def item(self, i, j):
-170        """Picks the element [i,j] from every matrix and returns a correlator containing one Obs per timeslice.
-171
-172        Parameters
-173        ----------
-174        i : int
-175            First index to be picked.
-176        j : int
-177            Second index to be picked.
-178        """
-179        if self.N == 1:
-180            raise Exception("Trying to pick item from projected Corr")
-181        newcontent = [None if (item is None) else item[i, j] for item in self.content]
-182        return Corr(newcontent)
+            
171    def item(self, i, j):
+172        """Picks the element [i,j] from every matrix and returns a correlator containing one Obs per timeslice.
+173
+174        Parameters
+175        ----------
+176        i : int
+177            First index to be picked.
+178        j : int
+179            Second index to be picked.
+180        """
+181        if self.N == 1:
+182            raise Exception("Trying to pick item from projected Corr")
+183        newcontent = [None if (item is None) else item[i, j] for item in self.content]
+184        return Corr(newcontent)
 
@@ -3099,19 +3103,19 @@ Second index to be picked.
-
184    def plottable(self):
-185        """Outputs the correlator in a plotable format.
-186
-187        Outputs three lists containing the timeslice index, the value on each
-188        timeslice and the error on each timeslice.
-189        """
-190        if self.N != 1:
-191            raise Exception("Can only make Corr[N=1] plottable")
-192        x_list = [x for x in range(self.T) if not self.content[x] is None]
-193        y_list = [y[0].value for y in self.content if y is not None]
-194        y_err_list = [y[0].dvalue for y in self.content if y is not None]
-195
-196        return x_list, y_list, y_err_list
+            
186    def plottable(self):
+187        """Outputs the correlator in a plotable format.
+188
+189        Outputs three lists containing the timeslice index, the value on each
+190        timeslice and the error on each timeslice.
+191        """
+192        if self.N != 1:
+193            raise Exception("Can only make Corr[N=1] plottable")
+194        x_list = [x for x in range(self.T) if not self.content[x] is None]
+195        y_list = [y[0].value for y in self.content if y is not None]
+196        y_err_list = [y[0].dvalue for y in self.content if y is not None]
+197
+198        return x_list, y_list, y_err_list
 
@@ -3134,26 +3138,26 @@ timeslice and the error on each timeslice.

-
198    def symmetric(self):
-199        """ Symmetrize the correlator around x0=0."""
-200        if self.N != 1:
-201            raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.')
-202        if self.T % 2 != 0:
-203            raise Exception("Can not symmetrize odd T")
-204
-205        if self.content[0] is not None:
-206            if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0:
-207                warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning)
-208
-209        newcontent = [self.content[0]]
-210        for t in range(1, self.T):
-211            if (self.content[t] is None) or (self.content[self.T - t] is None):
-212                newcontent.append(None)
-213            else:
-214                newcontent.append(0.5 * (self.content[t] + self.content[self.T - t]))
-215        if (all([x is None for x in newcontent])):
-216            raise Exception("Corr could not be symmetrized: No redundant values")
-217        return Corr(newcontent, prange=self.prange)
+            
200    def symmetric(self):
+201        """ Symmetrize the correlator around x0=0."""
+202        if self.N != 1:
+203            raise Exception('symmetric cannot be safely applied to multi-dimensional correlators.')
+204        if self.T % 2 != 0:
+205            raise Exception("Can not symmetrize odd T")
+206
+207        if self.content[0] is not None:
+208            if np.argmax(np.abs([o[0].value if o is not None else 0 for o in self.content])) != 0:
+209                warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning)
+210
+211        newcontent = [self.content[0]]
+212        for t in range(1, self.T):
+213            if (self.content[t] is None) or (self.content[self.T - t] is None):
+214                newcontent.append(None)
+215            else:
+216                newcontent.append(0.5 * (self.content[t] + self.content[self.T - t]))
+217        if (all([x is None for x in newcontent])):
+218            raise Exception("Corr could not be symmetrized: No redundant values")
+219        return Corr(newcontent, prange=self.prange)
 
@@ -3173,27 +3177,27 @@ timeslice and the error on each timeslice.

-
219    def anti_symmetric(self):
-220        """Anti-symmetrize the correlator around x0=0."""
-221        if self.N != 1:
-222            raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.')
-223        if self.T % 2 != 0:
-224            raise Exception("Can not symmetrize odd T")
-225
-226        test = 1 * self
-227        test.gamma_method()
-228        if not all([o.is_zero_within_error(3) for o in test.content[0]]):
-229            warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning)
-230
-231        newcontent = [self.content[0]]
-232        for t in range(1, self.T):
-233            if (self.content[t] is None) or (self.content[self.T - t] is None):
-234                newcontent.append(None)
-235            else:
-236                newcontent.append(0.5 * (self.content[t] - self.content[self.T - t]))
-237        if (all([x is None for x in newcontent])):
-238            raise Exception("Corr could not be symmetrized: No redundant values")
-239        return Corr(newcontent, prange=self.prange)
+            
221    def anti_symmetric(self):
+222        """Anti-symmetrize the correlator around x0=0."""
+223        if self.N != 1:
+224            raise Exception('anti_symmetric cannot be safely applied to multi-dimensional correlators.')
+225        if self.T % 2 != 0:
+226            raise Exception("Can not symmetrize odd T")
+227
+228        test = 1 * self
+229        test.gamma_method()
+230        if not all([o.is_zero_within_error(3) for o in test.content[0]]):
+231            warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning)
+232
+233        newcontent = [self.content[0]]
+234        for t in range(1, self.T):
+235            if (self.content[t] is None) or (self.content[self.T - t] is None):
+236                newcontent.append(None)
+237            else:
+238                newcontent.append(0.5 * (self.content[t] - self.content[self.T - t]))
+239        if (all([x is None for x in newcontent])):
+240            raise Exception("Corr could not be symmetrized: No redundant values")
+241        return Corr(newcontent, prange=self.prange)
 
@@ -3213,20 +3217,20 @@ timeslice and the error on each timeslice.

-
241    def is_matrix_symmetric(self):
-242        """Checks whether a correlator matrices is symmetric on every timeslice."""
-243        if self.N == 1:
-244            raise Exception("Only works for correlator matrices.")
-245        for t in range(self.T):
-246            if self[t] is None:
-247                continue
-248            for i in range(self.N):
-249                for j in range(i + 1, self.N):
-250                    if self[t][i, j] is self[t][j, i]:
-251                        continue
-252                    if hash(self[t][i, j]) != hash(self[t][j, i]):
-253                        return False
-254        return True
+            
243    def is_matrix_symmetric(self):
+244        """Checks whether a correlator matrices is symmetric on every timeslice."""
+245        if self.N == 1:
+246            raise Exception("Only works for correlator matrices.")
+247        for t in range(self.T):
+248            if self[t] is None:
+249                continue
+250            for i in range(self.N):
+251                for j in range(i + 1, self.N):
+252                    if self[t][i, j] is self[t][j, i]:
+253                        continue
+254                    if hash(self[t][i, j]) != hash(self[t][j, i]):
+255                        return False
+256        return True
 
@@ -3246,15 +3250,15 @@ timeslice and the error on each timeslice.

-
256    def matrix_symmetric(self):
-257        """Symmetrizes the correlator matrices on every timeslice."""
-258        if self.N == 1:
-259            raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.")
-260        if self.is_matrix_symmetric():
-261            return 1.0 * self
-262        else:
-263            transposed = [None if _check_for_none(self, G) else G.T for G in self.content]
-264            return 0.5 * (Corr(transposed) + self)
+            
258    def matrix_symmetric(self):
+259        """Symmetrizes the correlator matrices on every timeslice."""
+260        if self.N == 1:
+261            raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.")
+262        if self.is_matrix_symmetric():
+263            return 1.0 * self
+264        else:
+265            transposed = [None if _check_for_none(self, G) else G.T for G in self.content]
+266            return 0.5 * (Corr(transposed) + self)
 
@@ -3274,84 +3278,84 @@ timeslice and the error on each timeslice.

-
266    def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs):
-267        r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors.
-268
-269        The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the
-270        largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing
-271        ```python
-272        C.GEVP(t0=2)[0]  # Ground state vector(s)
-273        C.GEVP(t0=2)[:3]  # Vectors for the lowest three states
-274        ```
-275
-276        Parameters
-277        ----------
-278        t0 : int
-279            The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$
-280        ts : int
-281            fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None.
-282            If sort="Eigenvector" it gives a reference point for the sorting method.
-283        sort : string
-284            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.
-285            - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice.
-286            - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state.
-287              The reference state is identified by its eigenvalue at $t=t_s$.
-288
-289        Other Parameters
-290        ----------------
-291        state : int
-292           Returns only the vector(s) for a specified state. The lowest state is zero.
-293        '''
-294
-295        if self.N == 1:
-296            raise Exception("GEVP methods only works on correlator matrices and not single correlators.")
-297        if ts is not None:
-298            if (ts <= t0):
-299                raise Exception("ts has to be larger than t0.")
-300
-301        if "sorted_list" in kwargs:
-302            warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning)
-303            sort = kwargs.get("sorted_list")
-304
-305        if self.is_matrix_symmetric():
-306            symmetric_corr = self
-307        else:
-308            symmetric_corr = self.matrix_symmetric()
-309
-310        G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0])
-311        np.linalg.cholesky(G0)  # Check if matrix G0 is positive-semidefinite.
-312
-313        if sort is None:
-314            if (ts is None):
-315                raise Exception("ts is required if sort=None.")
-316            if (self.content[t0] is None) or (self.content[ts] is None):
-317                raise Exception("Corr not defined at t0/ts.")
-318            Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts])
-319            reordered_vecs = _GEVP_solver(Gt, G0)
-320
-321        elif sort in ["Eigenvalue", "Eigenvector"]:
-322            if sort == "Eigenvalue" and ts is not None:
-323                warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning)
-324            all_vecs = [None] * (t0 + 1)
-325            for t in range(t0 + 1, self.T):
-326                try:
-327                    Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t])
-328                    all_vecs.append(_GEVP_solver(Gt, G0))
-329                except Exception:
-330                    all_vecs.append(None)
-331            if sort == "Eigenvector":
-332                if ts is None:
-333                    raise Exception("ts is required for the Eigenvector sorting method.")
-334                all_vecs = _sort_vectors(all_vecs, ts)
-335
-336            reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)]
-337        else:
-338            raise Exception("Unkown value for 'sort'.")
-339
-340        if "state" in kwargs:
-341            return reordered_vecs[kwargs.get("state")]
-342        else:
-343            return reordered_vecs
+            
268    def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs):
+269        r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors.
+270
+271        The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the
+272        largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing
+273        ```python
+274        C.GEVP(t0=2)[0]  # Ground state vector(s)
+275        C.GEVP(t0=2)[:3]  # Vectors for the lowest three states
+276        ```
+277
+278        Parameters
+279        ----------
+280        t0 : int
+281            The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$
+282        ts : int
+283            fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None.
+284            If sort="Eigenvector" it gives a reference point for the sorting method.
+285        sort : string
+286            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.
+287            - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice.
+288            - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state.
+289              The reference state is identified by its eigenvalue at $t=t_s$.
+290
+291        Other Parameters
+292        ----------------
+293        state : int
+294           Returns only the vector(s) for a specified state. The lowest state is zero.
+295        '''
+296
+297        if self.N == 1:
+298            raise Exception("GEVP methods only works on correlator matrices and not single correlators.")
+299        if ts is not None:
+300            if (ts <= t0):
+301                raise Exception("ts has to be larger than t0.")
+302
+303        if "sorted_list" in kwargs:
+304            warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning)
+305            sort = kwargs.get("sorted_list")
+306
+307        if self.is_matrix_symmetric():
+308            symmetric_corr = self
+309        else:
+310            symmetric_corr = self.matrix_symmetric()
+311
+312        G0 = np.vectorize(lambda x: x.value)(symmetric_corr[t0])
+313        np.linalg.cholesky(G0)  # Check if matrix G0 is positive-semidefinite.
+314
+315        if sort is None:
+316            if (ts is None):
+317                raise Exception("ts is required if sort=None.")
+318            if (self.content[t0] is None) or (self.content[ts] is None):
+319                raise Exception("Corr not defined at t0/ts.")
+320            Gt = np.vectorize(lambda x: x.value)(symmetric_corr[ts])
+321            reordered_vecs = _GEVP_solver(Gt, G0)
+322
+323        elif sort in ["Eigenvalue", "Eigenvector"]:
+324            if sort == "Eigenvalue" and ts is not None:
+325                warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning)
+326            all_vecs = [None] * (t0 + 1)
+327            for t in range(t0 + 1, self.T):
+328                try:
+329                    Gt = np.vectorize(lambda x: x.value)(symmetric_corr[t])
+330                    all_vecs.append(_GEVP_solver(Gt, G0))
+331                except Exception:
+332                    all_vecs.append(None)
+333            if sort == "Eigenvector":
+334                if ts is None:
+335                    raise Exception("ts is required for the Eigenvector sorting method.")
+336                all_vecs = _sort_vectors(all_vecs, ts)
+337
+338            reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)]
+339        else:
+340            raise Exception("Unkown value for 'sort'.")
+341
+342        if "state" in kwargs:
+343            return reordered_vecs[kwargs.get("state")]
+344        else:
+345            return reordered_vecs
 
@@ -3404,18 +3408,18 @@ Returns only the vector(s) for a specified state. The lowest state is zero.
-
345    def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"):
-346        """Determines the eigenvalue of the GEVP by solving and projecting the correlator
-347
-348        Parameters
-349        ----------
-350        state : int
-351            The state one is interested in ordered by energy. The lowest state is zero.
-352
-353        All other parameters are identical to the ones of Corr.GEVP.
-354        """
-355        vec = self.GEVP(t0, ts=ts, sort=sort)[state]
-356        return self.projected(vec)
+            
347    def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"):
+348        """Determines the eigenvalue of the GEVP by solving and projecting the correlator
+349
+350        Parameters
+351        ----------
+352        state : int
+353            The state one is interested in ordered by energy. The lowest state is zero.
+354
+355        All other parameters are identical to the ones of Corr.GEVP.
+356        """
+357        vec = self.GEVP(t0, ts=ts, sort=sort)[state]
+358        return self.projected(vec)
 
@@ -3443,46 +3447,46 @@ The state one is interested in ordered by energy. The lowest state is zero.
-
358    def Hankel(self, N, periodic=False):
-359        """Constructs an NxN Hankel matrix
-360
-361        C(t) c(t+1) ... c(t+n-1)
-362        C(t+1) c(t+2) ... c(t+n)
-363        .................
-364        C(t+(n-1)) c(t+n) ... c(t+2(n-1))
-365
-366        Parameters
-367        ----------
-368        N : int
-369            Dimension of the Hankel matrix
-370        periodic : bool, optional
-371            determines whether the matrix is extended periodically
-372        """
-373
-374        if self.N != 1:
-375            raise Exception("Multi-operator Prony not implemented!")
-376
-377        array = np.empty([N, N], dtype="object")
-378        new_content = []
-379        for t in range(self.T):
-380            new_content.append(array.copy())
-381
-382        def wrap(i):
-383            while i >= self.T:
-384                i -= self.T
-385            return i
-386
-387        for t in range(self.T):
-388            for i in range(N):
-389                for j in range(N):
-390                    if periodic:
-391                        new_content[t][i, j] = self.content[wrap(t + i + j)][0]
-392                    elif (t + i + j) >= self.T:
-393                        new_content[t] = None
-394                    else:
-395                        new_content[t][i, j] = self.content[t + i + j][0]
-396
-397        return Corr(new_content)
+            
360    def Hankel(self, N, periodic=False):
+361        """Constructs an NxN Hankel matrix
+362
+363        C(t) c(t+1) ... c(t+n-1)
+364        C(t+1) c(t+2) ... c(t+n)
+365        .................
+366        C(t+(n-1)) c(t+n) ... c(t+2(n-1))
+367
+368        Parameters
+369        ----------
+370        N : int
+371            Dimension of the Hankel matrix
+372        periodic : bool, optional
+373            determines whether the matrix is extended periodically
+374        """
+375
+376        if self.N != 1:
+377            raise Exception("Multi-operator Prony not implemented!")
+378
+379        array = np.empty([N, N], dtype="object")
+380        new_content = []
+381        for t in range(self.T):
+382            new_content.append(array.copy())
+383
+384        def wrap(i):
+385            while i >= self.T:
+386                i -= self.T
+387            return i
+388
+389        for t in range(self.T):
+390            for i in range(N):
+391                for j in range(N):
+392                    if periodic:
+393                        new_content[t][i, j] = self.content[wrap(t + i + j)][0]
+394                    elif (t + i + j) >= self.T:
+395                        new_content[t] = None
+396                    else:
+397                        new_content[t][i, j] = self.content[t + i + j][0]
+398
+399        return Corr(new_content)
 
@@ -3516,15 +3520,15 @@ determines whether the matrix is extended periodically
-
399    def roll(self, dt):
-400        """Periodically shift the correlator by dt timeslices
-401
-402        Parameters
-403        ----------
-404        dt : int
-405            number of timeslices
-406        """
-407        return Corr(list(np.roll(np.array(self.content, dtype=object), dt)))
+            
401    def roll(self, dt):
+402        """Periodically shift the correlator by dt timeslices
+403
+404        Parameters
+405        ----------
+406        dt : int
+407            number of timeslices
+408        """
+409        return Corr(list(np.roll(np.array(self.content, dtype=object), dt)))
 
@@ -3551,9 +3555,9 @@ number of timeslices
-
409    def reverse(self):
-410        """Reverse the time ordering of the Corr"""
-411        return Corr(self.content[:: -1])
+            
411    def reverse(self):
+412        """Reverse the time ordering of the Corr"""
+413        return Corr(self.content[:: -1])
 
@@ -3573,23 +3577,23 @@ number of timeslices
-
413    def thin(self, spacing=2, offset=0):
-414        """Thin out a correlator to suppress correlations
-415
-416        Parameters
-417        ----------
-418        spacing : int
-419            Keep only every 'spacing'th entry of the correlator
-420        offset : int
-421            Offset the equal spacing
-422        """
-423        new_content = []
-424        for t in range(self.T):
-425            if (offset + t) % spacing != 0:
-426                new_content.append(None)
-427            else:
-428                new_content.append(self.content[t])
-429        return Corr(new_content)
+            
415    def thin(self, spacing=2, offset=0):
+416        """Thin out a correlator to suppress correlations
+417
+418        Parameters
+419        ----------
+420        spacing : int
+421            Keep only every 'spacing'th entry of the correlator
+422        offset : int
+423            Offset the equal spacing
+424        """
+425        new_content = []
+426        for t in range(self.T):
+427            if (offset + t) % spacing != 0:
+428                new_content.append(None)
+429            else:
+430                new_content.append(self.content[t])
+431        return Corr(new_content)
 
@@ -3618,34 +3622,34 @@ Offset the equal spacing
-
431    def correlate(self, partner):
-432        """Correlate the correlator with another correlator or Obs
-433
-434        Parameters
-435        ----------
-436        partner : Obs or Corr
-437            partner to correlate the correlator with.
-438            Can either be an Obs which is correlated with all entries of the
-439            correlator or a Corr of same length.
-440        """
-441        if self.N != 1:
-442            raise Exception("Only one-dimensional correlators can be safely correlated.")
-443        new_content = []
-444        for x0, t_slice in enumerate(self.content):
-445            if _check_for_none(self, t_slice):
-446                new_content.append(None)
-447            else:
-448                if isinstance(partner, Corr):
-449                    if _check_for_none(partner, partner.content[x0]):
-450                        new_content.append(None)
-451                    else:
-452                        new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice]))
-453                elif isinstance(partner, Obs):  # Should this include CObs?
-454                    new_content.append(np.array([correlate(o, partner) for o in t_slice]))
-455                else:
-456                    raise Exception("Can only correlate with an Obs or a Corr.")
-457
-458        return Corr(new_content)
+            
433    def correlate(self, partner):
+434        """Correlate the correlator with another correlator or Obs
+435
+436        Parameters
+437        ----------
+438        partner : Obs or Corr
+439            partner to correlate the correlator with.
+440            Can either be an Obs which is correlated with all entries of the
+441            correlator or a Corr of same length.
+442        """
+443        if self.N != 1:
+444            raise Exception("Only one-dimensional correlators can be safely correlated.")
+445        new_content = []
+446        for x0, t_slice in enumerate(self.content):
+447            if _check_for_none(self, t_slice):
+448                new_content.append(None)
+449            else:
+450                if isinstance(partner, Corr):
+451                    if _check_for_none(partner, partner.content[x0]):
+452                        new_content.append(None)
+453                    else:
+454                        new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice]))
+455                elif isinstance(partner, Obs):  # Should this include CObs?
+456                    new_content.append(np.array([correlate(o, partner) for o in t_slice]))
+457                else:
+458                    raise Exception("Can only correlate with an Obs or a Corr.")
+459
+460        return Corr(new_content)
 
@@ -3674,28 +3678,28 @@ correlator or a Corr of same length.
-
460    def reweight(self, weight, **kwargs):
-461        """Reweight the correlator.
-462
-463        Parameters
-464        ----------
-465        weight : Obs
-466            Reweighting factor. An Observable that has to be defined on a superset of the
-467            configurations in obs[i].idl for all i.
-468        all_configs : bool
-469            if True, the reweighted observables are normalized by the average of
-470            the reweighting factor on all configurations in weight.idl and not
-471            on the configurations in obs[i].idl.
-472        """
-473        if self.N != 1:
-474            raise Exception("Reweighting only implemented for one-dimensional correlators.")
-475        new_content = []
-476        for t_slice in self.content:
-477            if _check_for_none(self, t_slice):
-478                new_content.append(None)
-479            else:
-480                new_content.append(np.array(reweight(weight, t_slice, **kwargs)))
-481        return Corr(new_content)
+            
462    def reweight(self, weight, **kwargs):
+463        """Reweight the correlator.
+464
+465        Parameters
+466        ----------
+467        weight : Obs
+468            Reweighting factor. An Observable that has to be defined on a superset of the
+469            configurations in obs[i].idl for all i.
+470        all_configs : bool
+471            if True, the reweighted observables are normalized by the average of
+472            the reweighting factor on all configurations in weight.idl and not
+473            on the configurations in obs[i].idl.
+474        """
+475        if self.N != 1:
+476            raise Exception("Reweighting only implemented for one-dimensional correlators.")
+477        new_content = []
+478        for t_slice in self.content:
+479            if _check_for_none(self, t_slice):
+480                new_content.append(None)
+481            else:
+482                new_content.append(np.array(reweight(weight, t_slice, **kwargs)))
+483        return Corr(new_content)
 
@@ -3727,35 +3731,35 @@ on the configurations in obs[i].idl.
-
483    def T_symmetry(self, partner, parity=+1):
-484        """Return the time symmetry average of the correlator and its partner
-485
-486        Parameters
-487        ----------
-488        partner : Corr
-489            Time symmetry partner of the Corr
-490        partity : int
-491            Parity quantum number of the correlator, can be +1 or -1
-492        """
-493        if self.N != 1:
-494            raise Exception("T_symmetry only implemented for one-dimensional correlators.")
-495        if not isinstance(partner, Corr):
-496            raise Exception("T partner has to be a Corr object.")
-497        if parity not in [+1, -1]:
-498            raise Exception("Parity has to be +1 or -1.")
-499        T_partner = parity * partner.reverse()
-500
-501        t_slices = []
-502        test = (self - T_partner)
-503        test.gamma_method()
-504        for x0, t_slice in enumerate(test.content):
-505            if t_slice is not None:
-506                if not t_slice[0].is_zero_within_error(5):
-507                    t_slices.append(x0)
-508        if t_slices:
-509            warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning)
-510
-511        return (self + T_partner) / 2
+            
485    def T_symmetry(self, partner, parity=+1):
+486        """Return the time symmetry average of the correlator and its partner
+487
+488        Parameters
+489        ----------
+490        partner : Corr
+491            Time symmetry partner of the Corr
+492        partity : int
+493            Parity quantum number of the correlator, can be +1 or -1
+494        """
+495        if self.N != 1:
+496            raise Exception("T_symmetry only implemented for one-dimensional correlators.")
+497        if not isinstance(partner, Corr):
+498            raise Exception("T partner has to be a Corr object.")
+499        if parity not in [+1, -1]:
+500            raise Exception("Parity has to be +1 or -1.")
+501        T_partner = parity * partner.reverse()
+502
+503        t_slices = []
+504        test = (self - T_partner)
+505        test.gamma_method()
+506        for x0, t_slice in enumerate(test.content):
+507            if t_slice is not None:
+508                if not t_slice[0].is_zero_within_error(5):
+509                    t_slices.append(x0)
+510        if t_slices:
+511            warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning)
+512
+513        return (self + T_partner) / 2
 
@@ -3784,70 +3788,70 @@ Parity quantum number of the correlator, can be +1 or -1
-
513    def deriv(self, variant="symmetric"):
-514        """Return the first derivative of the correlator with respect to x0.
-515
-516        Parameters
-517        ----------
-518        variant : str
-519            decides which definition of the finite differences derivative is used.
-520            Available choice: symmetric, forward, backward, improved, log, default: symmetric
-521        """
-522        if self.N != 1:
-523            raise Exception("deriv only implemented for one-dimensional correlators.")
-524        if variant == "symmetric":
-525            newcontent = []
-526            for t in range(1, self.T - 1):
-527                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
-528                    newcontent.append(None)
-529                else:
-530                    newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1]))
-531            if (all([x is None for x in newcontent])):
-532                raise Exception('Derivative is undefined at all timeslices')
-533            return Corr(newcontent, padding=[1, 1])
-534        elif variant == "forward":
-535            newcontent = []
-536            for t in range(self.T - 1):
-537                if (self.content[t] is None) or (self.content[t + 1] is None):
-538                    newcontent.append(None)
-539                else:
-540                    newcontent.append(self.content[t + 1] - self.content[t])
-541            if (all([x is None for x in newcontent])):
-542                raise Exception("Derivative is undefined at all timeslices")
-543            return Corr(newcontent, padding=[0, 1])
-544        elif variant == "backward":
-545            newcontent = []
-546            for t in range(1, self.T):
-547                if (self.content[t - 1] is None) or (self.content[t] is None):
-548                    newcontent.append(None)
-549                else:
-550                    newcontent.append(self.content[t] - self.content[t - 1])
-551            if (all([x is None for x in newcontent])):
-552                raise Exception("Derivative is undefined at all timeslices")
-553            return Corr(newcontent, padding=[1, 0])
-554        elif variant == "improved":
-555            newcontent = []
-556            for t in range(2, self.T - 2):
-557                if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None):
-558                    newcontent.append(None)
-559                else:
-560                    newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2]))
-561            if (all([x is None for x in newcontent])):
-562                raise Exception('Derivative is undefined at all timeslices')
-563            return Corr(newcontent, padding=[2, 2])
-564        elif variant == 'log':
-565            newcontent = []
-566            for t in range(self.T):
-567                if (self.content[t] is None) or (self.content[t] <= 0):
-568                    newcontent.append(None)
-569                else:
-570                    newcontent.append(np.log(self.content[t]))
-571            if (all([x is None for x in newcontent])):
-572                raise Exception("Log is undefined at all timeslices")
-573            logcorr = Corr(newcontent)
-574            return self * logcorr.deriv('symmetric')
-575        else:
-576            raise Exception("Unknown variant.")
+            
515    def deriv(self, variant="symmetric"):
+516        """Return the first derivative of the correlator with respect to x0.
+517
+518        Parameters
+519        ----------
+520        variant : str
+521            decides which definition of the finite differences derivative is used.
+522            Available choice: symmetric, forward, backward, improved, log, default: symmetric
+523        """
+524        if self.N != 1:
+525            raise Exception("deriv only implemented for one-dimensional correlators.")
+526        if variant == "symmetric":
+527            newcontent = []
+528            for t in range(1, self.T - 1):
+529                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
+530                    newcontent.append(None)
+531                else:
+532                    newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1]))
+533            if (all([x is None for x in newcontent])):
+534                raise Exception('Derivative is undefined at all timeslices')
+535            return Corr(newcontent, padding=[1, 1])
+536        elif variant == "forward":
+537            newcontent = []
+538            for t in range(self.T - 1):
+539                if (self.content[t] is None) or (self.content[t + 1] is None):
+540                    newcontent.append(None)
+541                else:
+542                    newcontent.append(self.content[t + 1] - self.content[t])
+543            if (all([x is None for x in newcontent])):
+544                raise Exception("Derivative is undefined at all timeslices")
+545            return Corr(newcontent, padding=[0, 1])
+546        elif variant == "backward":
+547            newcontent = []
+548            for t in range(1, self.T):
+549                if (self.content[t - 1] is None) or (self.content[t] is None):
+550                    newcontent.append(None)
+551                else:
+552                    newcontent.append(self.content[t] - self.content[t - 1])
+553            if (all([x is None for x in newcontent])):
+554                raise Exception("Derivative is undefined at all timeslices")
+555            return Corr(newcontent, padding=[1, 0])
+556        elif variant == "improved":
+557            newcontent = []
+558            for t in range(2, self.T - 2):
+559                if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None):
+560                    newcontent.append(None)
+561                else:
+562                    newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2]))
+563            if (all([x is None for x in newcontent])):
+564                raise Exception('Derivative is undefined at all timeslices')
+565            return Corr(newcontent, padding=[2, 2])
+566        elif variant == 'log':
+567            newcontent = []
+568            for t in range(self.T):
+569                if (self.content[t] is None) or (self.content[t] <= 0):
+570                    newcontent.append(None)
+571                else:
+572                    newcontent.append(np.log(self.content[t]))
+573            if (all([x is None for x in newcontent])):
+574                raise Exception("Log is undefined at all timeslices")
+575            logcorr = Corr(newcontent)
+576            return self * logcorr.deriv('symmetric')
+577        else:
+578            raise Exception("Unknown variant.")
 
@@ -3875,50 +3879,50 @@ Available choice: symmetric, forward, backward, improved, log, default: symmetri
-
578    def second_deriv(self, variant="symmetric"):
-579        """Return the second derivative of the correlator with respect to x0.
-580
-581        Parameters
-582        ----------
-583        variant : str
-584            decides which definition of the finite differences derivative is used.
-585            Available choice: symmetric, improved, log, default: symmetric
-586        """
-587        if self.N != 1:
-588            raise Exception("second_deriv only implemented for one-dimensional correlators.")
-589        if variant == "symmetric":
-590            newcontent = []
-591            for t in range(1, self.T - 1):
-592                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
-593                    newcontent.append(None)
-594                else:
-595                    newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1]))
-596            if (all([x is None for x in newcontent])):
-597                raise Exception("Derivative is undefined at all timeslices")
-598            return Corr(newcontent, padding=[1, 1])
-599        elif variant == "improved":
-600            newcontent = []
-601            for t in range(2, self.T - 2):
-602                if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None):
-603                    newcontent.append(None)
-604                else:
-605                    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]))
-606            if (all([x is None for x in newcontent])):
-607                raise Exception("Derivative is undefined at all timeslices")
-608            return Corr(newcontent, padding=[2, 2])
-609        elif variant == 'log':
-610            newcontent = []
-611            for t in range(self.T):
-612                if (self.content[t] is None) or (self.content[t] <= 0):
-613                    newcontent.append(None)
-614                else:
-615                    newcontent.append(np.log(self.content[t]))
-616            if (all([x is None for x in newcontent])):
-617                raise Exception("Log is undefined at all timeslices")
-618            logcorr = Corr(newcontent)
-619            return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2)
-620        else:
-621            raise Exception("Unknown variant.")
+            
580    def second_deriv(self, variant="symmetric"):
+581        """Return the second derivative of the correlator with respect to x0.
+582
+583        Parameters
+584        ----------
+585        variant : str
+586            decides which definition of the finite differences derivative is used.
+587            Available choice: symmetric, improved, log, default: symmetric
+588        """
+589        if self.N != 1:
+590            raise Exception("second_deriv only implemented for one-dimensional correlators.")
+591        if variant == "symmetric":
+592            newcontent = []
+593            for t in range(1, self.T - 1):
+594                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
+595                    newcontent.append(None)
+596                else:
+597                    newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1]))
+598            if (all([x is None for x in newcontent])):
+599                raise Exception("Derivative is undefined at all timeslices")
+600            return Corr(newcontent, padding=[1, 1])
+601        elif variant == "improved":
+602            newcontent = []
+603            for t in range(2, self.T - 2):
+604                if (self.content[t - 2] is None) or (self.content[t - 1] is None) or (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 2] is None):
+605                    newcontent.append(None)
+606                else:
+607                    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]))
+608            if (all([x is None for x in newcontent])):
+609                raise Exception("Derivative is undefined at all timeslices")
+610            return Corr(newcontent, padding=[2, 2])
+611        elif variant == 'log':
+612            newcontent = []
+613            for t in range(self.T):
+614                if (self.content[t] is None) or (self.content[t] <= 0):
+615                    newcontent.append(None)
+616                else:
+617                    newcontent.append(np.log(self.content[t]))
+618            if (all([x is None for x in newcontent])):
+619                raise Exception("Log is undefined at all timeslices")
+620            logcorr = Corr(newcontent)
+621            return self * (logcorr.second_deriv('symmetric') + (logcorr.deriv('symmetric'))**2)
+622        else:
+623            raise Exception("Unknown variant.")
 
@@ -3946,89 +3950,89 @@ Available choice: symmetric, improved, log, default: symmetric
-
623    def m_eff(self, variant='log', guess=1.0):
-624        """Returns the effective mass of the correlator as correlator object
-625
-626        Parameters
-627        ----------
-628        variant : str
-629            log : uses the standard effective mass log(C(t) / C(t+1))
-630            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.
-631            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.
-632            See, e.g., arXiv:1205.5380
-633            arccosh : Uses the explicit form of the symmetrized correlator (not recommended)
-634            logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2
-635        guess : float
-636            guess for the root finder, only relevant for the root variant
-637        """
-638        if self.N != 1:
-639            raise Exception('Correlator must be projected before getting m_eff')
-640        if variant == 'log':
-641            newcontent = []
-642            for t in range(self.T - 1):
-643                if ((self.content[t] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0):
-644                    newcontent.append(None)
-645                elif self.content[t][0].value / self.content[t + 1][0].value < 0:
+            
625    def m_eff(self, variant='log', guess=1.0):
+626        """Returns the effective mass of the correlator as correlator object
+627
+628        Parameters
+629        ----------
+630        variant : str
+631            log : uses the standard effective mass log(C(t) / C(t+1))
+632            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.
+633            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.
+634            See, e.g., arXiv:1205.5380
+635            arccosh : Uses the explicit form of the symmetrized correlator (not recommended)
+636            logsym: uses the symmetric effective mass log(C(t-1) / C(t+1))/2
+637        guess : float
+638            guess for the root finder, only relevant for the root variant
+639        """
+640        if self.N != 1:
+641            raise Exception('Correlator must be projected before getting m_eff')
+642        if variant == 'log':
+643            newcontent = []
+644            for t in range(self.T - 1):
+645                if ((self.content[t] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0):
 646                    newcontent.append(None)
-647                else:
-648                    newcontent.append(self.content[t] / self.content[t + 1])
-649            if (all([x is None for x in newcontent])):
-650                raise Exception('m_eff is undefined at all timeslices')
-651
-652            return np.log(Corr(newcontent, padding=[0, 1]))
+647                elif self.content[t][0].value / self.content[t + 1][0].value < 0:
+648                    newcontent.append(None)
+649                else:
+650                    newcontent.append(self.content[t] / self.content[t + 1])
+651            if (all([x is None for x in newcontent])):
+652                raise Exception('m_eff is undefined at all timeslices')
 653
-654        elif variant == 'logsym':
-655            newcontent = []
-656            for t in range(1, self.T - 1):
-657                if ((self.content[t - 1] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0):
-658                    newcontent.append(None)
-659                elif self.content[t - 1][0].value / self.content[t + 1][0].value < 0:
+654            return np.log(Corr(newcontent, padding=[0, 1]))
+655
+656        elif variant == 'logsym':
+657            newcontent = []
+658            for t in range(1, self.T - 1):
+659                if ((self.content[t - 1] is None) or (self.content[t + 1] is None)) or (self.content[t + 1][0].value == 0):
 660                    newcontent.append(None)
-661                else:
-662                    newcontent.append(self.content[t - 1] / self.content[t + 1])
-663            if (all([x is None for x in newcontent])):
-664                raise Exception('m_eff is undefined at all timeslices')
-665
-666            return np.log(Corr(newcontent, padding=[1, 1])) / 2
+661                elif self.content[t - 1][0].value / self.content[t + 1][0].value < 0:
+662                    newcontent.append(None)
+663                else:
+664                    newcontent.append(self.content[t - 1] / self.content[t + 1])
+665            if (all([x is None for x in newcontent])):
+666                raise Exception('m_eff is undefined at all timeslices')
 667
-668        elif variant in ['periodic', 'cosh', 'sinh']:
-669            if variant in ['periodic', 'cosh']:
-670                func = anp.cosh
-671            else:
-672                func = anp.sinh
-673
-674            def root_function(x, d):
-675                return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d
-676
-677            newcontent = []
-678            for t in range(self.T - 1):
-679                if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 1][0].value == 0):
-680                    newcontent.append(None)
-681                # Fill the two timeslices in the middle of the lattice with their predecessors
-682                elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]:
-683                    newcontent.append(newcontent[-1])
-684                elif self.content[t][0].value / self.content[t + 1][0].value < 0:
-685                    newcontent.append(None)
-686                else:
-687                    newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess)))
-688            if (all([x is None for x in newcontent])):
-689                raise Exception('m_eff is undefined at all timeslices')
-690
-691            return Corr(newcontent, padding=[0, 1])
+668            return np.log(Corr(newcontent, padding=[1, 1])) / 2
+669
+670        elif variant in ['periodic', 'cosh', 'sinh']:
+671            if variant in ['periodic', 'cosh']:
+672                func = anp.cosh
+673            else:
+674                func = anp.sinh
+675
+676            def root_function(x, d):
+677                return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d
+678
+679            newcontent = []
+680            for t in range(self.T - 1):
+681                if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t + 1][0].value == 0):
+682                    newcontent.append(None)
+683                # Fill the two timeslices in the middle of the lattice with their predecessors
+684                elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]:
+685                    newcontent.append(newcontent[-1])
+686                elif self.content[t][0].value / self.content[t + 1][0].value < 0:
+687                    newcontent.append(None)
+688                else:
+689                    newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess)))
+690            if (all([x is None for x in newcontent])):
+691                raise Exception('m_eff is undefined at all timeslices')
 692
-693        elif variant == 'arccosh':
-694            newcontent = []
-695            for t in range(1, self.T - 1):
-696                if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None) or (self.content[t][0].value == 0):
-697                    newcontent.append(None)
-698                else:
-699                    newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t]))
-700            if (all([x is None for x in newcontent])):
-701                raise Exception("m_eff is undefined at all timeslices")
-702            return np.arccosh(Corr(newcontent, padding=[1, 1]))
-703
-704        else:
-705            raise Exception('Unknown variant.')
+693            return Corr(newcontent, padding=[0, 1])
+694
+695        elif variant == 'arccosh':
+696            newcontent = []
+697            for t in range(1, self.T - 1):
+698                if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None) or (self.content[t][0].value == 0):
+699                    newcontent.append(None)
+700                else:
+701                    newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t]))
+702            if (all([x is None for x in newcontent])):
+703                raise Exception("m_eff is undefined at all timeslices")
+704            return np.arccosh(Corr(newcontent, padding=[1, 1]))
+705
+706        else:
+707            raise Exception('Unknown variant.')
 
@@ -4062,39 +4066,39 @@ guess for the root finder, only relevant for the root variant
-
707    def fit(self, function, fitrange=None, silent=False, **kwargs):
-708        r'''Fits function to the data
-709
-710        Parameters
-711        ----------
-712        function : obj
-713            function to fit to the data. See fits.least_squares for details.
-714        fitrange : list
-715            Two element list containing the timeslices on which the fit is supposed to start and stop.
-716            Caution: This range is inclusive as opposed to standard python indexing.
-717            `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6.
-718            If not specified, self.prange or all timeslices are used.
-719        silent : bool
-720            Decides whether output is printed to the standard output.
-721        '''
-722        if self.N != 1:
-723            raise Exception("Correlator must be projected before fitting")
-724
-725        if fitrange is None:
-726            if self.prange:
-727                fitrange = self.prange
-728            else:
-729                fitrange = [0, self.T - 1]
-730        else:
-731            if not isinstance(fitrange, list):
-732                raise Exception("fitrange has to be a list with two elements")
-733            if len(fitrange) != 2:
-734                raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]")
-735
-736        xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
-737        ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
-738        result = least_squares(xs, ys, function, silent=silent, **kwargs)
-739        return result
+            
709    def fit(self, function, fitrange=None, silent=False, **kwargs):
+710        r'''Fits function to the data
+711
+712        Parameters
+713        ----------
+714        function : obj
+715            function to fit to the data. See fits.least_squares for details.
+716        fitrange : list
+717            Two element list containing the timeslices on which the fit is supposed to start and stop.
+718            Caution: This range is inclusive as opposed to standard python indexing.
+719            `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6.
+720            If not specified, self.prange or all timeslices are used.
+721        silent : bool
+722            Decides whether output is printed to the standard output.
+723        '''
+724        if self.N != 1:
+725            raise Exception("Correlator must be projected before fitting")
+726
+727        if fitrange is None:
+728            if self.prange:
+729                fitrange = self.prange
+730            else:
+731                fitrange = [0, self.T - 1]
+732        else:
+733            if not isinstance(fitrange, list):
+734                raise Exception("fitrange has to be a list with two elements")
+735            if len(fitrange) != 2:
+736                raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]")
+737
+738        xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
+739        ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
+740        result = least_squares(xs, ys, function, silent=silent, **kwargs)
+741        return result
 
@@ -4128,42 +4132,42 @@ Decides whether output is printed to the standard output.
-
741    def plateau(self, plateau_range=None, method="fit", auto_gamma=False):
-742        """ Extract a plateau value from a Corr object
-743
-744        Parameters
-745        ----------
-746        plateau_range : list
-747            list with two entries, indicating the first and the last timeslice
-748            of the plateau region.
-749        method : str
-750            method to extract the plateau.
-751                'fit' fits a constant to the plateau region
-752                'avg', 'average' or 'mean' just average over the given timeslices.
-753        auto_gamma : bool
-754            apply gamma_method with default parameters to the Corr. Defaults to None
-755        """
-756        if not plateau_range:
-757            if self.prange:
-758                plateau_range = self.prange
-759            else:
-760                raise Exception("no plateau range provided")
-761        if self.N != 1:
-762            raise Exception("Correlator must be projected before getting a plateau.")
-763        if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])):
-764            raise Exception("plateau is undefined at all timeslices in plateaurange.")
-765        if auto_gamma:
-766            self.gamma_method()
-767        if method == "fit":
-768            def const_func(a, t):
-769                return a[0]
-770            return self.fit(const_func, plateau_range)[0]
-771        elif method in ["avg", "average", "mean"]:
-772            returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None])
-773            return returnvalue
-774
-775        else:
-776            raise Exception("Unsupported plateau method: " + method)
+            
743    def plateau(self, plateau_range=None, method="fit", auto_gamma=False):
+744        """ Extract a plateau value from a Corr object
+745
+746        Parameters
+747        ----------
+748        plateau_range : list
+749            list with two entries, indicating the first and the last timeslice
+750            of the plateau region.
+751        method : str
+752            method to extract the plateau.
+753                'fit' fits a constant to the plateau region
+754                'avg', 'average' or 'mean' just average over the given timeslices.
+755        auto_gamma : bool
+756            apply gamma_method with default parameters to the Corr. Defaults to None
+757        """
+758        if not plateau_range:
+759            if self.prange:
+760                plateau_range = self.prange
+761            else:
+762                raise Exception("no plateau range provided")
+763        if self.N != 1:
+764            raise Exception("Correlator must be projected before getting a plateau.")
+765        if (all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])):
+766            raise Exception("plateau is undefined at all timeslices in plateaurange.")
+767        if auto_gamma:
+768            self.gamma_method()
+769        if method == "fit":
+770            def const_func(a, t):
+771                return a[0]
+772            return self.fit(const_func, plateau_range)[0]
+773        elif method in ["avg", "average", "mean"]:
+774            returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None])
+775            return returnvalue
+776
+777        else:
+778            raise Exception("Unsupported plateau method: " + method)
 
@@ -4197,17 +4201,17 @@ apply gamma_method with default parameters to the Corr. Defaults to None
-
778    def set_prange(self, prange):
-779        """Sets the attribute prange of the Corr object."""
-780        if not len(prange) == 2:
-781            raise Exception("prange must be a list or array with two values")
-782        if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))):
-783            raise Exception("Start and end point must be integers")
-784        if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]):
-785            raise Exception("Start and end point must define a range in the interval 0,T")
-786
-787        self.prange = prange
-788        return
+            
780    def set_prange(self, prange):
+781        """Sets the attribute prange of the Corr object."""
+782        if not len(prange) == 2:
+783            raise Exception("prange must be a list or array with two values")
+784        if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))):
+785            raise Exception("Start and end point must be integers")
+786        if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]):
+787            raise Exception("Start and end point must define a range in the interval 0,T")
+788
+789        self.prange = prange
+790        return
 
@@ -4227,124 +4231,124 @@ apply gamma_method with default parameters to the Corr. Defaults to None
-
790    def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None):
-791        """Plots the correlator using the tag of the correlator as label if available.
-792
-793        Parameters
-794        ----------
-795        x_range : list
-796            list of two values, determining the range of the x-axis e.g. [4, 8].
-797        comp : Corr or list of Corr
-798            Correlator or list of correlators which are plotted for comparison.
-799            The tags of these correlators are used as labels if available.
-800        logscale : bool
-801            Sets y-axis to logscale.
-802        plateau : Obs
-803            Plateau value to be visualized in the figure.
-804        fit_res : Fit_result
-805            Fit_result object to be visualized.
-806        ylabel : str
-807            Label for the y-axis.
-808        save : str
-809            path to file in which the figure should be saved.
-810        auto_gamma : bool
-811            Apply the gamma method with standard parameters to all correlators and plateau values before plotting.
-812        hide_sigma : float
-813            Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors.
-814        references : list
-815            List of floating point values that are displayed as horizontal lines for reference.
-816        title : string
-817            Optional title of the figure.
-818        """
-819        if self.N != 1:
-820            raise Exception("Correlator must be projected before plotting")
-821
-822        if auto_gamma:
-823            self.gamma_method()
-824
-825        if x_range is None:
-826            x_range = [0, self.T - 1]
-827
-828        fig = plt.figure()
-829        ax1 = fig.add_subplot(111)
-830
-831        x, y, y_err = self.plottable()
-832        if hide_sigma:
-833            hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
-834        else:
-835            hide_from = None
-836        ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag)
-837        if logscale:
-838            ax1.set_yscale('log')
-839        else:
-840            if y_range is None:
-841                try:
-842                    y_min = min([(x[0].value - x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)])
-843                    y_max = max([(x[0].value + x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)])
-844                    ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)])
-845                except Exception:
-846                    pass
-847            else:
-848                ax1.set_ylim(y_range)
-849        if comp:
-850            if isinstance(comp, (Corr, list)):
-851                for corr in comp if isinstance(comp, list) else [comp]:
-852                    if auto_gamma:
-853                        corr.gamma_method()
-854                    x, y, y_err = corr.plottable()
-855                    if hide_sigma:
-856                        hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
-857                    else:
-858                        hide_from = None
-859                    ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor'])
-860            else:
-861                raise Exception("'comp' must be a correlator or a list of correlators.")
-862
-863        if plateau:
-864            if isinstance(plateau, Obs):
-865                if auto_gamma:
-866                    plateau.gamma_method()
-867                ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau))
-868                ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-')
-869            else:
-870                raise Exception("'plateau' must be an Obs")
-871
-872        if references:
-873            if isinstance(references, list):
-874                for ref in references:
-875                    ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--')
-876            else:
-877                raise Exception("'references' must be a list of floating pint values.")
-878
-879        if self.prange:
-880            ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',')
-881            ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',')
-882
-883        if fit_res:
-884            x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05)
-885            ax1.plot(x_samples,
-886                     fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples),
-887                     ls='-', marker=',', lw=2)
-888
-889        ax1.set_xlabel(r'$x_0 / a$')
-890        if ylabel:
-891            ax1.set_ylabel(ylabel)
-892        ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5])
-893
-894        handles, labels = ax1.get_legend_handles_labels()
-895        if labels:
-896            ax1.legend()
-897
-898        if title:
-899            plt.title(title)
-900
-901        plt.draw()
+            
792    def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None):
+793        """Plots the correlator using the tag of the correlator as label if available.
+794
+795        Parameters
+796        ----------
+797        x_range : list
+798            list of two values, determining the range of the x-axis e.g. [4, 8].
+799        comp : Corr or list of Corr
+800            Correlator or list of correlators which are plotted for comparison.
+801            The tags of these correlators are used as labels if available.
+802        logscale : bool
+803            Sets y-axis to logscale.
+804        plateau : Obs
+805            Plateau value to be visualized in the figure.
+806        fit_res : Fit_result
+807            Fit_result object to be visualized.
+808        ylabel : str
+809            Label for the y-axis.
+810        save : str
+811            path to file in which the figure should be saved.
+812        auto_gamma : bool
+813            Apply the gamma method with standard parameters to all correlators and plateau values before plotting.
+814        hide_sigma : float
+815            Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors.
+816        references : list
+817            List of floating point values that are displayed as horizontal lines for reference.
+818        title : string
+819            Optional title of the figure.
+820        """
+821        if self.N != 1:
+822            raise Exception("Correlator must be projected before plotting")
+823
+824        if auto_gamma:
+825            self.gamma_method()
+826
+827        if x_range is None:
+828            x_range = [0, self.T - 1]
+829
+830        fig = plt.figure()
+831        ax1 = fig.add_subplot(111)
+832
+833        x, y, y_err = self.plottable()
+834        if hide_sigma:
+835            hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
+836        else:
+837            hide_from = None
+838        ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag)
+839        if logscale:
+840            ax1.set_yscale('log')
+841        else:
+842            if y_range is None:
+843                try:
+844                    y_min = min([(x[0].value - x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)])
+845                    y_max = max([(x[0].value + x[0].dvalue) for x in self.content[x_range[0]: x_range[1] + 1] if (x is not None) and x[0].dvalue < 2 * np.abs(x[0].value)])
+846                    ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)])
+847                except Exception:
+848                    pass
+849            else:
+850                ax1.set_ylim(y_range)
+851        if comp:
+852            if isinstance(comp, (Corr, list)):
+853                for corr in comp if isinstance(comp, list) else [comp]:
+854                    if auto_gamma:
+855                        corr.gamma_method()
+856                    x, y, y_err = corr.plottable()
+857                    if hide_sigma:
+858                        hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
+859                    else:
+860                        hide_from = None
+861                    ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor'])
+862            else:
+863                raise Exception("'comp' must be a correlator or a list of correlators.")
+864
+865        if plateau:
+866            if isinstance(plateau, Obs):
+867                if auto_gamma:
+868                    plateau.gamma_method()
+869                ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau))
+870                ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-')
+871            else:
+872                raise Exception("'plateau' must be an Obs")
+873
+874        if references:
+875            if isinstance(references, list):
+876                for ref in references:
+877                    ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--')
+878            else:
+879                raise Exception("'references' must be a list of floating pint values.")
+880
+881        if self.prange:
+882            ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',')
+883            ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',')
+884
+885        if fit_res:
+886            x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05)
+887            ax1.plot(x_samples,
+888                     fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples),
+889                     ls='-', marker=',', lw=2)
+890
+891        ax1.set_xlabel(r'$x_0 / a$')
+892        if ylabel:
+893            ax1.set_ylabel(ylabel)
+894        ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5])
+895
+896        handles, labels = ax1.get_legend_handles_labels()
+897        if labels:
+898            ax1.legend()
+899
+900        if title:
+901            plt.title(title)
 902
-903        if save:
-904            if isinstance(save, str):
-905                fig.savefig(save, bbox_inches='tight')
-906            else:
-907                raise Exception("'save' has to be a string.")
+903        plt.draw()
+904
+905        if save:
+906            if isinstance(save, str):
+907                fig.savefig(save, bbox_inches='tight')
+908            else:
+909                raise Exception("'save' has to be a string.")
 
@@ -4392,34 +4396,34 @@ Optional title of the figure.
-
909    def spaghetti_plot(self, logscale=True):
-910        """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations.
-911
-912        Parameters
-913        ----------
-914        logscale : bool
-915            Determines whether the scale of the y-axis is logarithmic or standard.
-916        """
-917        if self.N != 1:
-918            raise Exception("Correlator needs to be projected first.")
-919
-920        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]))
-921        x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None]
-922
-923        for name in mc_names:
-924            data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T
-925
-926            fig = plt.figure()
-927            ax = fig.add_subplot(111)
-928            for dat in data:
-929                ax.plot(x0_vals, dat, ls='-', marker='')
-930
-931            if logscale is True:
-932                ax.set_yscale('log')
-933
-934            ax.set_xlabel(r'$x_0 / a$')
-935            plt.title(name)
-936            plt.draw()
+            
911    def spaghetti_plot(self, logscale=True):
+912        """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations.
+913
+914        Parameters
+915        ----------
+916        logscale : bool
+917            Determines whether the scale of the y-axis is logarithmic or standard.
+918        """
+919        if self.N != 1:
+920            raise Exception("Correlator needs to be projected first.")
+921
+922        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]))
+923        x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None]
+924
+925        for name in mc_names:
+926            data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T
+927
+928            fig = plt.figure()
+929            ax = fig.add_subplot(111)
+930            for dat in data:
+931                ax.plot(x0_vals, dat, ls='-', marker='')
+932
+933            if logscale is True:
+934                ax.set_yscale('log')
+935
+936            ax.set_xlabel(r'$x_0 / a$')
+937            plt.title(name)
+938            plt.draw()
 
@@ -4446,29 +4450,29 @@ Determines whether the scale of the y-axis is logarithmic or standard.
-
938    def dump(self, filename, datatype="json.gz", **kwargs):
-939        """Dumps the Corr into a file of chosen type
-940        Parameters
-941        ----------
-942        filename : str
-943            Name of the file to be saved.
-944        datatype : str
-945            Format of the exported file. Supported formats include
-946            "json.gz" and "pickle"
-947        path : str
-948            specifies a custom path for the file (default '.')
-949        """
-950        if datatype == "json.gz":
-951            from .input.json import dump_to_json
-952            if 'path' in kwargs:
-953                file_name = kwargs.get('path') + '/' + filename
-954            else:
-955                file_name = filename
-956            dump_to_json(self, file_name)
-957        elif datatype == "pickle":
-958            dump_object(self, filename, **kwargs)
-959        else:
-960            raise Exception("Unknown datatype " + str(datatype))
+            
940    def dump(self, filename, datatype="json.gz", **kwargs):
+941        """Dumps the Corr into a file of chosen type
+942        Parameters
+943        ----------
+944        filename : str
+945            Name of the file to be saved.
+946        datatype : str
+947            Format of the exported file. Supported formats include
+948            "json.gz" and "pickle"
+949        path : str
+950            specifies a custom path for the file (default '.')
+951        """
+952        if datatype == "json.gz":
+953            from .input.json import dump_to_json
+954            if 'path' in kwargs:
+955                file_name = kwargs.get('path') + '/' + filename
+956            else:
+957                file_name = filename
+958            dump_to_json(self, file_name)
+959        elif datatype == "pickle":
+960            dump_object(self, filename, **kwargs)
+961        else:
+962            raise Exception("Unknown datatype " + str(datatype))
 
@@ -4500,8 +4504,8 @@ specifies a custom path for the file (default '.')
-
962    def print(self, print_range=None):
-963        print(self.__repr__(print_range))
+            
964    def print(self, print_range=None):
+965        print(self.__repr__(print_range))
 
@@ -4519,8 +4523,8 @@ specifies a custom path for the file (default '.')
-
1129    def sqrt(self):
-1130        return self ** 0.5
+            
1131    def sqrt(self):
+1132        return self ** 0.5
 
@@ -4538,9 +4542,9 @@ specifies a custom path for the file (default '.')
-
1132    def log(self):
-1133        newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content]
-1134        return Corr(newcontent, prange=self.prange)
+            
1134    def log(self):
+1135        newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content]
+1136        return Corr(newcontent, prange=self.prange)
 
@@ -4558,9 +4562,9 @@ specifies a custom path for the file (default '.')
-
1136    def exp(self):
-1137        newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content]
-1138        return Corr(newcontent, prange=self.prange)
+            
1138    def exp(self):
+1139        newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content]
+1140        return Corr(newcontent, prange=self.prange)
 
@@ -4578,8 +4582,8 @@ specifies a custom path for the file (default '.')
-
1153    def sin(self):
-1154        return self._apply_func_to_corr(np.sin)
+            
1155    def sin(self):
+1156        return self._apply_func_to_corr(np.sin)
 
@@ -4597,8 +4601,8 @@ specifies a custom path for the file (default '.')
-
1156    def cos(self):
-1157        return self._apply_func_to_corr(np.cos)
+            
1158    def cos(self):
+1159        return self._apply_func_to_corr(np.cos)
 
@@ -4616,8 +4620,8 @@ specifies a custom path for the file (default '.')
-
1159    def tan(self):
-1160        return self._apply_func_to_corr(np.tan)
+            
1161    def tan(self):
+1162        return self._apply_func_to_corr(np.tan)
 
@@ -4635,8 +4639,8 @@ specifies a custom path for the file (default '.')
-
1162    def sinh(self):
-1163        return self._apply_func_to_corr(np.sinh)
+            
1164    def sinh(self):
+1165        return self._apply_func_to_corr(np.sinh)
 
@@ -4654,8 +4658,8 @@ specifies a custom path for the file (default '.')
-
1165    def cosh(self):
-1166        return self._apply_func_to_corr(np.cosh)
+            
1167    def cosh(self):
+1168        return self._apply_func_to_corr(np.cosh)
 
@@ -4673,8 +4677,8 @@ specifies a custom path for the file (default '.')
-
1168    def tanh(self):
-1169        return self._apply_func_to_corr(np.tanh)
+            
1170    def tanh(self):
+1171        return self._apply_func_to_corr(np.tanh)
 
@@ -4692,8 +4696,8 @@ specifies a custom path for the file (default '.')
-
1171    def arcsin(self):
-1172        return self._apply_func_to_corr(np.arcsin)
+            
1173    def arcsin(self):
+1174        return self._apply_func_to_corr(np.arcsin)
 
@@ -4711,8 +4715,8 @@ specifies a custom path for the file (default '.')
-
1174    def arccos(self):
-1175        return self._apply_func_to_corr(np.arccos)
+            
1176    def arccos(self):
+1177        return self._apply_func_to_corr(np.arccos)
 
@@ -4730,8 +4734,8 @@ specifies a custom path for the file (default '.')
-
1177    def arctan(self):
-1178        return self._apply_func_to_corr(np.arctan)
+            
1179    def arctan(self):
+1180        return self._apply_func_to_corr(np.arctan)
 
@@ -4749,8 +4753,8 @@ specifies a custom path for the file (default '.')
-
1180    def arcsinh(self):
-1181        return self._apply_func_to_corr(np.arcsinh)
+            
1182    def arcsinh(self):
+1183        return self._apply_func_to_corr(np.arcsinh)
 
@@ -4768,8 +4772,8 @@ specifies a custom path for the file (default '.')
-
1183    def arccosh(self):
-1184        return self._apply_func_to_corr(np.arccosh)
+            
1185    def arccosh(self):
+1186        return self._apply_func_to_corr(np.arccosh)
 
@@ -4787,8 +4791,8 @@ specifies a custom path for the file (default '.')
-
1186    def arctanh(self):
-1187        return self._apply_func_to_corr(np.arctanh)
+            
1188    def arctanh(self):
+1189        return self._apply_func_to_corr(np.arctanh)
 
@@ -4806,62 +4810,62 @@ specifies a custom path for the file (default '.')
-
1222    def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None):
-1223        r''' Project large correlation matrix to lowest states
-1224
-1225        This method can be used to reduce the size of an (N x N) correlation matrix
-1226        to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise
-1227        is still small.
-1228
-1229        Parameters
-1230        ----------
-1231        Ntrunc: int
-1232            Rank of the target matrix.
-1233        tproj: int
-1234            Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method.
-1235            The default value is 3.
-1236        t0proj: int
-1237            Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly
-1238            discouraged for O(a) improved theories, since the correctness of the procedure
-1239            cannot be granted in this case. The default value is 2.
-1240        basematrix : Corr
-1241            Correlation matrix that is used to determine the eigenvectors of the
-1242            lowest states based on a GEVP. basematrix is taken to be the Corr itself if
-1243            is is not specified.
-1244
-1245        Notes
-1246        -----
-1247        We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving
-1248        the GEVP $$C(t) v_n(t, t_0) = \lambda_n(t, t_0) C(t_0) v_n(t, t_0)$$ where $t \equiv t_\mathrm{proj}$
-1249        and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the
-1250        resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via
-1251        $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large
-1252        correlation matrix and to remove some noise that is added by irrelevant operators.
-1253        This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated
-1254        bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$.
-1255        '''
-1256
-1257        if self.N == 1:
-1258            raise Exception('Method cannot be applied to one-dimensional correlators.')
-1259        if basematrix is None:
-1260            basematrix = self
-1261        if Ntrunc >= basematrix.N:
-1262            raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N))
-1263        if basematrix.N != self.N:
-1264            raise Exception('basematrix and targetmatrix have to be of the same size.')
-1265
-1266        evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc]
+            
1224    def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None):
+1225        r''' Project large correlation matrix to lowest states
+1226
+1227        This method can be used to reduce the size of an (N x N) correlation matrix
+1228        to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise
+1229        is still small.
+1230
+1231        Parameters
+1232        ----------
+1233        Ntrunc: int
+1234            Rank of the target matrix.
+1235        tproj: int
+1236            Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method.
+1237            The default value is 3.
+1238        t0proj: int
+1239            Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly
+1240            discouraged for O(a) improved theories, since the correctness of the procedure
+1241            cannot be granted in this case. The default value is 2.
+1242        basematrix : Corr
+1243            Correlation matrix that is used to determine the eigenvectors of the
+1244            lowest states based on a GEVP. basematrix is taken to be the Corr itself if
+1245            is is not specified.
+1246
+1247        Notes
+1248        -----
+1249        We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving
+1250        the GEVP $$C(t) v_n(t, t_0) = \lambda_n(t, t_0) C(t_0) v_n(t, t_0)$$ where $t \equiv t_\mathrm{proj}$
+1251        and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the
+1252        resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via
+1253        $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large
+1254        correlation matrix and to remove some noise that is added by irrelevant operators.
+1255        This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated
+1256        bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$.
+1257        '''
+1258
+1259        if self.N == 1:
+1260            raise Exception('Method cannot be applied to one-dimensional correlators.')
+1261        if basematrix is None:
+1262            basematrix = self
+1263        if Ntrunc >= basematrix.N:
+1264            raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N))
+1265        if basematrix.N != self.N:
+1266            raise Exception('basematrix and targetmatrix have to be of the same size.')
 1267
-1268        tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object)
-1269        rmat = []
-1270        for t in range(basematrix.T):
-1271            for i in range(Ntrunc):
-1272                for j in range(Ntrunc):
-1273                    tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j]
-1274            rmat.append(np.copy(tmpmat))
-1275
-1276        newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)]
-1277        return Corr(newcontent)
+1268        evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc]
+1269
+1270        tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object)
+1271        rmat = []
+1272        for t in range(basematrix.T):
+1273            for i in range(Ntrunc):
+1274                for j in range(Ntrunc):
+1275                    tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j]
+1276            rmat.append(np.copy(tmpmat))
+1277
+1278        newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)]
+1279        return Corr(newcontent)