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