sparse-ir 1.1.6__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/dlr.py CHANGED
@@ -1,16 +1,18 @@
1
- # Copyright (C) 2020-2022 Markus Wallerberger, Hiroshi Shinaoka, and others
2
- # SPDX-License-Identifier: MIT
3
- import numpy as np
1
+ """
2
+ Discrete Lehmann Representation (DLR) functionality for SparseIR.
4
3
 
5
- from . import abstract
6
- from . import kernel
7
- from . import sampling
8
- from . import basis as _basis
9
- from . import _util
10
- from . import svd
4
+ This module implements DLR basis with poles at IR extrema, providing
5
+ an alternative representation that can be more efficient for certain calculations.
6
+ """
11
7
 
8
+ import ctypes
9
+ import numpy as np
10
+ from .abstract import AbstractBasis
11
+ from pylibsparseir.core import basis_get_default_omega_sampling_points
12
+ from pylibsparseir.core import _lib, COMPUTATION_SUCCESS
13
+ from pylibsparseir.constants import SPIR_ORDER_ROW_MAJOR
12
14
 
13
- class DiscreteLehmannRepresentation(abstract.AbstractBasis):
15
+ class DiscreteLehmannRepresentation(AbstractBasis):
14
16
  """Discrete Lehmann representation (DLR), with poles being extrema of IR.
15
17
 
16
18
  This class implements a variant of the discrete Lehmann representation
@@ -32,38 +34,26 @@ class DiscreteLehmannRepresentation(abstract.AbstractBasis):
32
34
 
33
35
  .. _DLR: https://doi.org/10.1103/PhysRevB.105.235115
34
36
  """
35
- def __init__(self, basis: _basis.FiniteTempBasis, sampling_points=None):
36
- if sampling_points is None:
37
- sampling_points = basis.default_omega_sampling_points()
38
- if not isinstance(basis.kernel, kernel.LogisticKernel):
39
- raise ValueError("DLR supports only LogisticKernel")
40
37
 
38
+ def __init__(self, basis: AbstractBasis, poles=None):
39
+ status = ctypes.c_int()
40
+ if poles is None:
41
+ poles = basis_get_default_omega_sampling_points(basis._ptr)
41
42
  self._basis = basis
42
- self._poles = np.asarray(sampling_points)
43
- self._y_sampling_points = self._poles/basis.wmax
44
-
45
- self._u = TauPoles(basis.statistics, basis.beta, self._poles)
46
- self._uhat = MatsubaraPoles(basis.statistics, basis.beta, self._poles)
47
-
48
- # Fitting matrix from IR
49
- F = -basis.s[:, None] * basis.v(self._poles)
50
-
51
- # Now, here we *know* that F is ill-conditioned in very particular way:
52
- # it is a product A * B * C, where B is well conditioned and A, C are
53
- # scalings. This is handled with guaranteed relative accuracy by a
54
- # Jacobi SVD, implied by the 'accurate' strategy.
55
- uF, sF, vF = svd.compute(F, strategy='accurate')
56
- self.matrix = sampling.DecomposedMatrix(F, svd_result=(uF, sF, vF.T))
43
+ self._poles = poles
44
+ self._ptr = _lib.spir_dlr_new_with_poles(basis._ptr, len(poles), poles.ctypes.data_as(ctypes.POINTER(ctypes.c_double)), status)
45
+ if status.value != COMPUTATION_SUCCESS:
46
+ raise RuntimeError(f"Failed to create DLR basis: {status.value}")
57
47
 
58
48
  @property
59
- def u(self): return self._u
49
+ def u(self): return self._basis._u
60
50
 
61
51
  @property
62
- def uhat(self): return self._uhat
52
+ def uhat(self): return self._basis._uhat
63
53
 
64
54
  @property
65
55
  def statistics(self):
66
- return self.basis.statistics
56
+ return self._basis.statistics
67
57
 
68
58
  @property
69
59
  def sampling_points(self):
@@ -73,24 +63,24 @@ class DiscreteLehmannRepresentation(abstract.AbstractBasis):
73
63
  def shape(self): return self.size,
74
64
 
75
65
  @property
76
- def size(self): return self._poles.size
66
+ def size(self): return len(self._poles)
77
67
 
78
68
  @property
79
- def basis(self) -> _basis.FiniteTempBasis:
69
+ def basis(self) -> AbstractBasis:
80
70
  """ Underlying basis """
81
71
  return self._basis
82
72
 
83
73
  @property
84
74
  def lambda_(self):
85
- return self.basis.lambda_
75
+ return self._basis.lambda_
86
76
 
87
77
  @property
88
78
  def beta(self):
89
- return self.basis.beta
79
+ return self._basis.beta
90
80
 
91
81
  @property
92
82
  def wmax(self):
93
- return self.basis.wmax
83
+ return self._basis.wmax
94
84
 
95
85
  @property
96
86
  def significance(self):
@@ -98,71 +88,124 @@ class DiscreteLehmannRepresentation(abstract.AbstractBasis):
98
88
 
99
89
  @property
100
90
  def accuracy(self):
101
- return self.basis.accuracy
91
+ return self._basis.accuracy
102
92
 
103
93
  def from_IR(self, gl: np.ndarray, axis=0) -> np.ndarray:
104
- """
105
- From IR to DLR
94
+ """From IR to DLR
95
+
96
+ Convert expansion coefficients from IR basis to DLR basis.
106
97
 
107
- gl:
98
+ Parameters
99
+ ----------
100
+ gl : array_like
108
101
  Expansion coefficients in IR
102
+ axis : int, optional
103
+ Axis along which to convert
104
+
105
+ Returns
106
+ -------
107
+ array_like
108
+ Expansion coefficients in DLR
109
109
  """
110
- return self.matrix.lstsq(gl, axis)
110
+ if gl.shape[axis] != self.basis.size:
111
+ raise ValueError(f"Input array has wrong size along dimension {axis}")
112
+
113
+ output_dims = list(gl.shape)
114
+ output_dims[axis] = self.size
115
+ output = np.zeros(output_dims, dtype=gl.dtype)
116
+
117
+ ndim = len(gl.shape)
118
+ input_dims = np.asarray(gl.shape, dtype=np.int32)
119
+ target_dim = axis
120
+ order = SPIR_ORDER_ROW_MAJOR
121
+
122
+ if gl.dtype.kind == 'f':
123
+ ret = _lib.spir_ir2dlr_dd(
124
+ self._ptr,
125
+ order,
126
+ ndim,
127
+ input_dims.ctypes.data_as(ctypes.POINTER(ctypes.c_int32)),
128
+ target_dim,
129
+ gl.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
130
+ output.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
131
+ )
132
+ elif gl.dtype.kind == 'c':
133
+ ret = _lib.spir_ir2dlr_zz(
134
+ self._ptr,
135
+ order,
136
+ ndim,
137
+ input_dims.ctypes.data_as(ctypes.POINTER(ctypes.c_int32)),
138
+ target_dim,
139
+ # TODO: use complex data
140
+ gl.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
141
+ output.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
142
+ )
143
+ else:
144
+ raise ValueError(f"Unsupported dtype: {gl.dtype}")
145
+ if ret != COMPUTATION_SUCCESS:
146
+ raise RuntimeError(f"Failed to convert IR to DLR: {ret}")
147
+ return output
111
148
 
112
149
  def to_IR(self, g_dlr: np.ndarray, axis=0) -> np.ndarray:
113
- """
114
- From DLR to IR
150
+ """From DLR to IR
151
+
152
+ Convert expansion coefficients from DLR basis to IR basis.
115
153
 
116
- g_dlr:
154
+ Parameters
155
+ ----------
156
+ g_dlr : array_like
117
157
  Expansion coefficients in DLR
158
+ axis : int, optional
159
+ Axis along which to convert
160
+
161
+ Returns
162
+ -------
163
+ array_like
164
+ Expansion coefficients in IR
118
165
  """
119
- return self.matrix.matmul(g_dlr, axis)
166
+ if g_dlr.shape[axis] != self.size:
167
+ raise ValueError(f"Input array has wrong size along dimension {axis}")
168
+ output_dims = np.asarray(g_dlr.shape, dtype=np.int32)
169
+ output_dims[axis] = self.basis.size
170
+ output = np.zeros(output_dims, dtype=g_dlr.dtype)
171
+ ndim = len(g_dlr.shape)
172
+ input_dims = np.asarray(g_dlr.shape, dtype=np.int32)
173
+ target_dim = axis
174
+ order = SPIR_ORDER_ROW_MAJOR
175
+
176
+ if g_dlr.dtype.kind == 'f':
177
+ ret = _lib.spir_dlr2ir_dd(
178
+ self._ptr,
179
+ order,
180
+ ndim,
181
+ input_dims.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
182
+ target_dim,
183
+ g_dlr.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
184
+ output.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
185
+ )
186
+ elif g_dlr.dtype.kind == 'c':
187
+ ret = _lib.spir_dlr2ir_zz(
188
+ self._ptr,
189
+ order,
190
+ ndim,
191
+ input_dims.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
192
+ target_dim,
193
+ # TODO: use complex data
194
+ g_dlr.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
195
+ output.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
196
+ )
197
+ else:
198
+ raise ValueError(f"Unsupported dtype: {g_dlr.dtype}")
199
+ if ret != COMPUTATION_SUCCESS:
200
+ raise RuntimeError(f"Failed to convert DLR to IR: {ret}")
201
+ return output
120
202
 
121
203
  def default_tau_sampling_points(self):
122
- return self.basis.default_tau_sampling_points()
204
+ return self._basis.default_tau_sampling_points()
123
205
 
124
206
  def default_matsubara_sampling_points(self, **kwargs):
125
- return self.basis.default_matsubara_sampling_points(**kwargs)
207
+ return self._basis.default_matsubara_sampling_points(**kwargs)
126
208
 
127
209
  @property
128
210
  def is_well_conditioned(self):
129
211
  return False
130
-
131
-
132
- class MatsubaraPoles:
133
- def __init__(self, statistics: str, beta: float, poles: np.ndarray):
134
- self._statistics = statistics
135
- self._beta = beta
136
- self._poles = np.array(poles)
137
-
138
- @_util.ravel_argument(last_dim=True)
139
- def __call__(self, n):
140
- """Evaluate basis functions at given frequency n"""
141
- iv = 1j*n * np.pi/self._beta
142
- if self._statistics == 'F':
143
- return 1 /(iv[None, :] - self._poles[:, None])
144
- else:
145
- return np.tanh(0.5 * self._beta * self._poles)[:, None]\
146
- /(iv[None, :] - self._poles[:, None])
147
-
148
-
149
- class TauPoles:
150
- def __init__(self, statistics: str, beta: float, poles: np.ndarray):
151
- self._beta = beta
152
- self._statistics = statistics
153
- self._poles = np.array(poles)
154
- self._wmax = np.abs(poles).max()
155
-
156
- @_util.ravel_argument(last_dim=True)
157
- def __call__(self, tau):
158
- """ Evaluate basis functions at tau """
159
- tau = np.asarray(tau)
160
- if (tau < 0).any() or (tau > self._beta).any():
161
- raise RuntimeError("tau must be in [0, beta]!")
162
-
163
- x = 2 * tau/self._beta - 1
164
- y = self._poles/self._wmax
165
- lambda_ = self._beta * self._wmax
166
-
167
- res = -kernel.LogisticKernel(lambda_)(x[:, None], y[None, :])
168
- return res.T