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

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