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

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

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

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

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

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