sparse-ir 2.1.4__tar.gz → 2.1.6__tar.gz

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.
Files changed (44) hide show
  1. {sparse_ir-2.1.4/src/sparse_ir.egg-info → sparse_ir-2.1.6}/PKG-INFO +32 -19
  2. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/README.rst +30 -17
  3. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/pyproject.toml +10 -2
  4. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/_util.py +117 -10
  5. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/abstract.py +62 -30
  6. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/augment.py +118 -78
  7. sparse_ir-2.1.6/src/sparse_ir/basis.py +484 -0
  8. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/basis_set.py +15 -14
  9. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/dlr.py +89 -17
  10. sparse_ir-2.1.6/src/sparse_ir/kernel.py +151 -0
  11. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/poly.py +196 -103
  12. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/sampling.py +137 -55
  13. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/sve.py +45 -23
  14. {sparse_ir-2.1.4 → sparse_ir-2.1.6/src/sparse_ir.egg-info}/PKG-INFO +32 -19
  15. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir.egg-info/SOURCES.txt +4 -0
  16. sparse_ir-2.1.6/src/sparse_ir.egg-info/requires.txt +3 -0
  17. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_advanced_features.py +16 -7
  18. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_augment.py +108 -47
  19. sparse_ir-2.1.6/tests/test_basis.py +189 -0
  20. sparse_ir-2.1.6/tests/test_boundary.py +423 -0
  21. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_core.py +35 -44
  22. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_dlr.py +16 -11
  23. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_ffi_boundary.py +38 -2
  24. sparse_ir-2.1.6/tests/test_integration.py +115 -0
  25. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_kernel.py +16 -14
  26. sparse_ir-2.1.6/tests/test_oracle.py +292 -0
  27. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_poly.py +18 -0
  28. sparse_ir-2.1.6/tests/test_public_surface.py +235 -0
  29. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_sampling.py +22 -9
  30. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_sampling_advanced.py +7 -12
  31. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_sve.py +14 -23
  32. sparse_ir-2.1.4/src/sparse_ir/basis.py +0 -326
  33. sparse_ir-2.1.4/src/sparse_ir/kernel.py +0 -108
  34. sparse_ir-2.1.4/src/sparse_ir.egg-info/requires.txt +0 -3
  35. sparse_ir-2.1.4/tests/test_basis.py +0 -150
  36. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/LICENSE.txt +0 -0
  37. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/MANIFEST.in +0 -0
  38. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/setup.cfg +0 -0
  39. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/__init__.py +0 -0
  40. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir/_gauss.py +0 -0
  41. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir.egg-info/dependency_links.txt +0 -0
  42. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/src/sparse_ir.egg-info/top_level.txt +0 -0
  43. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_basis_set.py +0 -0
  44. {sparse_ir-2.1.4 → sparse_ir-2.1.6}/tests/test_sve_advanced.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sparse-ir
3
- Version: 2.1.4
3
+ Version: 2.1.6
4
4
  Summary: Python bindings for the libsparseir library, providing efficient sparse intermediate representation for many-body physics calculations
5
5
  Author: SpM-lab
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE.txt
21
21
  Requires-Dist: numpy
22
22
  Requires-Dist: scipy
23
- Requires-Dist: pylibsparseir<0.10.0,>=0.8.3
23
+ Requires-Dist: pylibsparseir<0.13.0,>=0.8.3
24
24
  Dynamic: license-file
25
25
 
26
26
  sparse-ir - A library for the intermediate representation of propagators
@@ -45,7 +45,7 @@ Install via `conda <https://anaconda.org/spm-lab/sparse-ir>`_::
45
45
 
46
46
  sparse-ir requires `numpy <https://numpy.org/>`_, `scipy <https://scipy.org/>`_,
47
47
  and `pylibsparseir <https://pypi.org/project/pylibsparseir>`_ (a thin Python wrapper
48
- for the `libsparseir <https://github.com/SpM-lab/libsparseir>`_ C API).
48
+ for the libsparseir C API from `sparse-ir-rs <https://github.com/SpM-lab/sparse-ir-rs>`_).
49
49
 
50
50
  To manually install the current development version, you can use the following::
51
51
 
@@ -78,13 +78,21 @@ Lichtenstein formula, FLEX, ... - are presented.
78
78
  Refer to the `API documentation`_ for more details on how to work
79
79
  with the python library.
80
80
 
81
+ The symbols and conventions are defined on the `notation page`_, shared with
82
+ the tutorial and the Julia and C libraries. In short: G(τ) = -⟨T c(τ) c†(0)⟩
83
+ for both statistics; Matsubara frequencies iν are passed as the reduced
84
+ frequency n, an integer with ν = nπ/β (odd for fermions, even for bosons);
85
+ G(iν) = ∫₀^β dτ e^{iντ} G(τ); and G(τ) is evaluated for τ in [-β, β], where
86
+ ``-0.0`` stands for 0⁻.
87
+
81
88
  There is also a `Julia library`_ and (currently somewhat restricted)
82
89
  `C library with Fortran bindings`_ available for the IR basis and sparse sampling.
83
90
 
84
- .. _comprehensive tutorial: https://spm-lab.github.io/sparse-ir-tutorial
91
+ .. _comprehensive tutorial: https://spm-lab.github.io/sparse-ir-tutorial-v2/
85
92
  .. _API documentation: https://sparse-ir.readthedocs.io
93
+ .. _notation page: https://spm-lab.github.io/sparse-ir-doc/src/notation.html
86
94
  .. _Julia library: https://github.com/SpM-lab/SparseIR.jl
87
- .. _C library with Fortran bindings: https://github.com/SpM-lab/libsparseir
95
+ .. _C library with Fortran bindings: https://github.com/SpM-lab/sparse-ir-rs
88
96
 
89
97
  Getting started
90
98
  ---------------
@@ -95,15 +103,16 @@ lines of Python code::
95
103
  import sparse_ir, numpy as np
96
104
  basis = sparse_ir.FiniteTempBasis('F', beta=10, wmax=8, eps=1e-6)
97
105
  stau = sparse_ir.TauSampling(basis)
98
- siw = sparse_ir.MatsubaraSampling(basis, positive_only=True)
106
+ siv = sparse_ir.MatsubaraSampling(basis, positive_only=True)
99
107
 
100
108
  # Solve the single impurity Anderson model coupled to a bath with a
101
- # semicircular states with unit half bandwidth.
109
+ # semicircular density of states with unit half bandwidth.
102
110
  U = 1.2
103
111
  def rho0w(w):
104
112
  return np.sqrt(1-w.clip(-1,1)**2) * 2/np.pi
105
113
 
106
- # Compute the IR basis coefficients for the non-interacting propagator
114
+ # Compute the IR basis coefficients G0_l = -S_l rho0_l for the
115
+ # non-interacting propagator
107
116
  rho0l = basis.v.overlap(rho0w)
108
117
  G0l = -basis.s * rho0l
109
118
 
@@ -116,23 +125,27 @@ lines of Python code::
116
125
  Gtau = stau.evaluate(Gl)
117
126
  Sigmatau = U**2 * Gtau**3
118
127
  Sigmal = stau.fit(Sigmatau)
119
- Sigmaiw = siw.evaluate(Sigmal)
120
- G0iw = siw.evaluate(G0l)
121
- Giw = 1/(1/G0iw - Sigmaiw)
122
- Gl = siw.fit(Giw)
128
+ Sigmaiv = siv.evaluate(Sigmal)
129
+ G0iv = siv.evaluate(G0l)
130
+ Giv = 1/(1/G0iv - Sigmaiv)
131
+ Gl = siv.fit(Giv)
123
132
 
124
133
  You may want to start with reading up on the `intermediate representation`_.
125
134
  It is tied to the analytic continuation of bosonic/fermionic spectral
126
135
  functions from (real) frequencies to imaginary time, a transformation mediated
127
- by a kernel ``K``. The kernel depends on a cutoff, which you should choose to
128
- be ``lambda_ >= beta * W``, where ``beta`` is the inverse temperature and ``W``
129
- is the bandwidth.
136
+ by the kernel K(τ, ω) = e^{-τω}/(1 + e^{-βω}). The basis depends on the
137
+ inverse temperature ``beta`` and on the frequency cutoff ``wmax``, which you
138
+ should choose so that the spectral function vanishes outside [-wmax, wmax].
139
+ In the dimensionless variables x = 2τ/β - 1 and y = ω/wmax, the kernel, and
140
+ hence its singular value expansion, depends on them only through the cutoff
141
+ Λ = ``beta * wmax`` (``basis.lambda_``).
130
142
 
131
143
  One can now perform a `singular value expansion`_ on this kernel, which
132
- generates two sets of orthonormal basis functions, one set ``v[l](w)`` for
133
- real frequency side ``w``, and one set ``u[l](tau)`` for the same obejct in
134
- imaginary (Euclidean) time ``tau``, together with a "coupling" strength
135
- ``s[l]`` between the two sides.
144
+ generates two sets of orthonormal basis functions, one set V_l(ω),
145
+ ``basis.v[l](w)``, on the real-frequency axis, orthonormal on
146
+ [-wmax, wmax], and one set U_l(τ), ``basis.u[l](tau)``, for the same object in
147
+ imaginary (Euclidean) time, orthonormal on [0, beta], together with the
148
+ singular values S_l, ``basis.s[l]``, which couple the two sides.
136
149
 
137
150
  By this construction, the imaginary time basis can be shown to be *optimal* in
138
151
  terms of compactness.
@@ -20,7 +20,7 @@ Install via `conda <https://anaconda.org/spm-lab/sparse-ir>`_::
20
20
 
21
21
  sparse-ir requires `numpy <https://numpy.org/>`_, `scipy <https://scipy.org/>`_,
22
22
  and `pylibsparseir <https://pypi.org/project/pylibsparseir>`_ (a thin Python wrapper
23
- for the `libsparseir <https://github.com/SpM-lab/libsparseir>`_ C API).
23
+ for the libsparseir C API from `sparse-ir-rs <https://github.com/SpM-lab/sparse-ir-rs>`_).
24
24
 
25
25
  To manually install the current development version, you can use the following::
26
26
 
@@ -53,13 +53,21 @@ Lichtenstein formula, FLEX, ... - are presented.
53
53
  Refer to the `API documentation`_ for more details on how to work
54
54
  with the python library.
55
55
 
56
+ The symbols and conventions are defined on the `notation page`_, shared with
57
+ the tutorial and the Julia and C libraries. In short: G(τ) = -⟨T c(τ) c†(0)⟩
58
+ for both statistics; Matsubara frequencies iν are passed as the reduced
59
+ frequency n, an integer with ν = nπ/β (odd for fermions, even for bosons);
60
+ G(iν) = ∫₀^β dτ e^{iντ} G(τ); and G(τ) is evaluated for τ in [-β, β], where
61
+ ``-0.0`` stands for 0⁻.
62
+
56
63
  There is also a `Julia library`_ and (currently somewhat restricted)
57
64
  `C library with Fortran bindings`_ available for the IR basis and sparse sampling.
58
65
 
59
- .. _comprehensive tutorial: https://spm-lab.github.io/sparse-ir-tutorial
66
+ .. _comprehensive tutorial: https://spm-lab.github.io/sparse-ir-tutorial-v2/
60
67
  .. _API documentation: https://sparse-ir.readthedocs.io
68
+ .. _notation page: https://spm-lab.github.io/sparse-ir-doc/src/notation.html
61
69
  .. _Julia library: https://github.com/SpM-lab/SparseIR.jl
62
- .. _C library with Fortran bindings: https://github.com/SpM-lab/libsparseir
70
+ .. _C library with Fortran bindings: https://github.com/SpM-lab/sparse-ir-rs
63
71
 
64
72
  Getting started
65
73
  ---------------
@@ -70,15 +78,16 @@ lines of Python code::
70
78
  import sparse_ir, numpy as np
71
79
  basis = sparse_ir.FiniteTempBasis('F', beta=10, wmax=8, eps=1e-6)
72
80
  stau = sparse_ir.TauSampling(basis)
73
- siw = sparse_ir.MatsubaraSampling(basis, positive_only=True)
81
+ siv = sparse_ir.MatsubaraSampling(basis, positive_only=True)
74
82
 
75
83
  # Solve the single impurity Anderson model coupled to a bath with a
76
- # semicircular states with unit half bandwidth.
84
+ # semicircular density of states with unit half bandwidth.
77
85
  U = 1.2
78
86
  def rho0w(w):
79
87
  return np.sqrt(1-w.clip(-1,1)**2) * 2/np.pi
80
88
 
81
- # Compute the IR basis coefficients for the non-interacting propagator
89
+ # Compute the IR basis coefficients G0_l = -S_l rho0_l for the
90
+ # non-interacting propagator
82
91
  rho0l = basis.v.overlap(rho0w)
83
92
  G0l = -basis.s * rho0l
84
93
 
@@ -91,23 +100,27 @@ lines of Python code::
91
100
  Gtau = stau.evaluate(Gl)
92
101
  Sigmatau = U**2 * Gtau**3
93
102
  Sigmal = stau.fit(Sigmatau)
94
- Sigmaiw = siw.evaluate(Sigmal)
95
- G0iw = siw.evaluate(G0l)
96
- Giw = 1/(1/G0iw - Sigmaiw)
97
- Gl = siw.fit(Giw)
103
+ Sigmaiv = siv.evaluate(Sigmal)
104
+ G0iv = siv.evaluate(G0l)
105
+ Giv = 1/(1/G0iv - Sigmaiv)
106
+ Gl = siv.fit(Giv)
98
107
 
99
108
  You may want to start with reading up on the `intermediate representation`_.
100
109
  It is tied to the analytic continuation of bosonic/fermionic spectral
101
110
  functions from (real) frequencies to imaginary time, a transformation mediated
102
- by a kernel ``K``. The kernel depends on a cutoff, which you should choose to
103
- be ``lambda_ >= beta * W``, where ``beta`` is the inverse temperature and ``W``
104
- is the bandwidth.
111
+ by the kernel K(τ, ω) = e^{-τω}/(1 + e^{-βω}). The basis depends on the
112
+ inverse temperature ``beta`` and on the frequency cutoff ``wmax``, which you
113
+ should choose so that the spectral function vanishes outside [-wmax, wmax].
114
+ In the dimensionless variables x = 2τ/β - 1 and y = ω/wmax, the kernel, and
115
+ hence its singular value expansion, depends on them only through the cutoff
116
+ Λ = ``beta * wmax`` (``basis.lambda_``).
105
117
 
106
118
  One can now perform a `singular value expansion`_ on this kernel, which
107
- generates two sets of orthonormal basis functions, one set ``v[l](w)`` for
108
- real frequency side ``w``, and one set ``u[l](tau)`` for the same obejct in
109
- imaginary (Euclidean) time ``tau``, together with a "coupling" strength
110
- ``s[l]`` between the two sides.
119
+ generates two sets of orthonormal basis functions, one set V_l(ω),
120
+ ``basis.v[l](w)``, on the real-frequency axis, orthonormal on
121
+ [-wmax, wmax], and one set U_l(τ), ``basis.u[l](tau)``, for the same object in
122
+ imaginary (Euclidean) time, orthonormal on [0, beta], together with the
123
+ singular values S_l, ``basis.s[l]``, which couple the two sides.
111
124
 
112
125
  By this construction, the imaginary time basis can be shown to be *optimal* in
113
126
  terms of compactness.
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "sparse-ir"
3
- version = "2.1.4"
3
+ version = "2.1.6"
4
4
  description = "Python bindings for the libsparseir library, providing efficient sparse intermediate representation for many-body physics calculations"
5
5
  readme = "README.rst"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
8
  "numpy",
9
9
  "scipy",
10
- "pylibsparseir>=0.8.3,<0.10.0",
10
+ "pylibsparseir>=0.8.3,<0.13.0",
11
11
  ]
12
12
  authors = [
13
13
  {name = "SpM-lab"}
@@ -56,6 +56,14 @@ doc = [
56
56
 
57
57
  [tool.pytest.ini_options]
58
58
  testpaths = ["tests"]
59
+ # RegularizedBoseKernel is deprecated but still tested; test_kernel.py asserts
60
+ # the warning itself (pytest.warns records it regardless of this filter).
61
+ # An exception in a finalizer (__del__) must fail the suite, not be reported
62
+ # as a warning (SpM-lab/sparse-ir-rs#282).
63
+ filterwarnings = [
64
+ "ignore:RegularizedBoseKernel is deprecated:DeprecationWarning",
65
+ "error::pytest.PytestUnraisableExceptionWarning",
66
+ ]
59
67
  python_files = ["test_*.py", "*_tests.py"]
60
68
  python_classes = ["Test*"]
61
69
  python_functions = ["test_*"]
@@ -1,9 +1,41 @@
1
1
  # Copyright (C) 2020-2025 Satoshi Terasaki, Markus Wallerberger, Hiroshi Shinaoka, and others
2
2
  # SPDX-License-Identifier: MIT
3
3
  import functools
4
+ import inspect
4
5
  import numpy as np
5
6
 
6
7
 
8
+ def release_handle(handle, release):
9
+ """Release a C handle obtained from pylibsparseir or its ``_lib``.
10
+
11
+ pylibsparseir >= 0.10 returns handles that own their C object and are
12
+ released with ``close()``; its raw ``_lib.spir_*_release`` entry points
13
+ refuse them (SpM-lab/sparse-ir-rs#282). Earlier releases, and direct
14
+ ``_lib`` calls in any release, return raw pointers that must be passed to
15
+ ``release``.
16
+ """
17
+ close = getattr(handle, "close", None)
18
+ if close is not None:
19
+ close()
20
+ else:
21
+ release(handle)
22
+
23
+
24
+ def has_basis_size_argument(fn):
25
+ """True if the pylibsparseir function ``fn`` takes ``basis_size``.
26
+
27
+ pylibsparseir >= 0.10 changed ``basis_get_default_matsus_ext`` from
28
+ ``(basis, positive_only, points)`` to
29
+ ``(basis, basis_size, positive_only, fence=False)``, returning the points
30
+ (SpM-lab/sparse-ir-rs#274). The signature is checked rather than the
31
+ version, since development builds may report an older version.
32
+ """
33
+ try:
34
+ return "basis_size" in inspect.signature(fn).parameters
35
+ except (TypeError, ValueError):
36
+ return False
37
+
38
+
7
39
  def ravel_argument(last_dim=False):
8
40
  """Wrap function operating on 1-D numpy array to allow arbitrary shapes.
9
41
 
@@ -46,10 +78,12 @@ _REAL_KINDS = "biuf"
46
78
  def check_reduced_matsubara(n, zeta=None):
47
79
  """Checks that ``n`` is a reduced Matsubara frequency.
48
80
 
49
- Check that the argument is a reduced Matsubara frequency, which is an
50
- integer obtained by scaling the freqency `w[n]` as follows::
81
+ Check that the argument is a reduced Matsubara frequency, which is the
82
+ integer ``n`` obtained by scaling the frequency ``nu`` as follows::
83
+
84
+ beta / np.pi * nu == n == 2 * m + zeta
51
85
 
52
- beta / np.pi * w[n] == 2 * n + zeta
86
+ where ``m`` is the ordinary Matsubara index.
53
87
 
54
88
  Note that this means that instead of a fermionic frequency (``zeta == 1``),
55
89
  we expect an odd integer, while for a bosonic frequency (``zeta == 0``),
@@ -74,6 +108,11 @@ def check_reduced_matsubara(n, zeta=None):
74
108
  raise ValueError(
75
109
  "reduced Matsubara frequency must be finite, got "
76
110
  f"{nfloat[~np.isfinite(nfloat)][0]!r}")
111
+ beyond = np.abs(nfloat) >= 2.0**63
112
+ if beyond.any():
113
+ raise ValueError(
114
+ "reduced Matsubara frequency is out of the int64 range, got "
115
+ f"{np.atleast_1d(nfloat)[np.atleast_1d(beyond)][0]!r}")
77
116
  n = np.rint(nfloat).astype(np.int64)
78
117
  bad = n != nfloat
79
118
  if bad.any():
@@ -142,11 +181,52 @@ def as_boundary_complex(a, name="array", check_finite=True):
142
181
  return out
143
182
 
144
183
 
184
+ def check_domain(x, xmin, xmax, name="evaluation points"):
185
+ """Check that every element of ``x`` is real, finite and in ``[xmin, xmax]``.
186
+
187
+ Returns ``x`` unchanged. Called before evaluation points are handed to
188
+ the C library, which would otherwise panic on an out-of-domain point
189
+ (SpM-lab/sparse-ir-rs#266) and report an internal error.
190
+
191
+ Raises:
192
+ TypeError: if ``x`` is complex or of a non-numeric element type.
193
+ ValueError: naming the first offending value otherwise.
194
+ """
195
+ arr = np.asarray(x)
196
+ if arr.dtype.kind == 'c':
197
+ raise TypeError(f"{name} must be real-valued, got dtype {arr.dtype}")
198
+ if arr.dtype.kind not in _REAL_KINDS:
199
+ raise TypeError(f"{name} has unsupported dtype {arr.dtype}")
200
+ xf = np.asarray(arr, dtype=np.float64)
201
+ bad = ~np.isfinite(xf) | (xf < xmin) | (xf > xmax)
202
+ if bad.any():
203
+ offending = np.atleast_1d(xf)[np.atleast_1d(bad)][0]
204
+ raise ValueError(f"{name} must be finite and lie in [{xmin}, {xmax}], "
205
+ f"got {offending!r}")
206
+ return x
207
+
208
+
209
+ def check_unique(points, name="sampling_points"):
210
+ """Raise ValueError if the one-dimensional ``points`` contain a duplicate.
211
+
212
+ Duplicated sampling points make the sampling matrix rank-deficient, which
213
+ the C library accepts silently.
214
+ """
215
+ arr = np.asarray(points)
216
+ values, counts = np.unique(arr, return_counts=True)
217
+ if (counts > 1).any():
218
+ dup = values[counts > 1][0]
219
+ first, second = np.flatnonzero(arr == dup)[:2]
220
+ raise ValueError(f"{name} contains the duplicate value {dup!r} at "
221
+ f"indices {first} and {second}; sampling points must "
222
+ "be pairwise distinct")
223
+
224
+
145
225
  def as_boundary_matsubara(n, name="Matsubara indices", zeta=None):
146
- """Normalize reduced Matsubara indices into a C-contiguous ``int64`` array.
226
+ """Normalize reduced Matsubara frequencies into a C-contiguous ``int64`` array.
147
227
 
148
228
  Validates integrality (and, if ``zeta`` is given, parity) *before* the
149
- conversion, so a non-integral index raises instead of being truncated.
229
+ conversion, so a non-integral value raises instead of being truncated.
150
230
  """
151
231
  checked = check_reduced_matsubara(n, zeta=zeta)
152
232
  return np.ascontiguousarray(checked, dtype=np.int64)
@@ -242,11 +322,14 @@ def normalize_tau(statistics, tau, beta):
242
322
  Raises:
243
323
  ValueError: If tau is outside [-β, β] or statistics is invalid.
244
324
 
245
- Special cases:
246
- - Negative zero (τ = -0.0) is treated as τ = β with appropriate sign
247
- - For τ in [0, β]: returns (τ, +1)
248
- - For τ in [-β, 0): returns (τ + β, sign) where sign depends on statistics
249
-
325
+ Special cases, with (-1)^ζ = -1 for fermions and +1 for bosons:
326
+ - For τ = +0.0 and τ in (0, β]: returns (τ, +1); +0.0 is read as 0⁺
327
+ and β as β⁻
328
+ - Negative zero (τ = -0.0) is read as 0⁻: returns (β, (-1)^ζ), since
329
+ f(0⁻) = (-1)^ζ f(β⁻)
330
+ - For τ in [-β, 0): returns (τ + β, (-1)^ζ); in particular -β, read
331
+ as (-β)⁺, gives (0, (-1)^ζ)
332
+
250
333
  .. versionadded:: 1.2
251
334
  """
252
335
  tau = np.asarray(tau, dtype=np.float64)
@@ -296,3 +379,27 @@ def check_svd_result(svd_result, matrix_shape=None):
296
379
  raise ValueError(f"shape mismatch between SVD ({m_u}, {n_v}) "
297
380
  f"and matrix ({m}, {n})")
298
381
  return u, s, vH
382
+
383
+ def slice_to_size(index, size):
384
+ """Return the number of basis functions selected by ``index``.
385
+
386
+ Only ``basis[:stop]``-style truncation is supported, mirroring
387
+ :py:meth:`FiniteTempBasis.__getitem__`.
388
+ """
389
+ if not isinstance(index, slice):
390
+ raise TypeError(
391
+ f"only slice truncation is supported, got {index!r}")
392
+ if index.start not in (None, 0):
393
+ raise ValueError(
394
+ f"basis truncation must start at 0, got {index.start!r}")
395
+ if index.step not in (None, 1):
396
+ raise ValueError(
397
+ f"basis truncation must have unit step, got {index.step!r}")
398
+ if index.stop is None:
399
+ return size
400
+ stop = int(index.stop)
401
+ if not 0 < stop <= size:
402
+ raise IndexError(
403
+ f"truncation to {stop} functions is out of range for a basis of "
404
+ f"size {size}")
405
+ return stop
@@ -9,7 +9,12 @@ This module provides the abstract interface that all basis types should implemen
9
9
  from abc import ABC, abstractmethod
10
10
 
11
11
  class AbstractKernel(ABC):
12
- """Abstract base class for kernels."""
12
+ """Abstract base class for kernels.
13
+
14
+ It defines no interface of its own: the supported kernels are
15
+ :class:`~sparse_ir.LogisticKernel` and the deprecated
16
+ :class:`~sparse_ir.RegularizedBoseKernel`.
17
+ """
13
18
  pass
14
19
 
15
20
  class AbstractBasis(ABC):
@@ -18,20 +23,34 @@ class AbstractBasis(ABC):
18
23
  This class stores a set of basis functions. We can then expand a two-point
19
24
  propagator G(τ), where τ is imaginary time:
20
25
 
21
- G(τ) ≈ Σ_{l=0}^{L-1} g_l U_l(τ)
26
+ .. math::
27
+
28
+ G(\tau) \approx \sum_{l=0}^{L-1} G_l U_l(\tau),
29
+
30
+ where :math:`U_l` is the l-th basis function, stored in :py:attr:`u`, and
31
+ the :math:`G_l` are the expansion coefficients. Similarly, the Fourier
32
+ transform G(iν) at the Matsubara frequency ν = nπ/β, where n is a reduced
33
+ Matsubara frequency, can be expanded as follows:
22
34
 
23
- where U is now the l-th basis function, stored in u and g denote the
24
- expansion coefficients. Similarly, the Fourier transform Ĝ(n), where n
25
- is a reduced Matsubara frequency, can be expanded as follows:
35
+ .. math::
26
36
 
27
- Ĝ(n) ≈ Σ_{l=0}^{L-1} g_l Û_l(n)
37
+ G(\mathrm{i}\nu) \approx \sum_{l=0}^{L-1} G_l \hat U_l(\mathrm{i}\nu),
28
38
 
29
- where Û is the Fourier transform of the l-th basis function, stored
30
- in uhat.
39
+ where :math:`\hat U_l` is the Fourier transform of the l-th basis
40
+ function, stored in :py:attr:`uhat`. The Fourier transform and its
41
+ inverse are
31
42
 
32
- Assuming that basis is an instance of some abstract basis, g is a vector
33
- of expansion coefficients, tau is some imaginary time and n some frequency,
34
- we can write this in the library as follows:
43
+ .. math::
44
+
45
+ G(\mathrm{i}\nu) = \int_0^\beta d\tau\, e^{\mathrm{i}\nu\tau} G(\tau),
46
+ \qquad
47
+ G(\tau) = \frac{1}{\beta} \sum_\nu e^{-\mathrm{i}\nu\tau}
48
+ G(\mathrm{i}\nu).
49
+
50
+ Assuming that ``basis`` is an instance of some abstract basis, ``gl`` is
51
+ a vector of expansion coefficients G_l, ``tau`` is some imaginary time and
52
+ ``n`` some reduced frequency, we can write this in the library as
53
+ follows::
35
54
 
36
55
  G_tau = basis.u(tau).T @ gl
37
56
  G_n = basis.uhat(n).T @ gl
@@ -42,9 +61,13 @@ class AbstractBasis(ABC):
42
61
  def u(self):
43
62
  r"""Basis functions on the imaginary time axis.
44
63
 
45
- Set of IR basis functions on the imaginary time (tau) axis, where tau
46
- is a real number between zero and beta. To get the l-th basis function
47
- at imaginary time tau of some basis, use:
64
+ Set of basis functions :math:`U_l(\tau)` on the imaginary time (tau)
65
+ axis, where tau is a real number in [-beta, beta]. Negative times
66
+ follow :math:`U_l(\tau) = (-1)^\zeta U_l(\tau + \beta)`, where
67
+ :math:`(-1)^\zeta` is -1 for fermions and +1 for bosons, and the
68
+ endpoints are one-sided limits: ``+0.0`` is 0⁺, ``beta`` is β⁻,
69
+ ``-0.0`` is 0⁻ and ``-beta`` is (-β)⁺. To get the l-th basis function
70
+ at imaginary time tau of some basis, use::
48
71
 
49
72
  ultau = basis.u[l](tau) # l-th basis function at time tau
50
73
 
@@ -55,24 +78,30 @@ class AbstractBasis(ABC):
55
78
  @property
56
79
  @abstractmethod
57
80
  def uhat(self):
58
- r"""Basis functions on the reduced Matsubara frequency (wn) axis.
81
+ r"""Basis functions on the reduced Matsubara frequency axis.
82
+
83
+ Set of basis functions :math:`\hat U_l(\mathrm{i}\nu)` at the
84
+ Matsubara frequencies ν = nπ/β, where the reduced frequency n is an
85
+ integer. These are related to u by the following Fourier transform:
59
86
 
60
- Set of IR basis functions reduced Matsubara frequency (wn) axis, where
61
- wn is an integer. These are related to u by the following Fourier transform:
87
+ .. math::
62
88
 
63
- û(n) = ∫₀^β dτ exp(iπnτ/β) u(τ)
89
+ \hat U_l(\mathrm{i}\nu) = \int_0^\beta d\tau\,
90
+ e^{\mathrm{i}\nu\tau} U_l(\tau).
64
91
 
65
- To get the l-th basis function at some reduced frequency wn of
66
- some basis, use:
92
+ To get the l-th basis function at some reduced frequency n of
93
+ some basis, use::
67
94
 
68
- uln = basis.uhat[l](wn) # l-th basis function at freq wn
95
+ uln = basis.uhat[l](n) # l-th basis function at iν, ν = nπ/β
69
96
 
70
97
  Note:
71
98
  Instead of the value of the Matsubara frequency, these functions
72
- expect integers corresponding to the prefactor of pi over beta.
73
- For example, the first few positive fermionic frequencies would
74
- be specified as [1, 3, 5, 7], and the first bosonic frequencies
75
- are [0, 2, 4, 6]. This is also distinct to an index!
99
+ expect the reduced frequency n = βν/π, the integer prefactor of
100
+ π/β. For example, the first few positive fermionic frequencies
101
+ would be specified as [1, 3, 5, 7], and the first bosonic
102
+ frequencies are [0, 2, 4, 6]: n is odd for fermions and even for
103
+ bosons. It is not the ordinary Matsubara index m, which gives
104
+ n = 2m + ζ (ζ = 1 for fermions, 0 for bosons).
76
105
  """
77
106
  raise NotImplementedError()
78
107
 
@@ -117,7 +146,11 @@ class AbstractBasis(ABC):
117
146
  """Accuracy of the basis.
118
147
 
119
148
  Upper bound to the relative error of representing a propagator with
120
- the given number of basis functions (number between 0 and 1).
149
+ the given number of basis functions (number between 0 and 1). This
150
+ default returns the smallest significance, ``significance[-1]``;
151
+ :class:`~sparse_ir.FiniteTempBasis` overrides it with S_L/S_0 of the
152
+ first discarded singular value, and the DLR and augmented bases
153
+ return the accuracy of their underlying basis.
121
154
  """
122
155
  return self.significance[-1]
123
156
 
@@ -161,10 +194,9 @@ class AbstractBasis(ABC):
161
194
  npoints : int, optional
162
195
  Minimum number of sampling points to return.
163
196
  positive_only : bool
164
- Only return non-negative frequencies. This is useful if the
165
- object to be fitted is symmetric in Matsubara frequency,
166
- ghat(w) == ghat(-w).conj(), or, equivalently, real in
167
- imaginary time.
197
+ Only return non-negative frequencies, n >= 0. This is useful if
198
+ the object to be fitted is symmetric in Matsubara frequency,
199
+ G(-iν) = G(iν)*, or, equivalently, real in imaginary time.
168
200
  """
169
201
  raise NotImplementedError()
170
202