pylibsparseir 0.1.0__cp313-cp313-musllinux_1_2_x86_64.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.

Potentially problematic release.


This version of pylibsparseir might be problematic. Click here for more details.

@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Clean build artifacts and files copied by setup_build.py and included via MANIFEST.in
4
+ """
5
+
6
+ import os
7
+ import shutil
8
+ import glob
9
+
10
+ def clean_build_artifacts():
11
+ """Remove build artifacts and copied files."""
12
+ current_dir = os.getcwd()
13
+
14
+ # Directories and files to remove (from setup_build.py and build artifacts)
15
+ items_to_remove = [
16
+ 'build',
17
+ 'dist',
18
+ '*.egg-info',
19
+ 'include',
20
+ 'src',
21
+ 'fortran',
22
+ 'cmake',
23
+ 'CMakeLists.txt'
24
+ ]
25
+
26
+ # Files in pylibsparseir to remove
27
+ pylibsparseir_patterns = [
28
+ 'pylibsparseir/*.so',
29
+ 'pylibsparseir/*.dylib',
30
+ 'pylibsparseir/*.dll',
31
+ 'pylibsparseir/__pycache__'
32
+ ]
33
+
34
+ print(f"Cleaning build artifacts in: {current_dir}")
35
+
36
+ # Remove main items
37
+ for item in items_to_remove:
38
+ if '*' in item:
39
+ # Handle glob patterns
40
+ for path in glob.glob(item):
41
+ if os.path.exists(path):
42
+ if os.path.isdir(path):
43
+ shutil.rmtree(path)
44
+ print(f"Removed directory: {path}")
45
+ else:
46
+ os.remove(path)
47
+ print(f"Removed file: {path}")
48
+ else:
49
+ # Handle direct paths
50
+ if os.path.exists(item):
51
+ if os.path.isdir(item):
52
+ shutil.rmtree(item)
53
+ print(f"Removed directory: {item}")
54
+ else:
55
+ os.remove(item)
56
+ print(f"Removed file: {item}")
57
+
58
+ # Remove pylibsparseir artifacts
59
+ for pattern in pylibsparseir_patterns:
60
+ for path in glob.glob(pattern):
61
+ if os.path.exists(path):
62
+ if os.path.isdir(path):
63
+ shutil.rmtree(path)
64
+ print(f"Removed directory: {path}")
65
+ else:
66
+ os.remove(path)
67
+ print(f"Removed file: {path}")
68
+
69
+ print("Clean completed!")
70
+
71
+ if __name__ == "__main__":
72
+ clean_build_artifacts()
@@ -0,0 +1,39 @@
1
+ """
2
+ Constants used in the SparseIR C API.
3
+ """
4
+
5
+ # Status codes
6
+ COMPUTATION_SUCCESS = 0
7
+ GET_IMPL_FAILED = -1
8
+ INVALID_DIMENSION = -2
9
+ INPUT_DIMENSION_MISMATCH = -3
10
+ OUTPUT_DIMENSION_MISMATCH = -4
11
+ NOT_SUPPORTED = -5
12
+ INVALID_ARGUMENT = -6
13
+ INTERNAL_ERROR = -7
14
+
15
+ # SPIR prefixed constants for compatibility
16
+ SPIR_COMPUTATION_SUCCESS = 0
17
+ SPIR_GET_IMPL_FAILED = -1
18
+ SPIR_INVALID_DIMENSION = -2
19
+ SPIR_INPUT_DIMENSION_MISMATCH = -3
20
+ SPIR_OUTPUT_DIMENSION_MISMATCH = -4
21
+ SPIR_NOT_SUPPORTED = -5
22
+ SPIR_INVALID_ARGUMENT = -6
23
+ SPIR_INTERNAL_ERROR = -7
24
+
25
+ # Statistics type constants
26
+ SPIR_STATISTICS_FERMIONIC = 1
27
+ SPIR_STATISTICS_BOSONIC = 0
28
+
29
+ # Order type constants
30
+ SPIR_ORDER_COLUMN_MAJOR = 1
31
+ SPIR_ORDER_ROW_MAJOR = 0
32
+
33
+ # Make sure these are available at module level
34
+ SPIR_ORDER_ROW_MAJOR = 0
35
+ SPIR_ORDER_COLUMN_MAJOR = 1
36
+
37
+ # SVE Twork constants
38
+ SPIR_TWORK_FLOAT64 = 0
39
+ SPIR_TWORK_FLOAT64X2 = 1
pylibsparseir/core.py ADDED
@@ -0,0 +1,609 @@
1
+ """
2
+ Core functionality for the SparseIR Python bindings.
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import ctypes
8
+ from ctypes import c_int, c_double, c_int64, c_size_t, c_bool, POINTER, byref
9
+ from ctypes import CDLL
10
+ import numpy as np
11
+
12
+ from .ctypes_wrapper import spir_kernel, spir_sve_result, spir_basis, spir_funcs, spir_sampling
13
+ from pylibsparseir.constants import COMPUTATION_SUCCESS, SPIR_ORDER_ROW_MAJOR, SPIR_ORDER_COLUMN_MAJOR, SPIR_TWORK_FLOAT64, SPIR_TWORK_FLOAT64X2, SPIR_STATISTICS_FERMIONIC, SPIR_STATISTICS_BOSONIC
14
+
15
+ def _find_library():
16
+ """Find the SparseIR shared library."""
17
+ if sys.platform == "darwin":
18
+ libname = "libsparseir.dylib"
19
+ elif sys.platform == "win32":
20
+ libname = "sparseir.dll"
21
+ else:
22
+ libname = "libsparseir.so"
23
+
24
+ # Try to find the library in common locations
25
+ search_paths = [
26
+ os.path.dirname(os.path.abspath(__file__)),
27
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "build"),
28
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "build"),
29
+ ]
30
+
31
+ for path in search_paths:
32
+ libpath = os.path.join(path, libname)
33
+ if os.path.exists(libpath):
34
+ return libpath
35
+
36
+ raise RuntimeError(f"Could not find {libname} in {search_paths}")
37
+
38
+ # Load the library
39
+ try:
40
+ _lib = CDLL(_find_library())
41
+ except Exception as e:
42
+ raise RuntimeError(f"Failed to load SparseIR library: {e}")
43
+
44
+ class c_double_complex(ctypes.Structure):
45
+ """complex is a c structure
46
+ https://docs.python.org/3/library/ctypes.html#module-ctypes suggests
47
+ to use ctypes.Structure to pass structures (and, therefore, complex)
48
+ See: https://stackoverflow.com/questions/13373291/complex-number-in-ctypes
49
+ """
50
+ _fields_ = [("real", ctypes.c_double),("imag", ctypes.c_double)]
51
+ @property
52
+ def value(self):
53
+ return self.real+1j*self.imag # fields declared above
54
+
55
+ # Set up function prototypes
56
+ def _setup_prototypes():
57
+ # Kernel functions
58
+ _lib.spir_logistic_kernel_new.argtypes = [c_double, POINTER(c_int)]
59
+ _lib.spir_logistic_kernel_new.restype = spir_kernel
60
+
61
+ _lib.spir_reg_bose_kernel_new.argtypes = [c_double, POINTER(c_int)]
62
+ _lib.spir_reg_bose_kernel_new.restype = spir_kernel
63
+
64
+ _lib.spir_kernel_domain.argtypes = [
65
+ spir_kernel, POINTER(c_double), POINTER(c_double),
66
+ POINTER(c_double), POINTER(c_double)
67
+ ]
68
+ _lib.spir_kernel_domain.restype = c_int
69
+
70
+ # SVE result functions
71
+ _lib.spir_sve_result_new.argtypes = [spir_kernel, c_double, c_double, c_int, c_int, c_int, POINTER(c_int)]
72
+ _lib.spir_sve_result_new.restype = spir_sve_result
73
+
74
+ _lib.spir_sve_result_get_size.argtypes = [spir_sve_result, POINTER(c_int)]
75
+ _lib.spir_sve_result_get_size.restype = c_int
76
+
77
+ _lib.spir_sve_result_get_svals.argtypes = [spir_sve_result, POINTER(c_double)]
78
+ _lib.spir_sve_result_get_svals.restype = c_int
79
+
80
+ # Basis functions
81
+ _lib.spir_basis_new.argtypes = [
82
+ c_int, c_double, c_double, spir_kernel, spir_sve_result, c_int, POINTER(c_int)
83
+ ]
84
+ _lib.spir_basis_new.restype = spir_basis
85
+
86
+ _lib.spir_basis_get_size.argtypes = [spir_basis, POINTER(c_int)]
87
+ _lib.spir_basis_get_size.restype = c_int
88
+
89
+ _lib.spir_basis_get_svals.argtypes = [spir_basis, POINTER(c_double)]
90
+ _lib.spir_basis_get_svals.restype = c_int
91
+
92
+ _lib.spir_basis_get_stats.argtypes = [spir_basis, POINTER(c_int)]
93
+ _lib.spir_basis_get_stats.restype = c_int
94
+
95
+ # Basis function objects
96
+ _lib.spir_basis_get_u.argtypes = [spir_basis, POINTER(c_int)]
97
+ _lib.spir_basis_get_u.restype = spir_funcs
98
+
99
+ _lib.spir_basis_get_v.argtypes = [spir_basis, POINTER(c_int)]
100
+ _lib.spir_basis_get_v.restype = spir_funcs
101
+
102
+ _lib.spir_basis_get_uhat.argtypes = [spir_basis, POINTER(c_int)]
103
+ _lib.spir_basis_get_uhat.restype = spir_funcs
104
+
105
+ _lib.spir_funcs_get_slice.argtypes = [spir_funcs, c_int, POINTER(c_int), POINTER(c_int)]
106
+ _lib.spir_funcs_get_slice.restype = spir_funcs
107
+
108
+ # Function evaluation
109
+ _lib.spir_funcs_get_size.argtypes = [spir_funcs, POINTER(c_int)]
110
+ _lib.spir_funcs_get_size.restype = c_int
111
+
112
+ _lib.spir_funcs_eval.argtypes = [spir_funcs, c_double, POINTER(c_double)]
113
+ _lib.spir_funcs_eval.restype = c_int
114
+
115
+ _lib.spir_funcs_eval_matsu.argtypes = [spir_funcs, c_int64, POINTER(c_double_complex)]
116
+ _lib.spir_funcs_eval_matsu.restype = c_int
117
+
118
+ _lib.spir_funcs_batch_eval.argtypes = [
119
+ spir_funcs, c_int, c_size_t, POINTER(c_double), POINTER(c_double)
120
+ ]
121
+ _lib.spir_funcs_batch_eval.restype = c_int
122
+
123
+ _lib.spir_funcs_batch_eval_matsu.argtypes = [
124
+ spir_funcs, c_int, c_int, POINTER(c_int64), POINTER(c_double)
125
+ ]
126
+ _lib.spir_funcs_batch_eval_matsu.restype = c_int
127
+
128
+ _lib.spir_funcs_get_n_roots.argtypes = [spir_funcs, POINTER(c_int)]
129
+ _lib.spir_funcs_get_n_roots.restype = c_int
130
+
131
+ _lib.spir_funcs_get_roots.argtypes = [spir_funcs, POINTER(c_double)]
132
+ _lib.spir_funcs_get_roots.restype = c_int
133
+
134
+ # Default sampling points
135
+ _lib.spir_basis_get_n_default_taus.argtypes = [spir_basis, POINTER(c_int)]
136
+ _lib.spir_basis_get_n_default_taus.restype = c_int
137
+
138
+ _lib.spir_basis_get_default_taus.argtypes = [spir_basis, POINTER(c_double)]
139
+ _lib.spir_basis_get_default_taus.restype = c_int
140
+
141
+ _lib.spir_basis_get_default_taus_ext.argtypes = [spir_basis, c_int, POINTER(c_double), POINTER(c_int)]
142
+ _lib.spir_basis_get_default_taus_ext.restype = c_int
143
+
144
+ _lib.spir_basis_get_n_default_ws.argtypes = [spir_basis, POINTER(c_int)]
145
+ _lib.spir_basis_get_n_default_ws.restype = c_int
146
+
147
+ _lib.spir_basis_get_default_ws.argtypes = [spir_basis, POINTER(c_double)]
148
+ _lib.spir_basis_get_default_ws.restype = c_int
149
+
150
+ _lib.spir_basis_get_n_default_matsus.argtypes = [spir_basis, c_bool, POINTER(c_int)]
151
+ _lib.spir_basis_get_n_default_matsus.restype = c_int
152
+
153
+ _lib.spir_basis_get_n_default_matsus_ext.argtypes = [spir_basis, c_bool, c_int, POINTER(c_int)]
154
+ _lib.spir_basis_get_n_default_matsus_ext.restype = c_int
155
+
156
+ _lib.spir_basis_get_default_matsus.argtypes = [spir_basis, c_bool, POINTER(c_int64)]
157
+ _lib.spir_basis_get_default_matsus.restype = c_int
158
+
159
+ _lib.spir_basis_get_default_matsus_ext.argtypes = [spir_basis, c_bool, c_int, POINTER(c_int64), POINTER(c_int)]
160
+ _lib.spir_basis_get_default_matsus_ext.restype = c_int
161
+
162
+ # Sampling objects
163
+ _lib.spir_tau_sampling_new.argtypes = [spir_basis, c_int, POINTER(c_double), POINTER(c_int)]
164
+ _lib.spir_tau_sampling_new.restype = spir_sampling
165
+
166
+ _lib.spir_tau_sampling_new_with_matrix.argtypes = [c_int, c_int, c_int, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_int)]
167
+ _lib.spir_tau_sampling_new_with_matrix.restype = spir_sampling
168
+
169
+ _lib.spir_matsu_sampling_new.argtypes = [spir_basis, c_bool, c_int, POINTER(c_int64), POINTER(c_int)]
170
+ _lib.spir_matsu_sampling_new.restype = spir_sampling
171
+
172
+ _lib.spir_matsu_sampling_new_with_matrix.argtypes = [
173
+ c_int, # order
174
+ c_int, # statistics
175
+ c_int, # basis_size
176
+ c_bool, # positive_only
177
+ c_int, # num_points
178
+ POINTER(c_int64), # points
179
+ POINTER(c_double_complex), # matrix
180
+ POINTER(c_int) # status
181
+ ]
182
+ _lib.spir_matsu_sampling_new_with_matrix.restype = spir_sampling
183
+
184
+ # Sampling operations
185
+ _lib.spir_sampling_eval_dd.argtypes = [
186
+ spir_sampling, c_int, c_int, POINTER(c_int), c_int,
187
+ POINTER(c_double), POINTER(c_double)
188
+ ]
189
+ _lib.spir_sampling_eval_dd.restype = c_int
190
+
191
+ _lib.spir_sampling_fit_dd.argtypes = [
192
+ spir_sampling, c_int, c_int, POINTER(c_int), c_int,
193
+ POINTER(c_double), POINTER(c_double)
194
+ ]
195
+ _lib.spir_sampling_fit_dd.restype = c_int
196
+
197
+ # Additional sampling functions
198
+ _lib.spir_sampling_get_npoints.argtypes = [spir_sampling, POINTER(c_int)]
199
+ _lib.spir_sampling_get_npoints.restype = c_int
200
+
201
+ _lib.spir_sampling_get_taus.argtypes = [spir_sampling, POINTER(c_double)]
202
+ _lib.spir_sampling_get_taus.restype = c_int
203
+
204
+ _lib.spir_sampling_get_matsus.argtypes = [spir_sampling, POINTER(c_int64)]
205
+ _lib.spir_sampling_get_matsus.restype = c_int
206
+
207
+ _lib.spir_sampling_get_cond_num.argtypes = [spir_sampling, POINTER(c_double)]
208
+ _lib.spir_sampling_get_cond_num.restype = c_int
209
+
210
+ # Multi-dimensional sampling evaluation functions
211
+ _lib.spir_sampling_eval_dz.argtypes = [
212
+ spir_sampling, c_int, c_int, POINTER(c_int), c_int,
213
+ POINTER(c_double), POINTER(c_double_complex)
214
+ ]
215
+ _lib.spir_sampling_eval_dz.restype = c_int
216
+
217
+ _lib.spir_sampling_eval_zz.argtypes = [
218
+ spir_sampling, c_int, c_int, POINTER(c_int), c_int,
219
+ POINTER(c_double_complex), POINTER(c_double_complex)
220
+ ]
221
+ _lib.spir_sampling_eval_zz.restype = c_int
222
+
223
+ _lib.spir_sampling_fit_zz.argtypes = [
224
+ spir_sampling, c_int, c_int, POINTER(c_int), c_int,
225
+ POINTER(c_double_complex), POINTER(c_double_complex)
226
+ ]
227
+ _lib.spir_sampling_fit_zz.restype = c_int
228
+
229
+ # DLR functions
230
+ _lib.spir_dlr_new.argtypes = [spir_basis, POINTER(c_int)]
231
+ _lib.spir_dlr_new.restype = spir_basis
232
+
233
+ _lib.spir_dlr_new_with_poles.argtypes = [spir_basis, c_int, POINTER(c_double), POINTER(c_int)]
234
+ _lib.spir_dlr_new_with_poles.restype = spir_basis
235
+
236
+ _lib.spir_dlr_get_npoles.argtypes = [spir_basis, POINTER(c_int)]
237
+ _lib.spir_dlr_get_npoles.restype = c_int
238
+
239
+ _lib.spir_dlr_get_poles.argtypes = [spir_basis, POINTER(c_double)]
240
+ _lib.spir_dlr_get_poles.restype = c_int
241
+
242
+ _lib.spir_dlr2ir_dd.argtypes = [
243
+ spir_basis, c_int, c_int, POINTER(c_int), c_int,
244
+ POINTER(c_double), POINTER(c_double)
245
+ ]
246
+ _lib.spir_dlr2ir_dd.restype = c_int
247
+
248
+ _lib.spir_dlr2ir_zz.argtypes = [
249
+ spir_basis, c_int, c_int, POINTER(c_int), c_int,
250
+ POINTER(c_double), POINTER(c_double)
251
+ ]
252
+ _lib.spir_dlr2ir_zz.restype = c_int
253
+
254
+ # Release functions
255
+ _lib.spir_kernel_release.argtypes = [spir_kernel]
256
+ _lib.spir_kernel_release.restype = None
257
+
258
+ _lib.spir_sve_result_release.argtypes = [spir_sve_result]
259
+ _lib.spir_sve_result_release.restype = None
260
+
261
+ _lib.spir_basis_release.argtypes = [spir_basis]
262
+ _lib.spir_basis_release.restype = None
263
+
264
+ _lib.spir_funcs_release.argtypes = [spir_funcs]
265
+ _lib.spir_funcs_release.restype = None
266
+
267
+ _lib.spir_sampling_release.argtypes = [spir_sampling]
268
+ _lib.spir_sampling_release.restype = None
269
+
270
+ _setup_prototypes()
271
+
272
+ # Python wrapper functions
273
+ def logistic_kernel_new(lambda_val):
274
+ """Create a new logistic kernel."""
275
+ status = c_int()
276
+ kernel = _lib.spir_logistic_kernel_new(lambda_val, byref(status))
277
+ if status.value != COMPUTATION_SUCCESS:
278
+ raise RuntimeError(f"Failed to create logistic kernel: {status.value}")
279
+ return kernel
280
+
281
+ def reg_bose_kernel_new(lambda_val):
282
+ """Create a new regularized bosonic kernel."""
283
+ status = c_int()
284
+ kernel = _lib.spir_reg_bose_kernel_new(lambda_val, byref(status))
285
+ if status.value != COMPUTATION_SUCCESS:
286
+ raise RuntimeError(f"Failed to create regularized bosonic kernel: {status.value}")
287
+ return kernel
288
+
289
+ def sve_result_new(kernel, epsilon, cutoff=None, lmax=None, n_gauss=None, Twork=None):
290
+ """Create a new SVE result."""
291
+ # Validate epsilon
292
+ if epsilon <= 0:
293
+ raise RuntimeError(f"Failed to create SVE result: epsilon must be positive, got {epsilon}")
294
+
295
+ if cutoff is None:
296
+ cutoff = -1.0
297
+ if lmax is None:
298
+ lmax = -1
299
+ if n_gauss is None:
300
+ n_gauss = -1
301
+ if Twork is None:
302
+ Twork = SPIR_TWORK_FLOAT64X2
303
+
304
+ status = c_int()
305
+ sve = _lib.spir_sve_result_new(kernel, epsilon, cutoff, lmax, n_gauss, Twork, byref(status))
306
+ if status.value != COMPUTATION_SUCCESS:
307
+ raise RuntimeError(f"Failed to create SVE result: {status.value}")
308
+ return sve
309
+
310
+ def sve_result_get_size(sve):
311
+ """Get the size of an SVE result."""
312
+ size = c_int()
313
+ status = _lib.spir_sve_result_get_size(sve, byref(size))
314
+ if status != COMPUTATION_SUCCESS:
315
+ raise RuntimeError(f"Failed to get SVE result size: {status}")
316
+ return size.value
317
+
318
+ def sve_result_get_svals(sve):
319
+ """Get the singular values from an SVE result."""
320
+ size = sve_result_get_size(sve)
321
+ svals = np.zeros(size, dtype=np.float64)
322
+ status = _lib.spir_sve_result_get_svals(sve, svals.ctypes.data_as(POINTER(c_double)))
323
+ if status != COMPUTATION_SUCCESS:
324
+ raise RuntimeError(f"Failed to get singular values: {status}")
325
+ return svals
326
+
327
+ def basis_new(statistics, beta, omega_max, kernel, sve, max_size):
328
+ """Create a new basis."""
329
+ status = c_int()
330
+ basis = _lib.spir_basis_new(
331
+ statistics, beta, omega_max, kernel, sve, max_size, byref(status)
332
+ )
333
+ if status.value != COMPUTATION_SUCCESS:
334
+ raise RuntimeError(f"Failed to create basis: {status.value}")
335
+ return basis
336
+
337
+ def basis_get_size(basis):
338
+ """Get the size of a basis."""
339
+ size = c_int()
340
+ status = _lib.spir_basis_get_size(basis, byref(size))
341
+ if status != COMPUTATION_SUCCESS:
342
+ raise RuntimeError(f"Failed to get basis size: {status}")
343
+ return size.value
344
+
345
+ def basis_get_svals(basis):
346
+ """Get the singular values of a basis."""
347
+ size = basis_get_size(basis)
348
+ svals = np.zeros(size, dtype=np.float64)
349
+ status = _lib.spir_basis_get_svals(basis, svals.ctypes.data_as(POINTER(c_double)))
350
+ if status != COMPUTATION_SUCCESS:
351
+ raise RuntimeError(f"Failed to get singular values: {status}")
352
+ return svals
353
+
354
+
355
+ def basis_get_stats(basis):
356
+ """Get the statistics type of a basis."""
357
+ stats = c_int()
358
+ status = _lib.spir_basis_get_stats(basis, byref(stats))
359
+ if status != COMPUTATION_SUCCESS:
360
+ raise RuntimeError(f"Failed to get basis statistics: {status}")
361
+ return stats.value
362
+
363
+ def basis_get_u(basis):
364
+ """Get the imaginary-time basis functions."""
365
+ status = c_int()
366
+ funcs = _lib.spir_basis_get_u(basis, byref(status))
367
+ if status.value != COMPUTATION_SUCCESS:
368
+ raise RuntimeError(f"Failed to get u basis functions: {status.value}")
369
+ return funcs
370
+
371
+ def basis_get_v(basis):
372
+ """Get the real-frequency basis functions."""
373
+ status = c_int()
374
+ funcs = _lib.spir_basis_get_v(basis, byref(status))
375
+ if status.value != COMPUTATION_SUCCESS:
376
+ raise RuntimeError(f"Failed to get v basis functions: {status.value}")
377
+ return funcs
378
+
379
+ def basis_get_uhat(basis):
380
+ """Get the Matsubara frequency basis functions."""
381
+ status = c_int()
382
+ funcs = _lib.spir_basis_get_uhat(basis, byref(status))
383
+ if status.value != COMPUTATION_SUCCESS:
384
+ raise RuntimeError(f"Failed to get uhat basis functions: {status.value}")
385
+ return funcs
386
+
387
+ def funcs_get_size(funcs):
388
+ """Get the size of a basis function set."""
389
+ size = c_int()
390
+ status = _lib.spir_funcs_get_size(funcs, byref(size))
391
+ if status != COMPUTATION_SUCCESS:
392
+ raise RuntimeError(f"Failed to get function size: {status}")
393
+ return size.value
394
+
395
+ # TODO: Rename funcs_eval_single
396
+ def funcs_eval_single_float64(funcs, x):
397
+ """Evaluate basis functions at a single point."""
398
+ # Get number of functions
399
+ size = c_int()
400
+ status = _lib.spir_funcs_get_size(funcs, byref(size))
401
+ if status != COMPUTATION_SUCCESS:
402
+ raise RuntimeError(f"Failed to get function size: {status}")
403
+
404
+ # Prepare output array
405
+ out = np.zeros(size.value, dtype=np.float64)
406
+
407
+ # Evaluate
408
+ status = _lib.spir_funcs_eval(
409
+ funcs, c_double(x),
410
+ out.ctypes.data_as(POINTER(c_double))
411
+ )
412
+ if status != COMPUTATION_SUCCESS:
413
+ raise RuntimeError(f"Failed to evaluate functions: {status}")
414
+
415
+ return out
416
+
417
+ # TODO: Rename to funcs_eval_matsu_single
418
+ def funcs_eval_single_complex128(funcs, x):
419
+ """Evaluate basis functions at a single point."""
420
+ # Get number of functions
421
+ size = c_int()
422
+ status = _lib.spir_funcs_get_size(funcs, byref(size))
423
+ if status != COMPUTATION_SUCCESS:
424
+ raise RuntimeError(f"Failed to get function size: {status}")
425
+
426
+ # Prepare output array
427
+ out = np.zeros(size.value, dtype=np.complex128)
428
+
429
+ # Evaluate
430
+ status = _lib.spir_funcs_eval_matsu(
431
+ funcs, c_int64(x),
432
+ out.ctypes.data_as(POINTER(c_double_complex))
433
+ )
434
+ if status != COMPUTATION_SUCCESS:
435
+ raise RuntimeError(f"Failed to evaluate functions: {status}")
436
+
437
+ return out
438
+
439
+ def funcs_get_n_roots(funcs):
440
+ """Get the number of roots of the basis functions."""
441
+ n_roots = c_int()
442
+ status = _lib.spir_funcs_get_n_roots(funcs, byref(n_roots))
443
+ if status != COMPUTATION_SUCCESS:
444
+ raise RuntimeError(f"Failed to get number of roots: {status}")
445
+ return n_roots.value
446
+
447
+ def funcs_get_roots(funcs):
448
+ """Get the roots of the basis functions."""
449
+ n_roots = funcs_get_n_roots(funcs)
450
+ roots = np.zeros(n_roots, dtype=np.float64)
451
+ status = _lib.spir_funcs_get_roots(funcs, roots.ctypes.data_as(POINTER(c_double)))
452
+ if status != COMPUTATION_SUCCESS:
453
+ raise RuntimeError(f"Failed to get roots: {status}")
454
+ return roots
455
+
456
+ def basis_get_default_tau_sampling_points(basis):
457
+ """Get default tau sampling points for a basis."""
458
+ # Get number of points
459
+ n_points = c_int()
460
+ status = _lib.spir_basis_get_n_default_taus(basis, byref(n_points))
461
+ if status != COMPUTATION_SUCCESS:
462
+ raise RuntimeError(f"Failed to get number of default tau points: {status}")
463
+
464
+ # Get the points
465
+ points = np.zeros(n_points.value, dtype=np.float64)
466
+ status = _lib.spir_basis_get_default_taus(basis, points.ctypes.data_as(POINTER(c_double)))
467
+ if status != COMPUTATION_SUCCESS:
468
+ raise RuntimeError(f"Failed to get default tau points: {status}")
469
+
470
+ return points
471
+
472
+ def basis_get_default_tau_sampling_points_ext(basis, n_points):
473
+ """Get default tau sampling points for a basis."""
474
+ points = np.zeros(n_points, dtype=np.float64)
475
+ n_points_returned = c_int()
476
+ status = _lib.spir_basis_get_default_taus_ext(basis, n_points, points.ctypes.data_as(POINTER(c_double)), byref(n_points_returned))
477
+ if status != COMPUTATION_SUCCESS:
478
+ raise RuntimeError(f"Failed to get default tau points: {status}")
479
+ return points
480
+
481
+ def basis_get_default_omega_sampling_points(basis):
482
+ """Get default omega (real frequency) sampling points for a basis."""
483
+ # Get number of points
484
+ n_points = c_int()
485
+ status = _lib.spir_basis_get_n_default_ws(basis, byref(n_points))
486
+ if status != COMPUTATION_SUCCESS:
487
+ raise RuntimeError(f"Failed to get number of default omega points: {status}")
488
+
489
+ # Get the points
490
+ points = np.zeros(n_points.value, dtype=np.float64)
491
+ status = _lib.spir_basis_get_default_ws(basis, points.ctypes.data_as(POINTER(c_double)))
492
+ if status != COMPUTATION_SUCCESS:
493
+ raise RuntimeError(f"Failed to get default omega points: {status}")
494
+
495
+ return points
496
+
497
+ def basis_get_default_matsubara_sampling_points(basis, positive_only=False):
498
+ """Get default Matsubara sampling points for a basis."""
499
+ # Get number of points
500
+ n_points = c_int()
501
+ status = _lib.spir_basis_get_n_default_matsus(basis, c_bool(positive_only), byref(n_points))
502
+ if status != COMPUTATION_SUCCESS:
503
+ raise RuntimeError(f"Failed to get number of default Matsubara points: {status}")
504
+
505
+ # Get the points
506
+ points = np.zeros(n_points.value, dtype=np.int64)
507
+ status = _lib.spir_basis_get_default_matsus(basis, c_bool(positive_only), points.ctypes.data_as(POINTER(c_int64)))
508
+ if status != COMPUTATION_SUCCESS:
509
+ raise RuntimeError(f"Failed to get default Matsubara points: {status}")
510
+
511
+ return points
512
+
513
+ def basis_get_n_default_matsus_ext(basis, n_points, positive_only):
514
+ """Get the number of default Matsubara sampling points for a basis."""
515
+ n_points_returned = c_int()
516
+ status = _lib.spir_basis_get_n_default_matsus_ext(basis, c_bool(positive_only), n_points, byref(n_points_returned))
517
+ if status != COMPUTATION_SUCCESS:
518
+ raise RuntimeError(f"Failed to get number of default Matsubara points: {status}")
519
+ return n_points_returned.value
520
+
521
+ def basis_get_default_matsus_ext(basis, positive_only, points):
522
+ n_points = len(points)
523
+ n_points_returned = c_int()
524
+ status = _lib.spir_basis_get_default_matsus_ext(basis, c_bool(positive_only), n_points, points.ctypes.data_as(POINTER(c_int64)), byref(n_points_returned))
525
+ if status != COMPUTATION_SUCCESS:
526
+ raise RuntimeError(f"Failed to get default Matsubara points: {status}")
527
+ return points
528
+
529
+ def tau_sampling_new(basis, sampling_points=None):
530
+ """Create a new tau sampling object."""
531
+ if sampling_points is None:
532
+ sampling_points = basis_get_default_tau_sampling_points(basis)
533
+
534
+ sampling_points = np.asarray(sampling_points, dtype=np.float64)
535
+ n_points = len(sampling_points)
536
+
537
+ status = c_int()
538
+ sampling = _lib.spir_tau_sampling_new(
539
+ basis, n_points,
540
+ sampling_points.ctypes.data_as(POINTER(c_double)),
541
+ byref(status)
542
+ )
543
+ if status.value != COMPUTATION_SUCCESS:
544
+ raise RuntimeError(f"Failed to create tau sampling: {status.value}")
545
+
546
+ return sampling
547
+
548
+ def _statistics_to_c(statistics):
549
+ """Convert statistics to c type."""
550
+ if statistics == "F":
551
+ return SPIR_STATISTICS_FERMIONIC
552
+ elif statistics == "B":
553
+ return SPIR_STATISTICS_BOSONIC
554
+ else:
555
+ raise ValueError(f"Invalid statistics: {statistics}")
556
+
557
+ def tau_sampling_new_with_matrix(basis, statistics, sampling_points, matrix):
558
+ """Create a new tau sampling object with a matrix."""
559
+ status = c_int()
560
+ sampling = _lib.spir_tau_sampling_new_with_matrix(
561
+ SPIR_ORDER_ROW_MAJOR,
562
+ _statistics_to_c(statistics),
563
+ basis.size,
564
+ sampling_points.size,
565
+ sampling_points.ctypes.data_as(POINTER(c_double)),
566
+ matrix.ctypes.data_as(POINTER(c_double)),
567
+ byref(status)
568
+ )
569
+ if status.value != COMPUTATION_SUCCESS:
570
+ raise RuntimeError(f"Failed to create tau sampling: {status.value}")
571
+
572
+ return sampling
573
+
574
+ def matsubara_sampling_new(basis, positive_only=False, sampling_points=None):
575
+ """Create a new Matsubara sampling object."""
576
+ if sampling_points is None:
577
+ sampling_points = basis_get_default_matsubara_sampling_points(basis, positive_only)
578
+
579
+ sampling_points = np.asarray(sampling_points, dtype=np.int64)
580
+ n_points = len(sampling_points)
581
+
582
+ status = c_int()
583
+ sampling = _lib.spir_matsu_sampling_new(
584
+ basis, c_bool(positive_only), n_points,
585
+ sampling_points.ctypes.data_as(POINTER(c_int64)),
586
+ byref(status)
587
+ )
588
+ if status.value != COMPUTATION_SUCCESS:
589
+ raise RuntimeError(f"Failed to create Matsubara sampling: {status.value}")
590
+
591
+ return sampling
592
+
593
+ def matsubara_sampling_new_with_matrix(statistics, basis_size, positive_only, sampling_points, matrix):
594
+ """Create a new Matsubara sampling object with a matrix."""
595
+ status = c_int()
596
+ sampling = _lib.spir_matsu_sampling_new_with_matrix(
597
+ SPIR_ORDER_ROW_MAJOR, # order
598
+ _statistics_to_c(statistics), # statistics
599
+ c_int(basis_size), # basis_size
600
+ c_bool(positive_only), # positive_only
601
+ c_int(len(sampling_points)), # num_points
602
+ sampling_points.ctypes.data_as(POINTER(c_int64)), # points
603
+ matrix.ctypes.data_as(POINTER(c_double_complex)), # matrix
604
+ byref(status) # status
605
+ )
606
+ if status.value != COMPUTATION_SUCCESS:
607
+ raise RuntimeError(f"Failed to create Matsubara sampling: {status.value}")
608
+
609
+ return sampling
@@ -0,0 +1,40 @@
1
+ """
2
+ Type definitions for the SparseIR C API.
3
+ """
4
+
5
+ from ctypes import *
6
+ import numpy as np
7
+
8
+ # Define complex type
9
+ c_complex = c_double * 2
10
+
11
+ # Opaque types
12
+ class _spir_kernel(Structure):
13
+ _fields_ = []
14
+
15
+ class _spir_funcs(Structure):
16
+ _fields_ = []
17
+
18
+ class _spir_basis(Structure):
19
+ _fields_ = []
20
+
21
+ class _spir_sampling(Structure):
22
+ _fields_ = []
23
+
24
+ class _spir_sve_result(Structure):
25
+ _fields_ = []
26
+
27
+ # Type aliases
28
+ spir_kernel = POINTER(_spir_kernel)
29
+ spir_funcs = POINTER(_spir_funcs)
30
+ spir_basis = POINTER(_spir_basis)
31
+ spir_sampling = POINTER(_spir_sampling)
32
+ spir_sve_result = POINTER(_spir_sve_result)
33
+
34
+ # Additional ctypes definitions
35
+ c_int64 = c_longlong
36
+
37
+ # NumPy dtype mappings
38
+ COMPLEX_DTYPE = np.dtype(np.complex128)
39
+ DOUBLE_DTYPE = np.dtype(np.float64)
40
+ INT64_DTYPE = np.dtype(np.int64)
Binary file
Binary file
Binary file
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: pylibsparseir
3
+ Version: 0.1.0
4
+ Summary: Python bindings for the libsparseir library, providing efficient sparse intermediate representation for many-body physics calculations
5
+ Author: SpM-lab
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SpM-lab/libsparseir
8
+ Project-URL: Repository, https://github.com/SpM-lab/libsparseir
9
+ Project-URL: Bug Tracker, https://github.com/SpM-lab/libsparseir/issues
10
+ Keywords: physics,many-body,green-functions,sparse-ir
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Scientific/Engineering :: Physics
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: numpy
19
+ Requires-Dist: scipy
20
+
21
+ # Python bindings for libsparseir
22
+
23
+ This is a low-level binding for the [libsparseir](https://github.com/SpM-lab/libsparseir) library.
24
+
25
+ ## Requirements
26
+
27
+ - Python >= 3.10
28
+ - CMake (for building the C++ library)
29
+ - C++11 compatible compiler
30
+ - numpy
31
+
32
+ ### Optional Dependencies
33
+
34
+ - **OpenBLAS** (recommended for better performance)
35
+ - macOS: `brew install openblas`
36
+ - Ubuntu/Debian: `sudo apt install libopenblas-dev`
37
+ - CentOS/RHEL: `sudo yum install openblas-devel`
38
+
39
+ ## Build
40
+
41
+ ### Install Dependencies and Build
42
+
43
+ ```bash
44
+ uv sync
45
+ ```
46
+
47
+ This will:
48
+ - Install Python dependencies (numpy)
49
+ - Build the C++ libsparseir library using CMake
50
+ - Install the Python package in development mode
51
+
52
+ ### Build with OpenBLAS Support
53
+
54
+ To enable OpenBLAS support for improved performance:
55
+
56
+ ```bash
57
+ # Set environment variable to enable BLAS
58
+ export SPARSEIR_USE_BLAS=1
59
+ uv sync
60
+ ```
61
+
62
+ Or for a single build:
63
+
64
+ ```bash
65
+ SPARSEIR_USE_BLAS=1 uv sync
66
+ ```
67
+
68
+ The build system will automatically detect OpenBLAS if it's installed in standard locations. If OpenBLAS is installed in a custom location, you may need to set additional environment variables:
69
+
70
+ ```bash
71
+ export CMAKE_PREFIX_PATH="/path/to/openblas"
72
+ export SPARSEIR_USE_BLAS=1
73
+ uv sync
74
+ ```
75
+
76
+ ### Clean Build Artifacts
77
+
78
+ To remove build artifacts and files copied from the parent directory:
79
+
80
+ ```bash
81
+ uv run clean
82
+ ```
83
+
84
+ This will remove:
85
+ - Build directories: `build/`, `dist/`, `*.egg-info`
86
+ - Copied source files: `include/`, `src/`, `fortran/`, `cmake/`, `CMakeLists.txt`
87
+ - Compiled libraries: `pylibsparseir/*.so`, `pylibsparseir/*.dylib`, `pylibsparseir/*.dll`
88
+ - Cache directories: `pylibsparseir/__pycache__`
89
+
90
+ ## Performance Notes
91
+
92
+ ### BLAS Support
93
+
94
+ This package supports BLAS libraries for improved linear algebra performance:
95
+
96
+ - **With OpenBLAS**: Significant performance improvements for matrix operations
97
+ - **Without BLAS**: Uses Eigen's built-in implementations (still efficient, but slower for large matrices)
98
+
99
+ The build system will automatically detect and use OpenBLAS if available. You can verify BLAS support by checking the build output for messages like:
100
+
101
+ ```
102
+ BLAS support enabled
103
+ Found OpenBLAS at: /opt/homebrew/opt/openblas
104
+ ```
105
+
106
+ ### Troubleshooting
107
+
108
+ **Build fails with "Could NOT find BLAS":**
109
+ ```bash
110
+ # Install OpenBLAS first
111
+ brew install openblas # macOS
112
+ sudo apt install libopenblas-dev # Ubuntu
113
+
114
+ # Then force BLAS detection
115
+ SPARSEIR_USE_BLAS=1 uv sync
116
+ ```
117
+
118
+ **OpenBLAS not detected automatically:**
119
+ ```bash
120
+ # Set CMake prefix path manually
121
+ export CMAKE_PREFIX_PATH="/usr/local/opt/openblas" # or your OpenBLAS path
122
+ export SPARSEIR_USE_BLAS=1
123
+ uv sync
124
+ ```
125
+
126
+ **Verify BLAS support in built package:**
127
+ ```python
128
+ import pylibsparseir
129
+ # Check build logs or library dependencies to confirm BLAS linking
130
+ ```
@@ -0,0 +1,14 @@
1
+ pylibsparseir/clean_build_artifacts.py,sha256=Wbfe_zlZB5T04pXR8rvRAvsaLMYl6Y61p77W-eRKUME,2079
2
+ pylibsparseir/constants.py,sha256=Ek3JnmgiDWy3LB8f_9QA7Gh0ReaEC-W2MlCBJG3cBYg,864
3
+ pylibsparseir/core.py,sha256=37sGLY_Gbddf3x-kScoJtmyaLOeDOFaPbBCMPIMLOP0,23647
4
+ pylibsparseir/ctypes_wrapper.py,sha256=G6_eraRUh9ON_2e_v4U_hc34HXirlotoawQDOkS0my0,798
5
+ pylibsparseir/libsparseir.so,sha256=p9tMeYFBERToWxH5ooWPvP9yqQYtdLP1B0aHm9VxlHI,2713377
6
+ pylibsparseir/libsparseir.so.0,sha256=p9tMeYFBERToWxH5ooWPvP9yqQYtdLP1B0aHm9VxlHI,2713377
7
+ pylibsparseir/libsparseir.so.0.4.2,sha256=p9tMeYFBERToWxH5ooWPvP9yqQYtdLP1B0aHm9VxlHI,2713377
8
+ pylibsparseir.libs/libgcc_s-0cd532bd.so.1,sha256=yPk0-VjyKzucjnkP3mvC0vVaua6Ln17qZUJbICcXgtA,181737
9
+ pylibsparseir.libs/libstdc++-5d72f927.so.6.0.33,sha256=fogxHsmB1_D6C-a_-uHh8Ei_6Qh52a8vLlicJRM3ehk,3562401
10
+ pylibsparseir-0.1.0.dist-info/METADATA,sha256=CItKxoGfymHNuwhCTxzA66svXd_uU4dgBHU-r8JYcVA,3585
11
+ pylibsparseir-0.1.0.dist-info/WHEEL,sha256=4VbEOkf4fdBUBHdV24POjoH-zuik_eIDLSImZZCAQpQ,112
12
+ pylibsparseir-0.1.0.dist-info/entry_points.txt,sha256=0XwdYIQ1lPtI8228zUo7BpvdnwOzqct0cc6gp3r18pA,84
13
+ pylibsparseir-0.1.0.dist-info/top_level.txt,sha256=eKNiduo4CIO59083m_BjY8uBZ9Fp1D3t8hkU4-k2waA,14
14
+ pylibsparseir-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp313-cp313-musllinux_1_2_x86_64
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ clean = pylibsparseir.clean_build_artifacts:clean_build_artifacts
@@ -0,0 +1 @@
1
+ pylibsparseir