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

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