sparse-ir 1.1.7__py3-none-any.whl → 2.0.0a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sparse_ir/__init__.py +33 -15
- sparse_ir/abstract.py +70 -82
- sparse_ir/augment.py +27 -17
- sparse_ir/basis.py +130 -239
- sparse_ir/basis_set.py +99 -57
- sparse_ir/dlr.py +131 -88
- sparse_ir/kernel.py +54 -477
- sparse_ir/poly.py +221 -476
- sparse_ir/sampling.py +260 -371
- sparse_ir/sve.py +56 -358
- sparse_ir-2.0.0a2.dist-info/METADATA +23 -0
- sparse_ir-2.0.0a2.dist-info/RECORD +16 -0
- {sparse_ir-1.1.7.dist-info → sparse_ir-2.0.0a2.dist-info}/WHEEL +1 -1
- sparse_ir/_gauss.py +0 -260
- sparse_ir/_roots.py +0 -140
- sparse_ir/adapter.py +0 -267
- sparse_ir/svd.py +0 -102
- sparse_ir-1.1.7.dist-info/METADATA +0 -155
- sparse_ir-1.1.7.dist-info/RECORD +0 -20
- {sparse_ir-1.1.7.dist-info → sparse_ir-2.0.0a2.dist-info/licenses}/LICENSE.txt +0 -0
- {sparse_ir-1.1.7.dist-info → sparse_ir-2.0.0a2.dist-info}/top_level.txt +0 -0
sparse_ir/basis.py
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
"""
|
2
|
+
High-level Python classes for FiniteTempBasis
|
3
|
+
"""
|
4
|
+
from typing import Optional
|
4
5
|
import numpy as np
|
5
|
-
from
|
6
|
-
|
7
|
-
from . import
|
8
|
-
from . import
|
9
|
-
from . import
|
10
|
-
from . import
|
11
|
-
|
12
|
-
|
13
|
-
class FiniteTempBasis(abstract.AbstractBasis):
|
6
|
+
from pylibsparseir.core import basis_new, basis_get_size, basis_get_svals, basis_get_u, basis_get_v, basis_get_uhat, basis_get_default_tau_sampling_points, basis_get_default_omega_sampling_points, basis_get_default_matsubara_sampling_points
|
7
|
+
from pylibsparseir.constants import SPIR_STATISTICS_FERMIONIC, SPIR_STATISTICS_BOSONIC
|
8
|
+
from .kernel import LogisticKernel
|
9
|
+
from .abstract import AbstractBasis
|
10
|
+
from .sve import SVEResult
|
11
|
+
from .poly import PiecewiseLegendrePolyVector, PiecewiseLegendrePolyFTVector, FunctionSet, FunctionSetFT
|
12
|
+
|
13
|
+
class FiniteTempBasis(AbstractBasis):
|
14
14
|
r"""Intermediate representation (IR) basis for given temperature.
|
15
15
|
|
16
16
|
For a continuation kernel from real frequencies, `ω` ∈ [-ωmax, ωmax], to
|
@@ -40,103 +40,89 @@ class FiniteTempBasis(abstract.AbstractBasis):
|
|
40
40
|
gl = basis.s * basis.v(2.5)
|
41
41
|
giw = gl @ basis.uhat([1, 3, 5, 7])
|
42
42
|
"""
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else:
|
60
|
-
# Sanity check of provided sve_result
|
61
|
-
# TODO: check for the kernel type/name or eps?
|
62
|
-
new_kernel_lambda = self._kernel.lambda_
|
63
|
-
sve_kernel_lambda = sve_result.K.lambda_
|
64
|
-
if not np.allclose(new_kernel_lambda, sve_kernel_lambda,
|
65
|
-
atol=1e-12, rtol=4e-16):
|
66
|
-
raise ValueError("Kernel mismatch:\n"
|
67
|
-
"provided SVE result is incompatible with "
|
68
|
-
"kernel parameters (lambda_ == beta*wmax)")
|
69
|
-
|
70
|
-
self._sve_result = sve_result
|
43
|
+
|
44
|
+
def __init__(self, statistics: str, beta: float, wmax: float, eps: float, sve_result: Optional[SVEResult] = None, max_size: int =-1):
|
45
|
+
"""
|
46
|
+
Initialize finite temperature basis.
|
47
|
+
|
48
|
+
Parameters:
|
49
|
+
-----------
|
50
|
+
statistics : str
|
51
|
+
'F' for fermions, 'B' for bosons
|
52
|
+
beta : float
|
53
|
+
Inverse temperature
|
54
|
+
wmax : float
|
55
|
+
Frequency cutoff
|
56
|
+
eps : float
|
57
|
+
Accuracy threshold
|
58
|
+
"""
|
71
59
|
self._statistics = statistics
|
72
60
|
self._beta = beta
|
73
61
|
self._wmax = wmax
|
62
|
+
self._lambda = beta * wmax
|
74
63
|
|
75
|
-
|
76
|
-
if
|
77
|
-
self.
|
64
|
+
# Create kernel
|
65
|
+
if statistics == 'F' or statistics == 'B':
|
66
|
+
self._kernel = LogisticKernel(self._lambda)
|
78
67
|
else:
|
79
|
-
|
80
|
-
|
81
|
-
# The polynomials are scaled to the new variables by transforming the
|
82
|
-
# knots according to: tau = beta/2 * (x + 1), w = wmax * y. Scaling
|
83
|
-
# the data is not necessary as the normalization is inferred.
|
84
|
-
self._u = u.__class__(u.data, beta/2 * (u.knots + 1), beta/2 * u.dx, u.symm)
|
85
|
-
self._v = v.__class__(v.data, wmax * v.knots, wmax * v.dx, v.symm)
|
86
|
-
|
87
|
-
# The singular values are scaled to match the change of variables, with
|
88
|
-
# the additional complexity that the kernel may have an additional
|
89
|
-
# power of w.
|
90
|
-
self._s = np.sqrt(beta/2 * wmax) * (wmax**(-self.kernel.ypower)) * s
|
91
|
-
|
92
|
-
# HACK: as we don't yet support Fourier transforms on anything but the
|
93
|
-
# unit interval, we need to scale the underlying data.
|
94
|
-
uhat_base_full = poly.PiecewiseLegendrePoly(
|
95
|
-
np.sqrt(beta) * sve_result.u.data, sve_result.u,
|
96
|
-
symm=sve_result.u.symm)
|
97
|
-
conv_radius = 40 * self.kernel.lambda_
|
98
|
-
even_odd = {'F': 'odd', 'B': 'even'}[statistics]
|
99
|
-
self._uhat_full = poly.PiecewiseLegendreFT(uhat_base_full, even_odd,
|
100
|
-
n_asymp=conv_radius)
|
101
|
-
self._uhat = self._uhat_full[:s.size]
|
68
|
+
raise ValueError(f"Invalid statistics: {statistics} expected 'F' or 'B'")
|
102
69
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
70
|
+
# Compute SVE
|
71
|
+
if sve_result is None:
|
72
|
+
self._sve = SVEResult(self._kernel, eps)
|
73
|
+
else:
|
74
|
+
self._sve = sve_result
|
108
75
|
|
109
|
-
|
110
|
-
|
76
|
+
# Create basis
|
77
|
+
stats_int = SPIR_STATISTICS_FERMIONIC if statistics == 'F' else SPIR_STATISTICS_BOSONIC
|
78
|
+
self._ptr = basis_new(stats_int, self._beta, self._wmax, self._kernel._ptr, self._sve._ptr, max_size)
|
111
79
|
|
112
|
-
|
113
|
-
|
80
|
+
u_funcs = FunctionSet(basis_get_u(self._ptr))
|
81
|
+
v_funcs = FunctionSet(basis_get_v(self._ptr))
|
82
|
+
uhat_funcs = FunctionSetFT(basis_get_uhat(self._ptr))
|
114
83
|
|
115
|
-
|
116
|
-
|
84
|
+
self._s = basis_get_svals(self._ptr)
|
85
|
+
self._u = PiecewiseLegendrePolyVector(u_funcs, 0, self._beta)
|
86
|
+
self._v = PiecewiseLegendrePolyVector(v_funcs, -self._wmax, self._wmax)
|
87
|
+
self._uhat = PiecewiseLegendrePolyFTVector(uhat_funcs)
|
117
88
|
|
118
89
|
@property
|
119
|
-
def
|
90
|
+
def statistics(self):
|
91
|
+
"""Quantum statistic ('F' for fermionic, 'B' for bosonic)"""
|
92
|
+
return self._statistics
|
120
93
|
|
121
94
|
@property
|
122
|
-
def
|
95
|
+
def beta(self):
|
96
|
+
"""Inverse temperature"""
|
97
|
+
return self._beta
|
123
98
|
|
124
99
|
@property
|
125
|
-
def
|
100
|
+
def wmax(self):
|
101
|
+
"""Real frequency cutoff"""
|
102
|
+
return self._wmax
|
126
103
|
|
127
104
|
@property
|
128
|
-
def
|
105
|
+
def lambda_(self):
|
106
|
+
"""Basis cutoff parameter, Λ = β * wmax"""
|
107
|
+
return self._lambda
|
129
108
|
|
130
109
|
@property
|
131
|
-
def
|
110
|
+
def size(self):
|
111
|
+
return self._s.size
|
132
112
|
|
133
113
|
@property
|
134
|
-
def s(self)
|
114
|
+
def s(self):
|
135
115
|
"""Vector of singular values of the continuation kernel"""
|
116
|
+
if self._s is None:
|
117
|
+
self._s = basis_get_svals(self._ptr)
|
136
118
|
return self._s
|
137
119
|
|
138
120
|
@property
|
139
|
-
def
|
121
|
+
def u(self):
|
122
|
+
return self._u
|
123
|
+
|
124
|
+
@property
|
125
|
+
def v(self):
|
140
126
|
r"""Basis functions on the real frequency axis.
|
141
127
|
|
142
128
|
Set of IR basis functions on the real frequency (omega) axis, where
|
@@ -162,49 +148,67 @@ class FiniteTempBasis(abstract.AbstractBasis):
|
|
162
148
|
"""
|
163
149
|
return self._v
|
164
150
|
|
151
|
+
@property
|
152
|
+
def uhat(self):
|
153
|
+
return self._uhat
|
154
|
+
|
165
155
|
@property
|
166
156
|
def significance(self):
|
167
|
-
|
157
|
+
"""Relative significance of basis functions."""
|
158
|
+
return self.s / self.s[0]
|
168
159
|
|
169
160
|
@property
|
170
161
|
def accuracy(self):
|
171
|
-
|
162
|
+
"""Overall accuracy bound."""
|
163
|
+
return self.s[-1] / self.s[0]
|
172
164
|
|
173
165
|
@property
|
174
|
-
def
|
175
|
-
"""
|
176
|
-
return self.
|
166
|
+
def shape(self):
|
167
|
+
"""Shape of the basis function set"""
|
168
|
+
return self.s.shape
|
177
169
|
|
178
|
-
|
179
|
-
|
180
|
-
return self.
|
181
|
-
|
182
|
-
def
|
183
|
-
if npoints is None:
|
184
|
-
npoints = self.size
|
185
|
-
x = _default_sampling_points(self._sve_result.u, npoints)
|
186
|
-
return self._beta/2 * (x + 1)
|
187
|
-
|
188
|
-
def default_matsubara_sampling_points(self, *, npoints=None,
|
189
|
-
positive_only=False):
|
190
|
-
if npoints is None:
|
191
|
-
npoints = self.size
|
192
|
-
return _default_matsubara_sampling_points(self._uhat_full, npoints,
|
193
|
-
positive_only=positive_only)
|
194
|
-
|
195
|
-
def default_omega_sampling_points(self, *, npoints=None):
|
170
|
+
def default_tau_sampling_points(self, npoints=None):
|
171
|
+
"""Get default tau sampling points."""
|
172
|
+
return basis_get_default_tau_sampling_points(self._ptr)
|
173
|
+
|
174
|
+
def default_omega_sampling_points(self, npoints=None):
|
196
175
|
"""Return default sampling points in imaginary time.
|
197
176
|
|
198
177
|
Arguments:
|
199
178
|
npoints (int):
|
200
179
|
Minimum number of sampling points to return.
|
201
180
|
|
202
|
-
.. versionadded
|
181
|
+
.. versionadded:: 1.1
|
182
|
+
"""
|
183
|
+
from pylibsparseir.core import basis_get_default_omega_sampling_points
|
184
|
+
return basis_get_default_omega_sampling_points(self._ptr)
|
185
|
+
|
186
|
+
def default_matsubara_sampling_points(self, npoints=None, positive_only=False):
|
187
|
+
"""Get default Matsubara sampling points."""
|
188
|
+
return basis_get_default_matsubara_sampling_points(self._ptr, positive_only)
|
189
|
+
|
190
|
+
def __repr__(self):
|
191
|
+
return (f"FiniteTempBasis(statistics='{self.statistics}', "
|
192
|
+
f"beta={self.beta}, wmax={self.wmax}, size={self.size})")
|
193
|
+
|
194
|
+
def __getitem__(self, index):
|
195
|
+
"""Return basis functions/singular values for given index/indices.
|
196
|
+
|
197
|
+
This can be used to truncate the basis to the n most significant
|
198
|
+
singular values: basis[:3].
|
203
199
|
"""
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
200
|
+
# TODO: Implement basis truncation when C API supports it
|
201
|
+
raise NotImplementedError("Basis truncation not yet implemented in C API")
|
202
|
+
|
203
|
+
@property
|
204
|
+
def kernel(self):
|
205
|
+
"""The kernel used to generate the basis."""
|
206
|
+
return self._kernel
|
207
|
+
|
208
|
+
@property
|
209
|
+
def sve_result(self):
|
210
|
+
"""The singular value expansion result."""
|
211
|
+
return self._sve
|
208
212
|
|
209
213
|
def rescale(self, new_beta):
|
210
214
|
"""Return a basis for different temperature.
|
@@ -213,137 +217,24 @@ class FiniteTempBasis(abstract.AbstractBasis):
|
|
213
217
|
temperature. Note that this implies a different UV cutoff ``wmax``,
|
214
218
|
since ``lambda_ == beta * wmax`` stays constant.
|
215
219
|
"""
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
+
# Calculate new beta and wmax that give the desired lambda
|
221
|
+
# We keep the ratio beta/wmax constant
|
222
|
+
ratio = self.beta / self.wmax
|
223
|
+
new_wmax = np.sqrt(new_lambda / ratio)
|
224
|
+
new_beta = new_lambda / new_wmax
|
225
|
+
|
226
|
+
# Get epsilon from the current basis accuracy
|
227
|
+
eps = self.accuracy
|
220
228
|
|
229
|
+
return FiniteTempBasis(self.statistics, new_beta, new_wmax, eps)
|
221
230
|
|
222
|
-
|
223
|
-
|
224
|
-
sve_result: tuple = None
|
225
|
-
)-> Tuple[FiniteTempBasis, FiniteTempBasis]:
|
231
|
+
|
232
|
+
def finite_temp_bases(beta, wmax, eps=None, sve_result=None):
|
226
233
|
"""Construct FiniteTempBasis objects for fermion and bosons
|
227
234
|
|
228
235
|
Construct FiniteTempBasis objects for fermion and bosons using
|
229
236
|
the same LogisticKernel instance.
|
230
237
|
"""
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
basis_b = FiniteTempBasis("B", beta, wmax, eps, sve_result=sve_result)
|
235
|
-
return basis_f, basis_b
|
236
|
-
|
237
|
-
|
238
|
-
def _default_sampling_points(u, L):
|
239
|
-
if u.xmin != -1 or u.xmax != 1:
|
240
|
-
raise ValueError("expecting unscaled functions here")
|
241
|
-
|
242
|
-
if L < u.size:
|
243
|
-
# For orthogonal polynomials (the high-T limit of IR), we know that the
|
244
|
-
# ideal sampling points for a basis of size L are the roots of the L-th
|
245
|
-
# polynomial. We empirically find that these stay good sampling points
|
246
|
-
# for our kernels (probably because the kernels are totally positive).
|
247
|
-
x0 = u[L].roots()
|
248
|
-
else:
|
249
|
-
# If we do not have enough polynomials in the basis, we approximate the
|
250
|
-
# roots of the L'th polynomial by the extrema of the (L-1)'st basis
|
251
|
-
# function, which is sensible due to the strong interleaving property
|
252
|
-
# of these functions' roots.
|
253
|
-
maxima = u[-1].deriv().roots()
|
254
|
-
|
255
|
-
# Putting the sampling points right at [0, beta], which would be the
|
256
|
-
# local extrema, is slightly worse conditioned than putting it in the
|
257
|
-
# middel. This can be understood by the fact that the roots never
|
258
|
-
# occur right at the border.
|
259
|
-
left = .5 * (maxima[:1] + u.xmin)
|
260
|
-
right = .5 * (maxima[-1:] + u.xmax)
|
261
|
-
x0 = np.concatenate([left, maxima, right])
|
262
|
-
|
263
|
-
if x0.size != L:
|
264
|
-
warn(f"Requesting {L} sampling points for corresponding basis size,\n"
|
265
|
-
f"but {x0.size} were returned. This may indiciate a problem "
|
266
|
-
f"with precision.", UserWarning, 3)
|
267
|
-
return x0
|
268
|
-
|
269
|
-
|
270
|
-
def _default_matsubara_sampling_points(uhat, L, *, fence=False, positive_only=False):
|
271
|
-
l_requested = L
|
272
|
-
|
273
|
-
# The number of sign changes is always odd for bosonic basis (freq=='even')
|
274
|
-
# and even for fermionic basis (freq='odd'). So in order to get at least
|
275
|
-
# as many sign changes as basis functions.
|
276
|
-
if uhat.freq == 'odd' and l_requested % 2 == 1:
|
277
|
-
l_requested += 1
|
278
|
-
elif uhat.freq == 'even' and l_requested % 2 == 0:
|
279
|
-
l_requested += 1
|
280
|
-
|
281
|
-
if l_requested < uhat.size:
|
282
|
-
# As with the zeros, the sign changes provide excellent sampling points
|
283
|
-
wn = uhat[l_requested].sign_changes(positive_only=positive_only)
|
284
|
-
else:
|
285
|
-
# As a fallback, use the (discrete) extrema of the corresponding
|
286
|
-
# highest-order basis function in Matsubara. This turns out to be okay.
|
287
|
-
polyhat = uhat[-1]
|
288
|
-
wn = polyhat.extrema(positive_only=positive_only)
|
289
|
-
|
290
|
-
# For bosonic bases, we must explicitly include the zero frequency,
|
291
|
-
# otherwise the condition number blows up.
|
292
|
-
if wn[0] % 2 == 0:
|
293
|
-
wn = np.unique(np.hstack((0, wn)))
|
294
|
-
|
295
|
-
expected_size = l_requested
|
296
|
-
if positive_only:
|
297
|
-
expected_size = (expected_size + 1) // 2
|
298
|
-
|
299
|
-
if wn.size != expected_size:
|
300
|
-
warn(f"Requesting {expected_size} {uhat.freq} sampling frequencies\n"
|
301
|
-
f"for basis size L={L}, but {wn.size} were returned. This may\n"
|
302
|
-
f"indiciate a problem with precision.", UserWarning, 3)
|
303
|
-
if fence:
|
304
|
-
wn = _fence_matsubara_sampling(wn, positive_only)
|
305
|
-
return wn
|
306
|
-
|
307
|
-
|
308
|
-
def _fence_matsubara_sampling(wn, positive_only):
|
309
|
-
# While the condition number for sparse sampling in tau saturates at a
|
310
|
-
# modest level, the conditioning in Matsubara steadily deteriorates due
|
311
|
-
# to the fact that we are not free to set sampling points continuously.
|
312
|
-
# At double precision, tau sampling is better conditioned than iwn
|
313
|
-
# by a factor of ~4 (still OK). To battle this, we fence the largest
|
314
|
-
# frequency with two carefully chosen oversampling points, which brings
|
315
|
-
# the two sampling problems within a factor of 2.
|
316
|
-
wn_outer = wn[-1:] if positive_only else wn[[0, -1]]
|
317
|
-
wn_diff = 2 * np.round(0.025 * wn_outer).astype(int)
|
318
|
-
if wn.size >= 20:
|
319
|
-
wn = np.hstack([wn, wn_outer - np.sign(wn_outer) * wn_diff])
|
320
|
-
if wn.size >= 42:
|
321
|
-
wn = np.hstack([wn, wn_outer + np.sign(wn_outer) * wn_diff])
|
322
|
-
return np.unique(wn)
|
323
|
-
|
324
|
-
|
325
|
-
def _get_kernel(statistics, lambda_, kernel):
|
326
|
-
if statistics not in 'BF':
|
327
|
-
raise ValueError("statistics must either be 'B' (for bosonic basis) "
|
328
|
-
"or 'F' (for fermionic basis)")
|
329
|
-
if kernel is None:
|
330
|
-
kernel = _kernel.LogisticKernel(lambda_)
|
331
|
-
else:
|
332
|
-
try:
|
333
|
-
lambda_kernel = kernel.lambda_
|
334
|
-
except AttributeError:
|
335
|
-
pass
|
336
|
-
else:
|
337
|
-
if not np.allclose(lambda_kernel, lambda_, atol=0, rtol=4e-16):
|
338
|
-
raise ValueError("lambda of kernel and basis mismatch")
|
339
|
-
return kernel
|
340
|
-
|
341
|
-
|
342
|
-
def _slice_to_size(index):
|
343
|
-
if not isinstance(index, slice):
|
344
|
-
raise ValueError("argument must be a slice (`n:m`)")
|
345
|
-
if index.start is not None and index.start != 0:
|
346
|
-
raise ValueError("slice must start at zero")
|
347
|
-
if index.step is not None and index.step != 1:
|
348
|
-
raise ValueError("slice must step in ones")
|
349
|
-
return index.stop
|
238
|
+
fermion_basis = FiniteTempBasis('F', beta, wmax, eps, sve_result=sve_result)
|
239
|
+
boson_basis = FiniteTempBasis('B', beta, wmax, eps, sve_result=sve_result)
|
240
|
+
return fermion_basis, boson_basis
|
sparse_ir/basis_set.py
CHANGED
@@ -1,101 +1,143 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
from . import basis
|
4
|
-
from . import sampling
|
1
|
+
"""
|
2
|
+
Basis set functionality for SparseIR.
|
5
3
|
|
4
|
+
This module provides a convenience class that holds both fermionic and bosonic
|
5
|
+
bases along with their associated sampling objects.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .basis import FiniteTempBasis, finite_temp_bases
|
9
|
+
from .sampling import TauSampling, MatsubaraSampling
|
6
10
|
|
7
|
-
class FiniteTempBasisSet:
|
8
|
-
"""Class for holding IR bases and sparse-sampling objects.
|
9
11
|
|
12
|
+
class FiniteTempBasisSet:
|
13
|
+
"""
|
14
|
+
Class for holding IR bases and sparse-sampling objects.
|
15
|
+
|
10
16
|
An object of this class holds IR bases for fermions and bosons
|
11
17
|
and associated sparse-sampling objects.
|
12
|
-
|
13
|
-
Attributes
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
|
19
|
+
Attributes
|
20
|
+
----------
|
21
|
+
basis_f : FiniteTempBasis
|
22
|
+
Fermion basis
|
23
|
+
basis_b : FiniteTempBasis
|
24
|
+
Boson basis
|
25
|
+
smpl_tau_f : TauSampling
|
26
|
+
Sparse sampling for tau & fermion
|
27
|
+
smpl_tau_b : TauSampling
|
28
|
+
Sparse sampling for tau & boson
|
29
|
+
smpl_wn_f : MatsubaraSampling
|
30
|
+
Sparse sampling for Matsubara frequency & fermion
|
31
|
+
smpl_wn_b : MatsubaraSampling
|
32
|
+
Sparse sampling for Matsubara frequency & boson
|
26
33
|
"""
|
27
|
-
|
34
|
+
|
35
|
+
def __init__(self, beta, wmax, eps=None, sve_result=None, use_positive_taus=False):
|
28
36
|
"""
|
29
|
-
Create basis sets for fermion and boson and
|
30
|
-
|
37
|
+
Create basis sets for fermion and boson and associated sampling objects.
|
38
|
+
|
31
39
|
Fermion and bosonic bases are constructed by SVE of the logistic kernel.
|
40
|
+
|
41
|
+
Parameters
|
42
|
+
----------
|
43
|
+
beta : float
|
44
|
+
Inverse temperature
|
45
|
+
wmax : float
|
46
|
+
Cut-off frequency
|
47
|
+
eps : float, optional
|
48
|
+
Tolerance parameter for the basis construction.
|
49
|
+
If not provided, a default value will be used.
|
50
|
+
sve_result : SVEResult, optional
|
51
|
+
Pre-computed SVE result to use for basis construction.
|
52
|
+
If not provided, SVE will be computed internally.
|
53
|
+
use_positive_taus : bool, optional
|
54
|
+
If `use_positive_taus=False`, the sampling points are within
|
55
|
+
the range [-β/2, β/2] and the distribution is symmetric.
|
56
|
+
If `use_positive_taus=True`, the sampling points are
|
57
|
+
folded to the positive tau domain [0, β), which is
|
58
|
+
the default behavior of sparseir 1.x.x.
|
32
59
|
"""
|
33
60
|
if sve_result is None:
|
34
61
|
# Create bases by sve of the logistic kernel
|
35
|
-
self.basis_f, self.basis_b =
|
62
|
+
self.basis_f, self.basis_b = finite_temp_bases(beta, wmax, eps)
|
36
63
|
else:
|
37
64
|
# Create bases using the given sve results
|
38
|
-
self.basis_f =
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
65
|
+
self.basis_f = FiniteTempBasis(
|
66
|
+
"F", beta, wmax, eps, sve_result=sve_result
|
67
|
+
)
|
68
|
+
self.basis_b = FiniteTempBasis(
|
69
|
+
"B", beta, wmax, eps, sve_result=sve_result
|
70
|
+
)
|
71
|
+
|
43
72
|
# Tau sampling
|
44
|
-
self.smpl_tau_f =
|
45
|
-
self.smpl_tau_b =
|
46
|
-
|
73
|
+
self.smpl_tau_f = TauSampling(self.basis_f, use_positive_taus=use_positive_taus)
|
74
|
+
self.smpl_tau_b = TauSampling(self.basis_b, use_positive_taus=use_positive_taus)
|
75
|
+
|
47
76
|
# Matsubara sampling
|
48
|
-
self.smpl_wn_f =
|
49
|
-
self.smpl_wn_b =
|
50
|
-
|
77
|
+
self.smpl_wn_f = MatsubaraSampling(self.basis_f)
|
78
|
+
self.smpl_wn_b = MatsubaraSampling(self.basis_b)
|
79
|
+
|
51
80
|
@property
|
52
81
|
def lambda_(self):
|
53
|
-
"""Ultra-violet cutoff of the kernel"""
|
82
|
+
"""Ultra-violet cutoff of the kernel."""
|
54
83
|
return self.basis_f.lambda_
|
55
|
-
|
84
|
+
|
56
85
|
@property
|
57
86
|
def beta(self):
|
58
|
-
"""Inverse temperature"""
|
87
|
+
"""Inverse temperature."""
|
59
88
|
return self.basis_f.beta
|
60
|
-
|
89
|
+
|
61
90
|
@property
|
62
91
|
def wmax(self):
|
63
|
-
"""Cut-off frequency"""
|
92
|
+
"""Cut-off frequency."""
|
64
93
|
return self.basis_f.wmax
|
65
|
-
|
94
|
+
|
66
95
|
@property
|
67
96
|
def accuracy(self):
|
68
|
-
"""Accuracy of the bases"""
|
97
|
+
"""Accuracy of the bases."""
|
69
98
|
return self.basis_f.accuracy
|
70
|
-
|
99
|
+
|
71
100
|
@property
|
72
101
|
def sve_result(self):
|
73
|
-
"""Result of singular value expansion"""
|
102
|
+
"""Result of singular value expansion."""
|
74
103
|
return self.basis_f.sve_result
|
75
|
-
|
104
|
+
|
76
105
|
@property
|
77
106
|
def tau(self):
|
78
|
-
"""Sampling points in the imaginary-time domain"""
|
107
|
+
"""Sampling points in the imaginary-time domain."""
|
79
108
|
return self.smpl_tau_f.sampling_points
|
80
|
-
|
109
|
+
|
81
110
|
@property
|
82
111
|
def wn_f(self):
|
83
|
-
"""Sampling fermionic frequencies"""
|
112
|
+
"""Sampling fermionic frequencies."""
|
84
113
|
return self.smpl_wn_f.sampling_points
|
85
|
-
|
114
|
+
|
86
115
|
@property
|
87
116
|
def wn_b(self):
|
88
|
-
"""Sampling bosonic frequencies"""
|
117
|
+
"""Sampling bosonic frequencies."""
|
89
118
|
return self.smpl_wn_b.sampling_points
|
90
|
-
|
119
|
+
|
91
120
|
def rescale(self, new_beta):
|
92
|
-
"""
|
93
|
-
|
121
|
+
"""
|
122
|
+
Return a basis set for a different temperature.
|
123
|
+
|
94
124
|
Uses the same kernel with the same ``eps``, but a different
|
95
|
-
temperature.
|
125
|
+
temperature. Note that this implies a different UV cutoff ``wmax``,
|
96
126
|
since ``lambda_ == beta * wmax`` stays constant.
|
127
|
+
|
128
|
+
Parameters
|
129
|
+
----------
|
130
|
+
new_beta : float
|
131
|
+
New inverse temperature
|
132
|
+
|
133
|
+
Returns
|
134
|
+
-------
|
135
|
+
FiniteTempBasisSet
|
136
|
+
New basis set for the different temperature
|
97
137
|
"""
|
98
138
|
new_wmax = self.basis_f.lambda_ / new_beta
|
99
|
-
return FiniteTempBasisSet(
|
100
|
-
|
101
|
-
|
139
|
+
return FiniteTempBasisSet(
|
140
|
+
new_beta, new_wmax,
|
141
|
+
eps=self.basis_f.accuracy, # Use accuracy instead of eps
|
142
|
+
sve_result=self.basis_f.sve_result
|
143
|
+
)
|