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

199    def symmetric(self):
 200        """ Symmetrize the correlator around x0=0."""
-201        if self.T % 2 != 0:
-202            raise Exception("Can not symmetrize odd T")
-203
-204        if np.argmax(np.abs(self.content)) != 0:
-205            warnings.warn("Correlator does not seem to be symmetric around x0=0.", RuntimeWarning)
-206
-207        newcontent = [self.content[0]]
-208        for t in range(1, self.T):
-209            if (self.content[t] is None) or (self.content[self.T - t] is None):
-210                newcontent.append(None)
-211            else:
-212                newcontent.append(0.5 * (self.content[t] + self.content[self.T - t]))
-213        if(all([x is None for x in newcontent])):
-214            raise Exception("Corr could not be symmetrized: No redundant values")
-215        return Corr(newcontent, prange=self.prange)
+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 np.argmax(np.abs(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)
 
@@ -2986,25 +3021,27 @@ timeslice and the error on each timeslice.

-
217    def anti_symmetric(self):
-218        """Anti-symmetrize the correlator around x0=0."""
-219        if self.T % 2 != 0:
-220            raise Exception("Can not symmetrize odd T")
-221
-222        test = 1 * self
-223        test.gamma_method()
-224        if not all([o.is_zero_within_error(3) for o in test.content[0]]):
-225            warnings.warn("Correlator does not seem to be anti-symmetric around x0=0.", RuntimeWarning)
-226
-227        newcontent = [self.content[0]]
-228        for t in range(1, self.T):
-229            if (self.content[t] is None) or (self.content[self.T - t] is None):
-230                newcontent.append(None)
-231            else:
-232                newcontent.append(0.5 * (self.content[t] - self.content[self.T - t]))
-233        if(all([x is None for x in newcontent])):
-234            raise Exception("Corr could not be symmetrized: No redundant values")
-235        return Corr(newcontent, prange=self.prange)
+            
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)
 
@@ -3024,13 +3061,13 @@ timeslice and the error on each timeslice.

-
237    def matrix_symmetric(self):
-238        """Symmetrizes the correlator matrices on every timeslice."""
-239        if self.N > 1:
-240            transposed = [None if len(list(filter(None, np.asarray(G).flatten()))) < self.N ** 2 else G.T for G in self.content]
-241            return 0.5 * (Corr(transposed) + self)
-242        if self.N == 1:
-243            raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.")
+            
241    def matrix_symmetric(self):
+242        """Symmetrizes the correlator matrices on every timeslice."""
+243        if self.N > 1:
+244            transposed = [None if _check_for_none(self, G) else G.T for G in self.content]
+245            return 0.5 * (Corr(transposed) + self)
+246        if self.N == 1:
+247            raise Exception("Trying to symmetrize a correlator matrix, that already has N=1.")
 
@@ -3050,87 +3087,87 @@ timeslice and the error on each timeslice.

-
245    def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs):
-246        r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors.
-247
-248        The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the
-249        largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing
-250        ```python
-251        C.GEVP(t0=2)[0]  # Ground state vector(s)
-252        C.GEVP(t0=2)[:3]  # Vectors for the lowest three states
-253        ```
-254
-255        Parameters
-256        ----------
-257        t0 : int
-258            The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$
-259        ts : int
-260            fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None.
-261            If sort="Eigenvector" it gives a reference point for the sorting method.
-262        sort : string
-263            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.
-264            - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice.
-265            - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state.
-266              The reference state is identified by its eigenvalue at $t=t_s$.
-267
-268        Other Parameters
-269        ----------------
-270        state : int
-271           Returns only the vector(s) for a specified state. The lowest state is zero.
-272        '''
-273
-274        if self.N == 1:
-275            raise Exception("GEVP methods only works on correlator matrices and not single correlators.")
-276        if ts is not None:
-277            if (ts <= t0):
-278                raise Exception("ts has to be larger than t0.")
-279
-280        if "sorted_list" in kwargs:
-281            warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning)
-282            sort = kwargs.get("sorted_list")
+            
249    def GEVP(self, t0, ts=None, sort="Eigenvalue", **kwargs):
+250        r'''Solve the generalized eigenvalue problem on the correlator matrix and returns the corresponding eigenvectors.
+251
+252        The eigenvectors are sorted according to the descending eigenvalues, the zeroth eigenvector(s) correspond to the
+253        largest eigenvalue(s). The eigenvector(s) for the individual states can be accessed via slicing
+254        ```python
+255        C.GEVP(t0=2)[0]  # Ground state vector(s)
+256        C.GEVP(t0=2)[:3]  # Vectors for the lowest three states
+257        ```
+258
+259        Parameters
+260        ----------
+261        t0 : int
+262            The time t0 for the right hand side of the GEVP according to $G(t)v_i=\lambda_i G(t_0)v_i$
+263        ts : int
+264            fixed time $G(t_s)v_i=\lambda_i G(t_0)v_i$ if sort=None.
+265            If sort="Eigenvector" it gives a reference point for the sorting method.
+266        sort : string
+267            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.
+268            - "Eigenvalue": The eigenvector is chosen according to which eigenvalue it belongs individually on every timeslice.
+269            - "Eigenvector": Use the method described in arXiv:2004.10472 to find the set of v(t) belonging to the state.
+270              The reference state is identified by its eigenvalue at $t=t_s$.
+271
+272        Other Parameters
+273        ----------------
+274        state : int
+275           Returns only the vector(s) for a specified state. The lowest state is zero.
+276        '''
+277
+278        if self.N == 1:
+279            raise Exception("GEVP methods only works on correlator matrices and not single correlators.")
+280        if ts is not None:
+281            if (ts <= t0):
+282                raise Exception("ts has to be larger than t0.")
 283
-284        symmetric_corr = self.matrix_symmetric()
-285        if sort is None:
-286            if (ts is None):
-287                raise Exception("ts is required if sort=None.")
-288            if (self.content[t0] is None) or (self.content[ts] is None):
-289                raise Exception("Corr not defined at t0/ts.")
-290            G0, Gt = np.empty([self.N, self.N], dtype="double"), np.empty([self.N, self.N], dtype="double")
-291            for i in range(self.N):
-292                for j in range(self.N):
-293                    G0[i, j] = symmetric_corr[t0][i, j].value
-294                    Gt[i, j] = symmetric_corr[ts][i, j].value
-295
-296            reordered_vecs = _GEVP_solver(Gt, G0)
-297
-298        elif sort in ["Eigenvalue", "Eigenvector"]:
-299            if sort == "Eigenvalue" and ts is not None:
-300                warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning)
-301            all_vecs = [None] * (t0 + 1)
-302            for t in range(t0 + 1, self.T):
-303                try:
-304                    G0, Gt = np.empty([self.N, self.N], dtype="double"), np.empty([self.N, self.N], dtype="double")
-305                    for i in range(self.N):
-306                        for j in range(self.N):
-307                            G0[i, j] = symmetric_corr[t0][i, j].value
-308                            Gt[i, j] = symmetric_corr[t][i, j].value
-309
-310                    all_vecs.append(_GEVP_solver(Gt, G0))
-311                except Exception:
-312                    all_vecs.append(None)
-313            if sort == "Eigenvector":
-314                if (ts is None):
-315                    raise Exception("ts is required for the Eigenvector sorting method.")
-316                all_vecs = _sort_vectors(all_vecs, ts)
-317
-318            reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)]
-319        else:
-320            raise Exception("Unkown value for 'sort'.")
+284        if "sorted_list" in kwargs:
+285            warnings.warn("Argument 'sorted_list' is deprecated, use 'sort' instead.", DeprecationWarning)
+286            sort = kwargs.get("sorted_list")
+287
+288        symmetric_corr = self.matrix_symmetric()
+289        if sort is None:
+290            if (ts is None):
+291                raise Exception("ts is required if sort=None.")
+292            if (self.content[t0] is None) or (self.content[ts] is None):
+293                raise Exception("Corr not defined at t0/ts.")
+294            G0, Gt = np.empty([self.N, self.N], dtype="double"), np.empty([self.N, self.N], dtype="double")
+295            for i in range(self.N):
+296                for j in range(self.N):
+297                    G0[i, j] = symmetric_corr[t0][i, j].value
+298                    Gt[i, j] = symmetric_corr[ts][i, j].value
+299
+300            reordered_vecs = _GEVP_solver(Gt, G0)
+301
+302        elif sort in ["Eigenvalue", "Eigenvector"]:
+303            if sort == "Eigenvalue" and ts is not None:
+304                warnings.warn("ts has no effect when sorting by eigenvalue is chosen.", RuntimeWarning)
+305            all_vecs = [None] * (t0 + 1)
+306            for t in range(t0 + 1, self.T):
+307                try:
+308                    G0, Gt = np.empty([self.N, self.N], dtype="double"), np.empty([self.N, self.N], dtype="double")
+309                    for i in range(self.N):
+310                        for j in range(self.N):
+311                            G0[i, j] = symmetric_corr[t0][i, j].value
+312                            Gt[i, j] = symmetric_corr[t][i, j].value
+313
+314                    all_vecs.append(_GEVP_solver(Gt, G0))
+315                except Exception:
+316                    all_vecs.append(None)
+317            if sort == "Eigenvector":
+318                if (ts is None):
+319                    raise Exception("ts is required for the Eigenvector sorting method.")
+320                all_vecs = _sort_vectors(all_vecs, ts)
 321
-322        if "state" in kwargs:
-323            return reordered_vecs[kwargs.get("state")]
-324        else:
-325            return reordered_vecs
+322            reordered_vecs = [[v[s] if v is not None else None for v in all_vecs] for s in range(self.N)]
+323        else:
+324            raise Exception("Unkown value for 'sort'.")
+325
+326        if "state" in kwargs:
+327            return reordered_vecs[kwargs.get("state")]
+328        else:
+329            return reordered_vecs
 
@@ -3181,18 +3218,18 @@ Returns only the vector(s) for a specified state. The lowest state is zero.
-
327    def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"):
-328        """Determines the eigenvalue of the GEVP by solving and projecting the correlator
-329
-330        Parameters
-331        ----------
-332        state : int
-333            The state one is interested in ordered by energy. The lowest state is zero.
-334
-335        All other parameters are identical to the ones of Corr.GEVP.
-336        """
-337        vec = self.GEVP(t0, ts=ts, sort=sort)[state]
-338        return self.projected(vec)
+            
331    def Eigenvalue(self, t0, ts=None, state=0, sort="Eigenvalue"):
+332        """Determines the eigenvalue of the GEVP by solving and projecting the correlator
+333
+334        Parameters
+335        ----------
+336        state : int
+337            The state one is interested in ordered by energy. The lowest state is zero.
+338
+339        All other parameters are identical to the ones of Corr.GEVP.
+340        """
+341        vec = self.GEVP(t0, ts=ts, sort=sort)[state]
+342        return self.projected(vec)
 
@@ -3220,46 +3257,46 @@ The state one is interested in ordered by energy. The lowest state is zero.
-
340    def Hankel(self, N, periodic=False):
-341        """Constructs an NxN Hankel matrix
-342
-343        C(t) c(t+1) ... c(t+n-1)
-344        C(t+1) c(t+2) ... c(t+n)
-345        .................
-346        C(t+(n-1)) c(t+n) ... c(t+2(n-1))
-347
-348        Parameters
-349        ----------
-350        N : int
-351            Dimension of the Hankel matrix
-352        periodic : bool, optional
-353            determines whether the matrix is extended periodically
-354        """
-355
-356        if self.N != 1:
-357            raise Exception("Multi-operator Prony not implemented!")
-358
-359        array = np.empty([N, N], dtype="object")
-360        new_content = []
-361        for t in range(self.T):
-362            new_content.append(array.copy())
-363
-364        def wrap(i):
-365            while i >= self.T:
-366                i -= self.T
-367            return i
-368
-369        for t in range(self.T):
-370            for i in range(N):
-371                for j in range(N):
-372                    if periodic:
-373                        new_content[t][i, j] = self.content[wrap(t + i + j)][0]
-374                    elif (t + i + j) >= self.T:
-375                        new_content[t] = None
-376                    else:
-377                        new_content[t][i, j] = self.content[t + i + j][0]
-378
-379        return Corr(new_content)
+            
344    def Hankel(self, N, periodic=False):
+345        """Constructs an NxN Hankel matrix
+346
+347        C(t) c(t+1) ... c(t+n-1)
+348        C(t+1) c(t+2) ... c(t+n)
+349        .................
+350        C(t+(n-1)) c(t+n) ... c(t+2(n-1))
+351
+352        Parameters
+353        ----------
+354        N : int
+355            Dimension of the Hankel matrix
+356        periodic : bool, optional
+357            determines whether the matrix is extended periodically
+358        """
+359
+360        if self.N != 1:
+361            raise Exception("Multi-operator Prony not implemented!")
+362
+363        array = np.empty([N, N], dtype="object")
+364        new_content = []
+365        for t in range(self.T):
+366            new_content.append(array.copy())
+367
+368        def wrap(i):
+369            while i >= self.T:
+370                i -= self.T
+371            return i
+372
+373        for t in range(self.T):
+374            for i in range(N):
+375                for j in range(N):
+376                    if periodic:
+377                        new_content[t][i, j] = self.content[wrap(t + i + j)][0]
+378                    elif (t + i + j) >= self.T:
+379                        new_content[t] = None
+380                    else:
+381                        new_content[t][i, j] = self.content[t + i + j][0]
+382
+383        return Corr(new_content)
 
@@ -3293,15 +3330,15 @@ determines whether the matrix is extended periodically
-
381    def roll(self, dt):
-382        """Periodically shift the correlator by dt timeslices
-383
-384        Parameters
-385        ----------
-386        dt : int
-387            number of timeslices
-388        """
-389        return Corr(list(np.roll(np.array(self.content, dtype=object), dt)))
+            
385    def roll(self, dt):
+386        """Periodically shift the correlator by dt timeslices
+387
+388        Parameters
+389        ----------
+390        dt : int
+391            number of timeslices
+392        """
+393        return Corr(list(np.roll(np.array(self.content, dtype=object), dt)))
 
@@ -3328,9 +3365,9 @@ number of timeslices
-
391    def reverse(self):
-392        """Reverse the time ordering of the Corr"""
-393        return Corr(self.content[:: -1])
+            
395    def reverse(self):
+396        """Reverse the time ordering of the Corr"""
+397        return Corr(self.content[:: -1])
 
@@ -3350,23 +3387,23 @@ number of timeslices
-
395    def thin(self, spacing=2, offset=0):
-396        """Thin out a correlator to suppress correlations
-397
-398        Parameters
-399        ----------
-400        spacing : int
-401            Keep only every 'spacing'th entry of the correlator
-402        offset : int
-403            Offset the equal spacing
-404        """
-405        new_content = []
-406        for t in range(self.T):
-407            if (offset + t) % spacing != 0:
-408                new_content.append(None)
-409            else:
-410                new_content.append(self.content[t])
-411        return Corr(new_content)
+            
399    def thin(self, spacing=2, offset=0):
+400        """Thin out a correlator to suppress correlations
+401
+402        Parameters
+403        ----------
+404        spacing : int
+405            Keep only every 'spacing'th entry of the correlator
+406        offset : int
+407            Offset the equal spacing
+408        """
+409        new_content = []
+410        for t in range(self.T):
+411            if (offset + t) % spacing != 0:
+412                new_content.append(None)
+413            else:
+414                new_content.append(self.content[t])
+415        return Corr(new_content)
 
@@ -3395,32 +3432,34 @@ Offset the equal spacing
-
413    def correlate(self, partner):
-414        """Correlate the correlator with another correlator or Obs
-415
-416        Parameters
-417        ----------
-418        partner : Obs or Corr
-419            partner to correlate the correlator with.
-420            Can either be an Obs which is correlated with all entries of the
-421            correlator or a Corr of same length.
-422        """
-423        new_content = []
-424        for x0, t_slice in enumerate(self.content):
-425            if t_slice is None:
-426                new_content.append(None)
-427            else:
-428                if isinstance(partner, Corr):
-429                    if partner.content[x0] is None:
-430                        new_content.append(None)
-431                    else:
-432                        new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice]))
-433                elif isinstance(partner, Obs):  # Should this include CObs?
-434                    new_content.append(np.array([correlate(o, partner) for o in t_slice]))
-435                else:
-436                    raise Exception("Can only correlate with an Obs or a Corr.")
-437
-438        return Corr(new_content)
+            
417    def correlate(self, partner):
+418        """Correlate the correlator with another correlator or Obs
+419
+420        Parameters
+421        ----------
+422        partner : Obs or Corr
+423            partner to correlate the correlator with.
+424            Can either be an Obs which is correlated with all entries of the
+425            correlator or a Corr of same length.
+426        """
+427        if self.N != 1:
+428            raise Exception("Only one-dimensional correlators can be safely correlated.")
+429        new_content = []
+430        for x0, t_slice in enumerate(self.content):
+431            if _check_for_none(self, t_slice):
+432                new_content.append(None)
+433            else:
+434                if isinstance(partner, Corr):
+435                    if _check_for_none(partner, partner.content[x0]):
+436                        new_content.append(None)
+437                    else:
+438                        new_content.append(np.array([correlate(o, partner.content[x0][0]) for o in t_slice]))
+439                elif isinstance(partner, Obs):  # Should this include CObs?
+440                    new_content.append(np.array([correlate(o, partner) for o in t_slice]))
+441                else:
+442                    raise Exception("Can only correlate with an Obs or a Corr.")
+443
+444        return Corr(new_content)
 
@@ -3449,26 +3488,28 @@ correlator or a Corr of same length.
-
440    def reweight(self, weight, **kwargs):
-441        """Reweight the correlator.
-442
-443        Parameters
-444        ----------
-445        weight : Obs
-446            Reweighting factor. An Observable that has to be defined on a superset of the
-447            configurations in obs[i].idl for all i.
-448        all_configs : bool
-449            if True, the reweighted observables are normalized by the average of
-450            the reweighting factor on all configurations in weight.idl and not
-451            on the configurations in obs[i].idl.
-452        """
-453        new_content = []
-454        for t_slice in self.content:
-455            if t_slice is None:
-456                new_content.append(None)
-457            else:
-458                new_content.append(np.array(reweight(weight, t_slice, **kwargs)))
-459        return Corr(new_content)
+            
446    def reweight(self, weight, **kwargs):
+447        """Reweight the correlator.
+448
+449        Parameters
+450        ----------
+451        weight : Obs
+452            Reweighting factor. An Observable that has to be defined on a superset of the
+453            configurations in obs[i].idl for all i.
+454        all_configs : bool
+455            if True, the reweighted observables are normalized by the average of
+456            the reweighting factor on all configurations in weight.idl and not
+457            on the configurations in obs[i].idl.
+458        """
+459        if self.N != 1:
+460            raise Exception("Reweighting only implemented for one-dimensional correlators.")
+461        new_content = []
+462        for t_slice in self.content:
+463            if _check_for_none(self, t_slice):
+464                new_content.append(None)
+465            else:
+466                new_content.append(np.array(reweight(weight, t_slice, **kwargs)))
+467        return Corr(new_content)
 
@@ -3500,33 +3541,35 @@ on the configurations in obs[i].idl.
-
461    def T_symmetry(self, partner, parity=+1):
-462        """Return the time symmetry average of the correlator and its partner
-463
-464        Parameters
-465        ----------
-466        partner : Corr
-467            Time symmetry partner of the Corr
-468        partity : int
-469            Parity quantum number of the correlator, can be +1 or -1
-470        """
-471        if not isinstance(partner, Corr):
-472            raise Exception("T partner has to be a Corr object.")
-473        if parity not in [+1, -1]:
-474            raise Exception("Parity has to be +1 or -1.")
-475        T_partner = parity * partner.reverse()
-476
-477        t_slices = []
-478        test = (self - T_partner)
-479        test.gamma_method()
-480        for x0, t_slice in enumerate(test.content):
-481            if t_slice is not None:
-482                if not t_slice[0].is_zero_within_error(5):
-483                    t_slices.append(x0)
-484        if t_slices:
-485            warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning)
+            
469    def T_symmetry(self, partner, parity=+1):
+470        """Return the time symmetry average of the correlator and its partner
+471
+472        Parameters
+473        ----------
+474        partner : Corr
+475            Time symmetry partner of the Corr
+476        partity : int
+477            Parity quantum number of the correlator, can be +1 or -1
+478        """
+479        if self.N != 1:
+480            raise Exception("T_symmetry only implemented for one-dimensional correlators.")
+481        if not isinstance(partner, Corr):
+482            raise Exception("T partner has to be a Corr object.")
+483        if parity not in [+1, -1]:
+484            raise Exception("Parity has to be +1 or -1.")
+485        T_partner = parity * partner.reverse()
 486
-487        return (self + T_partner) / 2
+487        t_slices = []
+488        test = (self - T_partner)
+489        test.gamma_method()
+490        for x0, t_slice in enumerate(test.content):
+491            if t_slice is not None:
+492                if not t_slice[0].is_zero_within_error(5):
+493                    t_slices.append(x0)
+494        if t_slices:
+495            warnings.warn("T symmetry partners do not agree within 5 sigma on time slices " + str(t_slices) + ".", RuntimeWarning)
+496
+497        return (self + T_partner) / 2
 
@@ -3555,57 +3598,59 @@ Parity quantum number of the correlator, can be +1 or -1
-
489    def deriv(self, variant="symmetric"):
-490        """Return the first derivative of the correlator with respect to x0.
-491
-492        Parameters
-493        ----------
-494        variant : str
-495            decides which definition of the finite differences derivative is used.
-496            Available choice: symmetric, forward, backward, improved, default: symmetric
-497        """
-498        if variant == "symmetric":
-499            newcontent = []
-500            for t in range(1, self.T - 1):
-501                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
-502                    newcontent.append(None)
-503                else:
-504                    newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1]))
-505            if(all([x is None for x in newcontent])):
-506                raise Exception('Derivative is undefined at all timeslices')
-507            return Corr(newcontent, padding=[1, 1])
-508        elif variant == "forward":
-509            newcontent = []
-510            for t in range(self.T - 1):
-511                if (self.content[t] is None) or (self.content[t + 1] is None):
-512                    newcontent.append(None)
-513                else:
-514                    newcontent.append(self.content[t + 1] - self.content[t])
-515            if(all([x is None for x in newcontent])):
-516                raise Exception("Derivative is undefined at all timeslices")
-517            return Corr(newcontent, padding=[0, 1])
-518        elif variant == "backward":
-519            newcontent = []
-520            for t in range(1, self.T):
-521                if (self.content[t - 1] is None) or (self.content[t] is None):
-522                    newcontent.append(None)
-523                else:
-524                    newcontent.append(self.content[t] - self.content[t - 1])
-525            if(all([x is None for x in newcontent])):
-526                raise Exception("Derivative is undefined at all timeslices")
-527            return Corr(newcontent, padding=[1, 0])
-528        elif variant == "improved":
-529            newcontent = []
-530            for t in range(2, self.T - 2):
-531                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):
-532                    newcontent.append(None)
-533                else:
-534                    newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2]))
-535            if(all([x is None for x in newcontent])):
-536                raise Exception('Derivative is undefined at all timeslices')
-537            return Corr(newcontent, padding=[2, 2])
-538        else:
-539            raise Exception("Unknown variant.")
+            
499    def deriv(self, variant="symmetric"):
+500        """Return the first derivative of the correlator with respect to x0.
+501
+502        Parameters
+503        ----------
+504        variant : str
+505            decides which definition of the finite differences derivative is used.
+506            Available choice: symmetric, forward, backward, improved, default: symmetric
+507        """
+508        if self.N != 1:
+509            raise Exception("deriv only implemented for one-dimensional correlators.")
+510        if variant == "symmetric":
+511            newcontent = []
+512            for t in range(1, self.T - 1):
+513                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
+514                    newcontent.append(None)
+515                else:
+516                    newcontent.append(0.5 * (self.content[t + 1] - self.content[t - 1]))
+517            if(all([x is None for x in newcontent])):
+518                raise Exception('Derivative is undefined at all timeslices')
+519            return Corr(newcontent, padding=[1, 1])
+520        elif variant == "forward":
+521            newcontent = []
+522            for t in range(self.T - 1):
+523                if (self.content[t] is None) or (self.content[t + 1] is None):
+524                    newcontent.append(None)
+525                else:
+526                    newcontent.append(self.content[t + 1] - self.content[t])
+527            if(all([x is None for x in newcontent])):
+528                raise Exception("Derivative is undefined at all timeslices")
+529            return Corr(newcontent, padding=[0, 1])
+530        elif variant == "backward":
+531            newcontent = []
+532            for t in range(1, self.T):
+533                if (self.content[t - 1] is None) or (self.content[t] is None):
+534                    newcontent.append(None)
+535                else:
+536                    newcontent.append(self.content[t] - self.content[t - 1])
+537            if(all([x is None for x in newcontent])):
+538                raise Exception("Derivative is undefined at all timeslices")
+539            return Corr(newcontent, padding=[1, 0])
+540        elif variant == "improved":
+541            newcontent = []
+542            for t in range(2, self.T - 2):
+543                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):
+544                    newcontent.append(None)
+545                else:
+546                    newcontent.append((1 / 12) * (self.content[t - 2] - 8 * self.content[t - 1] + 8 * self.content[t + 1] - self.content[t + 2]))
+547            if(all([x is None for x in newcontent])):
+548                raise Exception('Derivative is undefined at all timeslices')
+549            return Corr(newcontent, padding=[2, 2])
+550        else:
+551            raise Exception("Unknown variant.")
 
@@ -3633,37 +3678,39 @@ Available choice: symmetric, forward, backward, improved, default: symmetric -
541    def second_deriv(self, variant="symmetric"):
-542        """Return the second derivative of the correlator with respect to x0.
-543
-544        Parameters
-545        ----------
-546        variant : str
-547            decides which definition of the finite differences derivative is used.
-548            Available choice: symmetric, improved, default: symmetric
-549        """
-550        if variant == "symmetric":
-551            newcontent = []
-552            for t in range(1, self.T - 1):
-553                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
-554                    newcontent.append(None)
-555                else:
-556                    newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1]))
-557            if(all([x is None for x in newcontent])):
-558                raise Exception("Derivative is undefined at all timeslices")
-559            return Corr(newcontent, padding=[1, 1])
-560        elif variant == "improved":
-561            newcontent = []
-562            for t in range(2, self.T - 2):
-563                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):
-564                    newcontent.append(None)
-565                else:
-566                    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]))
-567            if(all([x is None for x in newcontent])):
-568                raise Exception("Derivative is undefined at all timeslices")
-569            return Corr(newcontent, padding=[2, 2])
-570        else:
-571            raise Exception("Unknown variant.")
+            
553    def second_deriv(self, variant="symmetric"):
+554        """Return the second derivative of the correlator with respect to x0.
+555
+556        Parameters
+557        ----------
+558        variant : str
+559            decides which definition of the finite differences derivative is used.
+560            Available choice: symmetric, improved, default: symmetric
+561        """
+562        if self.N != 1:
+563            raise Exception("second_deriv only implemented for one-dimensional correlators.")
+564        if variant == "symmetric":
+565            newcontent = []
+566            for t in range(1, self.T - 1):
+567                if (self.content[t - 1] is None) or (self.content[t + 1] is None):
+568                    newcontent.append(None)
+569                else:
+570                    newcontent.append((self.content[t + 1] - 2 * self.content[t] + self.content[t - 1]))
+571            if(all([x is None for x in newcontent])):
+572                raise Exception("Derivative is undefined at all timeslices")
+573            return Corr(newcontent, padding=[1, 1])
+574        elif variant == "improved":
+575            newcontent = []
+576            for t in range(2, self.T - 2):
+577                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):
+578                    newcontent.append(None)
+579                else:
+580                    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]))
+581            if(all([x is None for x in newcontent])):
+582                raise Exception("Derivative is undefined at all timeslices")
+583            return Corr(newcontent, padding=[2, 2])
+584        else:
+585            raise Exception("Unknown variant.")
 
@@ -3691,70 +3738,70 @@ Available choice: symmetric, improved, default: symmetric
-
573    def m_eff(self, variant='log', guess=1.0):
-574        """Returns the effective mass of the correlator as correlator object
-575
-576        Parameters
-577        ----------
-578        variant : str
-579            log : uses the standard effective mass log(C(t) / C(t+1))
-580            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.
-581            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.
-582            See, e.g., arXiv:1205.5380
-583            arccosh : Uses the explicit form of the symmetrized correlator (not recommended)
-584        guess : float
-585            guess for the root finder, only relevant for the root variant
-586        """
-587        if self.N != 1:
-588            raise Exception('Correlator must be projected before getting m_eff')
-589        if variant == 'log':
-590            newcontent = []
-591            for t in range(self.T - 1):
-592                if (self.content[t] is None) or (self.content[t + 1] is None):
-593                    newcontent.append(None)
-594                else:
-595                    newcontent.append(self.content[t] / self.content[t + 1])
-596            if(all([x is None for x in newcontent])):
-597                raise Exception('m_eff is undefined at all timeslices')
-598
-599            return np.log(Corr(newcontent, padding=[0, 1]))
-600
-601        elif variant in ['periodic', 'cosh', 'sinh']:
-602            if variant in ['periodic', 'cosh']:
-603                func = anp.cosh
-604            else:
-605                func = anp.sinh
-606
-607            def root_function(x, d):
-608                return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d
-609
-610            newcontent = []
-611            for t in range(self.T - 1):
-612                if (self.content[t] is None) or (self.content[t + 1] is None):
-613                    newcontent.append(None)
-614                # Fill the two timeslices in the middle of the lattice with their predecessors
-615                elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]:
-616                    newcontent.append(newcontent[-1])
-617                else:
-618                    newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess)))
-619            if(all([x is None for x in newcontent])):
-620                raise Exception('m_eff is undefined at all timeslices')
-621
-622            return Corr(newcontent, padding=[0, 1])
+            
587    def m_eff(self, variant='log', guess=1.0):
+588        """Returns the effective mass of the correlator as correlator object
+589
+590        Parameters
+591        ----------
+592        variant : str
+593            log : uses the standard effective mass log(C(t) / C(t+1))
+594            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.
+595            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.
+596            See, e.g., arXiv:1205.5380
+597            arccosh : Uses the explicit form of the symmetrized correlator (not recommended)
+598        guess : float
+599            guess for the root finder, only relevant for the root variant
+600        """
+601        if self.N != 1:
+602            raise Exception('Correlator must be projected before getting m_eff')
+603        if variant == 'log':
+604            newcontent = []
+605            for t in range(self.T - 1):
+606                if (self.content[t] is None) or (self.content[t + 1] is None):
+607                    newcontent.append(None)
+608                else:
+609                    newcontent.append(self.content[t] / self.content[t + 1])
+610            if(all([x is None for x in newcontent])):
+611                raise Exception('m_eff is undefined at all timeslices')
+612
+613            return np.log(Corr(newcontent, padding=[0, 1]))
+614
+615        elif variant in ['periodic', 'cosh', 'sinh']:
+616            if variant in ['periodic', 'cosh']:
+617                func = anp.cosh
+618            else:
+619                func = anp.sinh
+620
+621            def root_function(x, d):
+622                return func(x * (t - self.T / 2)) / func(x * (t + 1 - self.T / 2)) - d
 623
-624        elif variant == 'arccosh':
-625            newcontent = []
-626            for t in range(1, self.T - 1):
-627                if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None):
-628                    newcontent.append(None)
-629                else:
-630                    newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t]))
-631            if(all([x is None for x in newcontent])):
-632                raise Exception("m_eff is undefined at all timeslices")
-633            return np.arccosh(Corr(newcontent, padding=[1, 1]))
-634
-635        else:
-636            raise Exception('Unknown variant.')
+624            newcontent = []
+625            for t in range(self.T - 1):
+626                if (self.content[t] is None) or (self.content[t + 1] is None):
+627                    newcontent.append(None)
+628                # Fill the two timeslices in the middle of the lattice with their predecessors
+629                elif variant == 'sinh' and t in [self.T / 2, self.T / 2 - 1]:
+630                    newcontent.append(newcontent[-1])
+631                else:
+632                    newcontent.append(np.abs(find_root(self.content[t][0] / self.content[t + 1][0], root_function, guess=guess)))
+633            if(all([x is None for x in newcontent])):
+634                raise Exception('m_eff is undefined at all timeslices')
+635
+636            return Corr(newcontent, padding=[0, 1])
+637
+638        elif variant == 'arccosh':
+639            newcontent = []
+640            for t in range(1, self.T - 1):
+641                if (self.content[t] is None) or (self.content[t + 1] is None) or (self.content[t - 1] is None):
+642                    newcontent.append(None)
+643                else:
+644                    newcontent.append((self.content[t + 1] + self.content[t - 1]) / (2 * self.content[t]))
+645            if(all([x is None for x in newcontent])):
+646                raise Exception("m_eff is undefined at all timeslices")
+647            return np.arccosh(Corr(newcontent, padding=[1, 1]))
+648
+649        else:
+650            raise Exception('Unknown variant.')
 
@@ -3787,39 +3834,39 @@ guess for the root finder, only relevant for the root variant
-
638    def fit(self, function, fitrange=None, silent=False, **kwargs):
-639        r'''Fits function to the data
-640
-641        Parameters
-642        ----------
-643        function : obj
-644            function to fit to the data. See fits.least_squares for details.
-645        fitrange : list
-646            Two element list containing the timeslices on which the fit is supposed to start and stop.
-647            Caution: This range is inclusive as opposed to standard python indexing.
-648            `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6.
-649            If not specified, self.prange or all timeslices are used.
-650        silent : bool
-651            Decides whether output is printed to the standard output.
-652        '''
-653        if self.N != 1:
-654            raise Exception("Correlator must be projected before fitting")
-655
-656        if fitrange is None:
-657            if self.prange:
-658                fitrange = self.prange
-659            else:
-660                fitrange = [0, self.T - 1]
-661        else:
-662            if not isinstance(fitrange, list):
-663                raise Exception("fitrange has to be a list with two elements")
-664            if len(fitrange) != 2:
-665                raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]")
-666
-667        xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
-668        ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
-669        result = least_squares(xs, ys, function, silent=silent, **kwargs)
-670        return result
+            
652    def fit(self, function, fitrange=None, silent=False, **kwargs):
+653        r'''Fits function to the data
+654
+655        Parameters
+656        ----------
+657        function : obj
+658            function to fit to the data. See fits.least_squares for details.
+659        fitrange : list
+660            Two element list containing the timeslices on which the fit is supposed to start and stop.
+661            Caution: This range is inclusive as opposed to standard python indexing.
+662            `fitrange=[4, 6]` corresponds to the three entries 4, 5 and 6.
+663            If not specified, self.prange or all timeslices are used.
+664        silent : bool
+665            Decides whether output is printed to the standard output.
+666        '''
+667        if self.N != 1:
+668            raise Exception("Correlator must be projected before fitting")
+669
+670        if fitrange is None:
+671            if self.prange:
+672                fitrange = self.prange
+673            else:
+674                fitrange = [0, self.T - 1]
+675        else:
+676            if not isinstance(fitrange, list):
+677                raise Exception("fitrange has to be a list with two elements")
+678            if len(fitrange) != 2:
+679                raise Exception("fitrange has to have exactly two elements [fit_start, fit_stop]")
+680
+681        xs = [x for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
+682        ys = [self.content[x][0] for x in range(fitrange[0], fitrange[1] + 1) if not self.content[x] is None]
+683        result = least_squares(xs, ys, function, silent=silent, **kwargs)
+684        return result
 
@@ -3853,42 +3900,42 @@ Decides whether output is printed to the standard output.
-
672    def plateau(self, plateau_range=None, method="fit", auto_gamma=False):
-673        """ Extract a plateau value from a Corr object
-674
-675        Parameters
-676        ----------
-677        plateau_range : list
-678            list with two entries, indicating the first and the last timeslice
-679            of the plateau region.
-680        method : str
-681            method to extract the plateau.
-682                'fit' fits a constant to the plateau region
-683                'avg', 'average' or 'mean' just average over the given timeslices.
-684        auto_gamma : bool
-685            apply gamma_method with default parameters to the Corr. Defaults to None
-686        """
-687        if not plateau_range:
-688            if self.prange:
-689                plateau_range = self.prange
-690            else:
-691                raise Exception("no plateau range provided")
-692        if self.N != 1:
-693            raise Exception("Correlator must be projected before getting a plateau.")
-694        if(all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])):
-695            raise Exception("plateau is undefined at all timeslices in plateaurange.")
-696        if auto_gamma:
-697            self.gamma_method()
-698        if method == "fit":
-699            def const_func(a, t):
-700                return a[0]
-701            return self.fit(const_func, plateau_range)[0]
-702        elif method in ["avg", "average", "mean"]:
-703            returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None])
-704            return returnvalue
-705
-706        else:
-707            raise Exception("Unsupported plateau method: " + method)
+            
686    def plateau(self, plateau_range=None, method="fit", auto_gamma=False):
+687        """ Extract a plateau value from a Corr object
+688
+689        Parameters
+690        ----------
+691        plateau_range : list
+692            list with two entries, indicating the first and the last timeslice
+693            of the plateau region.
+694        method : str
+695            method to extract the plateau.
+696                'fit' fits a constant to the plateau region
+697                'avg', 'average' or 'mean' just average over the given timeslices.
+698        auto_gamma : bool
+699            apply gamma_method with default parameters to the Corr. Defaults to None
+700        """
+701        if not plateau_range:
+702            if self.prange:
+703                plateau_range = self.prange
+704            else:
+705                raise Exception("no plateau range provided")
+706        if self.N != 1:
+707            raise Exception("Correlator must be projected before getting a plateau.")
+708        if(all([self.content[t] is None for t in range(plateau_range[0], plateau_range[1] + 1)])):
+709            raise Exception("plateau is undefined at all timeslices in plateaurange.")
+710        if auto_gamma:
+711            self.gamma_method()
+712        if method == "fit":
+713            def const_func(a, t):
+714                return a[0]
+715            return self.fit(const_func, plateau_range)[0]
+716        elif method in ["avg", "average", "mean"]:
+717            returnvalue = np.mean([item[0] for item in self.content[plateau_range[0]:plateau_range[1] + 1] if item is not None])
+718            return returnvalue
+719
+720        else:
+721            raise Exception("Unsupported plateau method: " + method)
 
@@ -3922,17 +3969,17 @@ apply gamma_method with default parameters to the Corr. Defaults to None
-
709    def set_prange(self, prange):
-710        """Sets the attribute prange of the Corr object."""
-711        if not len(prange) == 2:
-712            raise Exception("prange must be a list or array with two values")
-713        if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))):
-714            raise Exception("Start and end point must be integers")
-715        if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]):
-716            raise Exception("Start and end point must define a range in the interval 0,T")
-717
-718        self.prange = prange
-719        return
+            
723    def set_prange(self, prange):
+724        """Sets the attribute prange of the Corr object."""
+725        if not len(prange) == 2:
+726            raise Exception("prange must be a list or array with two values")
+727        if not ((isinstance(prange[0], int)) and (isinstance(prange[1], int))):
+728            raise Exception("Start and end point must be integers")
+729        if not (0 <= prange[0] <= self.T and 0 <= prange[1] <= self.T and prange[0] < prange[1]):
+730            raise Exception("Start and end point must define a range in the interval 0,T")
+731
+732        self.prange = prange
+733        return
 
@@ -3952,118 +3999,118 @@ apply gamma_method with default parameters to the Corr. Defaults to None
-
721    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):
-722        """Plots the correlator using the tag of the correlator as label if available.
-723
-724        Parameters
-725        ----------
-726        x_range : list
-727            list of two values, determining the range of the x-axis e.g. [4, 8]
-728        comp : Corr or list of Corr
-729            Correlator or list of correlators which are plotted for comparison.
-730            The tags of these correlators are used as labels if available.
-731        logscale : bool
-732            Sets y-axis to logscale
-733        plateau : Obs
-734            Plateau value to be visualized in the figure
-735        fit_res : Fit_result
-736            Fit_result object to be visualized
-737        ylabel : str
-738            Label for the y-axis
-739        save : str
-740            path to file in which the figure should be saved
-741        auto_gamma : bool
-742            Apply the gamma method with standard parameters to all correlators and plateau values before plotting.
-743        hide_sigma : float
-744            Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors.
-745        references : list
-746            List of floating point values that are displayed as horizontal lines for reference.
-747        """
-748        if self.N != 1:
-749            raise Exception("Correlator must be projected before plotting")
-750
-751        if auto_gamma:
-752            self.gamma_method()
-753
-754        if x_range is None:
-755            x_range = [0, self.T - 1]
-756
-757        fig = plt.figure()
-758        ax1 = fig.add_subplot(111)
-759
-760        x, y, y_err = self.plottable()
-761        if hide_sigma:
-762            hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
-763        else:
-764            hide_from = None
-765        ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag)
-766        if logscale:
-767            ax1.set_yscale('log')
-768        else:
-769            if y_range is None:
-770                try:
-771                    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)])
-772                    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)])
-773                    ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)])
-774                except Exception:
-775                    pass
-776            else:
-777                ax1.set_ylim(y_range)
-778        if comp:
-779            if isinstance(comp, (Corr, list)):
-780                for corr in comp if isinstance(comp, list) else [comp]:
-781                    if auto_gamma:
-782                        corr.gamma_method()
-783                    x, y, y_err = corr.plottable()
-784                    if hide_sigma:
-785                        hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
-786                    else:
-787                        hide_from = None
-788                    plt.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor'])
-789            else:
-790                raise Exception("'comp' must be a correlator or a list of correlators.")
-791
-792        if plateau:
-793            if isinstance(plateau, Obs):
-794                if auto_gamma:
-795                    plateau.gamma_method()
-796                ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau))
-797                ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-')
-798            else:
-799                raise Exception("'plateau' must be an Obs")
-800
-801        if references:
-802            if isinstance(references, list):
-803                for ref in references:
-804                    ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--')
-805            else:
-806                raise Exception("'references' must be a list of floating pint values.")
-807
-808        if self.prange:
-809            ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',')
-810            ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',')
-811
-812        if fit_res:
-813            x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05)
-814            ax1.plot(x_samples,
-815                     fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples),
-816                     ls='-', marker=',', lw=2)
-817
-818        ax1.set_xlabel(r'$x_0 / a$')
-819        if ylabel:
-820            ax1.set_ylabel(ylabel)
-821        ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5])
-822
-823        handles, labels = ax1.get_legend_handles_labels()
-824        if labels:
-825            ax1.legend()
-826        plt.draw()
-827
-828        if save:
-829            if isinstance(save, str):
-830                fig.savefig(save)
-831            else:
-832                raise Exception("'save' has to be a string.")
+            
735    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):
+736        """Plots the correlator using the tag of the correlator as label if available.
+737
+738        Parameters
+739        ----------
+740        x_range : list
+741            list of two values, determining the range of the x-axis e.g. [4, 8]
+742        comp : Corr or list of Corr
+743            Correlator or list of correlators which are plotted for comparison.
+744            The tags of these correlators are used as labels if available.
+745        logscale : bool
+746            Sets y-axis to logscale
+747        plateau : Obs
+748            Plateau value to be visualized in the figure
+749        fit_res : Fit_result
+750            Fit_result object to be visualized
+751        ylabel : str
+752            Label for the y-axis
+753        save : str
+754            path to file in which the figure should be saved
+755        auto_gamma : bool
+756            Apply the gamma method with standard parameters to all correlators and plateau values before plotting.
+757        hide_sigma : float
+758            Hides data points from the first value on which is consistent with zero within 'hide_sigma' standard errors.
+759        references : list
+760            List of floating point values that are displayed as horizontal lines for reference.
+761        """
+762        if self.N != 1:
+763            raise Exception("Correlator must be projected before plotting")
+764
+765        if auto_gamma:
+766            self.gamma_method()
+767
+768        if x_range is None:
+769            x_range = [0, self.T - 1]
+770
+771        fig = plt.figure()
+772        ax1 = fig.add_subplot(111)
+773
+774        x, y, y_err = self.plottable()
+775        if hide_sigma:
+776            hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
+777        else:
+778            hide_from = None
+779        ax1.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=self.tag)
+780        if logscale:
+781            ax1.set_yscale('log')
+782        else:
+783            if y_range is None:
+784                try:
+785                    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)])
+786                    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)])
+787                    ax1.set_ylim([y_min - 0.1 * (y_max - y_min), y_max + 0.1 * (y_max - y_min)])
+788                except Exception:
+789                    pass
+790            else:
+791                ax1.set_ylim(y_range)
+792        if comp:
+793            if isinstance(comp, (Corr, list)):
+794                for corr in comp if isinstance(comp, list) else [comp]:
+795                    if auto_gamma:
+796                        corr.gamma_method()
+797                    x, y, y_err = corr.plottable()
+798                    if hide_sigma:
+799                        hide_from = np.argmax((hide_sigma * np.array(y_err[1:])) > np.abs(y[1:])) - 1
+800                    else:
+801                        hide_from = None
+802                    plt.errorbar(x[:hide_from], y[:hide_from], y_err[:hide_from], label=corr.tag, mfc=plt.rcParams['axes.facecolor'])
+803            else:
+804                raise Exception("'comp' must be a correlator or a list of correlators.")
+805
+806        if plateau:
+807            if isinstance(plateau, Obs):
+808                if auto_gamma:
+809                    plateau.gamma_method()
+810                ax1.axhline(y=plateau.value, linewidth=2, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--', label=str(plateau))
+811                ax1.axhspan(plateau.value - plateau.dvalue, plateau.value + plateau.dvalue, alpha=0.25, color=plt.rcParams['text.color'], ls='-')
+812            else:
+813                raise Exception("'plateau' must be an Obs")
+814
+815        if references:
+816            if isinstance(references, list):
+817                for ref in references:
+818                    ax1.axhline(y=ref, linewidth=1, color=plt.rcParams['text.color'], alpha=0.6, marker=',', ls='--')
+819            else:
+820                raise Exception("'references' must be a list of floating pint values.")
+821
+822        if self.prange:
+823            ax1.axvline(self.prange[0], 0, 1, ls='-', marker=',')
+824            ax1.axvline(self.prange[1], 0, 1, ls='-', marker=',')
+825
+826        if fit_res:
+827            x_samples = np.arange(x_range[0], x_range[1] + 1, 0.05)
+828            ax1.plot(x_samples,
+829                     fit_res.fit_function([o.value for o in fit_res.fit_parameters], x_samples),
+830                     ls='-', marker=',', lw=2)
+831
+832        ax1.set_xlabel(r'$x_0 / a$')
+833        if ylabel:
+834            ax1.set_ylabel(ylabel)
+835        ax1.set_xlim([x_range[0] - 0.5, x_range[1] + 0.5])
+836
+837        handles, labels = ax1.get_legend_handles_labels()
+838        if labels:
+839            ax1.legend()
+840        plt.draw()
+841
+842        if save:
+843            if isinstance(save, str):
+844                fig.savefig(save)
+845            else:
+846                raise Exception("'save' has to be a string.")
 
@@ -4109,34 +4156,34 @@ List of floating point values that are displayed as horizontal lines for referen
-
834    def spaghetti_plot(self, logscale=True):
-835        """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations.
-836
-837        Parameters
-838        ----------
-839        logscale : bool
-840            Determines whether the scale of the y-axis is logarithmic or standard.
-841        """
-842        if self.N != 1:
-843            raise Exception("Correlator needs to be projected first.")
-844
-845        mc_names = list(set([item for sublist in [o[0].mc_names for o in self.content if o is not None] for item in sublist]))
-846        x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None]
-847
-848        for name in mc_names:
-849            data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T
+            
848    def spaghetti_plot(self, logscale=True):
+849        """Produces a spaghetti plot of the correlator suited to monitor exceptional configurations.
 850
-851            fig = plt.figure()
-852            ax = fig.add_subplot(111)
-853            for dat in data:
-854                ax.plot(x0_vals, dat, ls='-', marker='')
-855
-856            if logscale is True:
-857                ax.set_yscale('log')
+851        Parameters
+852        ----------
+853        logscale : bool
+854            Determines whether the scale of the y-axis is logarithmic or standard.
+855        """
+856        if self.N != 1:
+857            raise Exception("Correlator needs to be projected first.")
 858
-859            ax.set_xlabel(r'$x_0 / a$')
-860            plt.title(name)
-861            plt.draw()
+859        mc_names = list(set([item for sublist in [o[0].mc_names for o in self.content if o is not None] for item in sublist]))
+860        x0_vals = [n for (n, o) in zip(np.arange(self.T), self.content) if o is not None]
+861
+862        for name in mc_names:
+863            data = np.array([o[0].deltas[name] + o[0].r_values[name] for o in self.content if o is not None]).T
+864
+865            fig = plt.figure()
+866            ax = fig.add_subplot(111)
+867            for dat in data:
+868                ax.plot(x0_vals, dat, ls='-', marker='')
+869
+870            if logscale is True:
+871                ax.set_yscale('log')
+872
+873            ax.set_xlabel(r'$x_0 / a$')
+874            plt.title(name)
+875            plt.draw()
 
@@ -4163,29 +4210,29 @@ Determines whether the scale of the y-axis is logarithmic or standard.
-
863    def dump(self, filename, datatype="json.gz", **kwargs):
-864        """Dumps the Corr into a file of chosen type
-865        Parameters
-866        ----------
-867        filename : str
-868            Name of the file to be saved.
-869        datatype : str
-870            Format of the exported file. Supported formats include
-871            "json.gz" and "pickle"
-872        path : str
-873            specifies a custom path for the file (default '.')
-874        """
-875        if datatype == "json.gz":
-876            from .input.json import dump_to_json
-877            if 'path' in kwargs:
-878                file_name = kwargs.get('path') + '/' + filename
-879            else:
-880                file_name = filename
-881            dump_to_json(self, file_name)
-882        elif datatype == "pickle":
-883            dump_object(self, filename, **kwargs)
-884        else:
-885            raise Exception("Unknown datatype " + str(datatype))
+            
877    def dump(self, filename, datatype="json.gz", **kwargs):
+878        """Dumps the Corr into a file of chosen type
+879        Parameters
+880        ----------
+881        filename : str
+882            Name of the file to be saved.
+883        datatype : str
+884            Format of the exported file. Supported formats include
+885            "json.gz" and "pickle"
+886        path : str
+887            specifies a custom path for the file (default '.')
+888        """
+889        if datatype == "json.gz":
+890            from .input.json import dump_to_json
+891            if 'path' in kwargs:
+892                file_name = kwargs.get('path') + '/' + filename
+893            else:
+894                file_name = filename
+895            dump_to_json(self, file_name)
+896        elif datatype == "pickle":
+897            dump_object(self, filename, **kwargs)
+898        else:
+899            raise Exception("Unknown datatype " + str(datatype))
 
@@ -4217,8 +4264,8 @@ specifies a custom path for the file (default '.')
-
887    def print(self, range=[0, None]):
-888        print(self.__repr__(range))
+            
901    def print(self, range=[0, None]):
+902        print(self.__repr__(range))
 
@@ -4236,8 +4283,8 @@ specifies a custom path for the file (default '.')
-
1050    def sqrt(self):
-1051        return self**0.5
+            
1064    def sqrt(self):
+1065        return self ** 0.5
 
@@ -4255,9 +4302,9 @@ specifies a custom path for the file (default '.')
-
1053    def log(self):
-1054        newcontent = [None if (item is None) else np.log(item) for item in self.content]
-1055        return Corr(newcontent, prange=self.prange)
+            
1067    def log(self):
+1068        newcontent = [None if _check_for_none(self, item) else np.log(item) for item in self.content]
+1069        return Corr(newcontent, prange=self.prange)
 
@@ -4275,9 +4322,9 @@ specifies a custom path for the file (default '.')
-
1057    def exp(self):
-1058        newcontent = [None if (item is None) else np.exp(item) for item in self.content]
-1059        return Corr(newcontent, prange=self.prange)
+            
1071    def exp(self):
+1072        newcontent = [None if _check_for_none(self, item) else np.exp(item) for item in self.content]
+1073        return Corr(newcontent, prange=self.prange)
 
@@ -4295,8 +4342,8 @@ specifies a custom path for the file (default '.')
-
1072    def sin(self):
-1073        return self._apply_func_to_corr(np.sin)
+            
1086    def sin(self):
+1087        return self._apply_func_to_corr(np.sin)
 
@@ -4314,8 +4361,8 @@ specifies a custom path for the file (default '.')
-
1075    def cos(self):
-1076        return self._apply_func_to_corr(np.cos)
+            
1089    def cos(self):
+1090        return self._apply_func_to_corr(np.cos)
 
@@ -4333,8 +4380,8 @@ specifies a custom path for the file (default '.')
-
1078    def tan(self):
-1079        return self._apply_func_to_corr(np.tan)
+            
1092    def tan(self):
+1093        return self._apply_func_to_corr(np.tan)
 
@@ -4352,8 +4399,8 @@ specifies a custom path for the file (default '.')
-
1081    def sinh(self):
-1082        return self._apply_func_to_corr(np.sinh)
+            
1095    def sinh(self):
+1096        return self._apply_func_to_corr(np.sinh)
 
@@ -4371,8 +4418,8 @@ specifies a custom path for the file (default '.')
-
1084    def cosh(self):
-1085        return self._apply_func_to_corr(np.cosh)
+            
1098    def cosh(self):
+1099        return self._apply_func_to_corr(np.cosh)
 
@@ -4390,8 +4437,8 @@ specifies a custom path for the file (default '.')
-
1087    def tanh(self):
-1088        return self._apply_func_to_corr(np.tanh)
+            
1101    def tanh(self):
+1102        return self._apply_func_to_corr(np.tanh)
 
@@ -4409,8 +4456,8 @@ specifies a custom path for the file (default '.')
-
1090    def arcsin(self):
-1091        return self._apply_func_to_corr(np.arcsin)
+            
1104    def arcsin(self):
+1105        return self._apply_func_to_corr(np.arcsin)
 
@@ -4428,8 +4475,8 @@ specifies a custom path for the file (default '.')
-
1093    def arccos(self):
-1094        return self._apply_func_to_corr(np.arccos)
+            
1107    def arccos(self):
+1108        return self._apply_func_to_corr(np.arccos)
 
@@ -4447,8 +4494,8 @@ specifies a custom path for the file (default '.')
-
1096    def arctan(self):
-1097        return self._apply_func_to_corr(np.arctan)
+            
1110    def arctan(self):
+1111        return self._apply_func_to_corr(np.arctan)
 
@@ -4466,8 +4513,8 @@ specifies a custom path for the file (default '.')
-
1099    def arcsinh(self):
-1100        return self._apply_func_to_corr(np.arcsinh)
+            
1113    def arcsinh(self):
+1114        return self._apply_func_to_corr(np.arcsinh)
 
@@ -4485,8 +4532,8 @@ specifies a custom path for the file (default '.')
-
1102    def arccosh(self):
-1103        return self._apply_func_to_corr(np.arccosh)
+            
1116    def arccosh(self):
+1117        return self._apply_func_to_corr(np.arccosh)
 
@@ -4504,8 +4551,8 @@ specifies a custom path for the file (default '.')
-
1105    def arctanh(self):
-1106        return self._apply_func_to_corr(np.arctanh)
+            
1119    def arctanh(self):
+1120        return self._apply_func_to_corr(np.arctanh)
 
@@ -4545,62 +4592,62 @@ specifies a custom path for the file (default '.')
-
1141    def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None):
-1142        r''' Project large correlation matrix to lowest states
-1143
-1144        This method can be used to reduce the size of an (N x N) correlation matrix
-1145        to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise
-1146        is still small.
-1147
-1148        Parameters
-1149        ----------
-1150        Ntrunc: int
-1151            Rank of the target matrix.
-1152        tproj: int
-1153            Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method.
-1154            The default value is 3.
-1155        t0proj: int
-1156            Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly
-1157            discouraged for O(a) improved theories, since the correctness of the procedure
-1158            cannot be granted in this case. The default value is 2.
-1159        basematrix : Corr
-1160            Correlation matrix that is used to determine the eigenvectors of the
-1161            lowest states based on a GEVP. basematrix is taken to be the Corr itself if
-1162            is is not specified.
-1163
-1164        Notes
-1165        -----
-1166        We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving
-1167        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}$
-1168        and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the
-1169        resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via
-1170        $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large
-1171        correlation matrix and to remove some noise that is added by irrelevant operators.
-1172        This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated
-1173        bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$.
-1174        '''
-1175
-1176        if self.N == 1:
-1177            raise Exception('Method cannot be applied to one-dimensional correlators.')
-1178        if basematrix is None:
-1179            basematrix = self
-1180        if Ntrunc >= basematrix.N:
-1181            raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N))
-1182        if basematrix.N != self.N:
-1183            raise Exception('basematrix and targetmatrix have to be of the same size.')
-1184
-1185        evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc]
-1186
-1187        tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object)
-1188        rmat = []
-1189        for t in range(basematrix.T):
-1190            for i in range(Ntrunc):
-1191                for j in range(Ntrunc):
-1192                    tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j]
-1193            rmat.append(np.copy(tmpmat))
-1194
-1195        newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)]
-1196        return Corr(newcontent)
+            
1155    def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None):
+1156        r''' Project large correlation matrix to lowest states
+1157
+1158        This method can be used to reduce the size of an (N x N) correlation matrix
+1159        to (Ntrunc x Ntrunc) by solving a GEVP at very early times where the noise
+1160        is still small.
+1161
+1162        Parameters
+1163        ----------
+1164        Ntrunc: int
+1165            Rank of the target matrix.
+1166        tproj: int
+1167            Time where the eigenvectors are evaluated, corresponds to ts in the GEVP method.
+1168            The default value is 3.
+1169        t0proj: int
+1170            Time where the correlation matrix is inverted. Choosing t0proj=1 is strongly
+1171            discouraged for O(a) improved theories, since the correctness of the procedure
+1172            cannot be granted in this case. The default value is 2.
+1173        basematrix : Corr
+1174            Correlation matrix that is used to determine the eigenvectors of the
+1175            lowest states based on a GEVP. basematrix is taken to be the Corr itself if
+1176            is is not specified.
+1177
+1178        Notes
+1179        -----
+1180        We have the basematrix $C(t)$ and the target matrix $G(t)$. We start by solving
+1181        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}$
+1182        and $t_0 \equiv t_{0, \mathrm{proj}}$. The target matrix is projected onto the subspace of the
+1183        resulting eigenvectors $v_n, n=1,\dots,N_\mathrm{trunc}$ via
+1184        $$G^\prime_{i, j}(t) = (v_i, G(t) v_j)$$. This allows to reduce the size of a large
+1185        correlation matrix and to remove some noise that is added by irrelevant operators.
+1186        This may allow to use the GEVP on $G(t)$ at late times such that the theoretically motivated
+1187        bound $t_0 \leq t/2$ holds, since the condition number of $G(t)$ is decreased, compared to $C(t)$.
+1188        '''
+1189
+1190        if self.N == 1:
+1191            raise Exception('Method cannot be applied to one-dimensional correlators.')
+1192        if basematrix is None:
+1193            basematrix = self
+1194        if Ntrunc >= basematrix.N:
+1195            raise Exception('Cannot truncate using Ntrunc <= %d' % (basematrix.N))
+1196        if basematrix.N != self.N:
+1197            raise Exception('basematrix and targetmatrix have to be of the same size.')
+1198
+1199        evecs = basematrix.GEVP(t0proj, tproj, sort=None)[:Ntrunc]
+1200
+1201        tmpmat = np.empty((Ntrunc, Ntrunc), dtype=object)
+1202        rmat = []
+1203        for t in range(basematrix.T):
+1204            for i in range(Ntrunc):
+1205                for j in range(Ntrunc):
+1206                    tmpmat[i][j] = evecs[i].T @ self[t] @ evecs[j]
+1207            rmat.append(np.copy(tmpmat))
+1208
+1209        newcontent = [None if (self.content[t] is None) else rmat[t] for t in range(self.T)]
+1210        return Corr(newcontent)