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

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