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