Fix real-input Matsubara fit and complex DLR at the ctypes boundary - #81
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes two ctypes-boundary defects that make real-input Matsubara fitting and complex-coefficient DLR unusable, plus a patch version bump.
1.
MatsubaraSampling.fitwith real-dtype input produced silent garbagesrc/sparse_ir/sampling.py,MatsubaraSampling.fit(around line 316) passed the input buffer straight tospir_sampling_fit_zz(the complex128 C entry point) without normalizing dtype first. Afloat64array was reinterpreted ascomplex128— i.e. its raw byte buffer was read as though it were twice as long as actually allocated. This is an out-of-bounds read that does not raise: it silently returns finite-looking-but-wrong (in practicenan) values.Failure mode: silent numerical garbage, no exception.
Fix: normalize
axtocomplex128vianp.ascontiguousarray(ax, dtype=np.complex128)before taking the pointer, matching the pattern already used byTauSampling.fit/evaluateandMatsubaraSampling.evaluate. Added a dtype guard (ax.dtype.kind not in ("f", "c")) that raisesValueErrorfor unsupported dtypes instead of silently misreading them.2. Complex DLR (
from_IR/to_IR) raised actypes.ArgumentErrorsrc/sparse_ir/dlr.py,DiscreteLehmannRepresentation.from_IR/to_IR(lines ~137–148 and ~194–205) cast complex128 buffers asctypes.c_doublepointers (with a leftover# TODO: use complex data) when callingspir_ir2dlr_zz/spir_dlr2ir_zz, which actually expectc_double_complexpointers.Failure mode:
ctypes.ArgumentError: argument 7: TypeError: expected LP_c_double_complex instance instead of LP_c_double— complex DLR coefficients were completely unusable.Fix: mirror the pattern used in
sampling.py: convert input tocomplex128contiguous arrays, allocate the output as ac_double_complex-dtyped structured array, pass properPOINTER(c_double_complex)pointers to the_zzentry points, and reassemble the output asoutput['real'] + 1j * output['imag']. Confirmedpylibsparseirexposes the needed complex-capable bindings (spir_ir2dlr_zz,spir_dlr2ir_zz) withLP_c_double_complexargtypes.Regression tests
tests/test_sampling.py: addedtest_evaluate_fit_roundtrip_complexto bothTestTauSamplingandTestMatsubaraSampling(rng seed 42, assertsnp.iscomplexobjand max error < 1e-12 including.imag).tests/test_sampling.py: addedTestMatsubaraSampling::test_fit_real_input_matches_complex_reference, the regression test for defect 1 — fits a real float64 array and checks it agrees with fitting the same values cast to complex128 (pre-fix this producednan).tests/test_dlr.py: addedtest_complex_roundtrip, the regression test for defect 2 — builds complex IR coefficients from known poles (mirroringtest_compression's construction so the round trip is expected to be near-exact) and checksfrom_IR/to_IRround-trips within300*eps(pre-fix this raisedctypes.ArgumentError).Verified both defects experimentally against the pre-fix code: defect 1's test failed with
nanvalues (silent garbage), and defect 2's test failed withctypes.ArgumentError(TypeError), exactly matching the reported failure modes.Version bump
pyproject.toml:2.1.2→2.1.3(patch bump for bugfix release)..conda/meta.yamlreads its version dynamically frompyproject.tomlviaload_file_regex, so no separate edit was needed there.check_libsparseir_version_consistency.pypasses.Test results
test_fit_real_input_matches_complex_reference,test_complex_roundtrip), matching the described failure modes.101 passed, 8 skipped(pre-existing skips intest_sve_advanced.py, unrelated to this change).🤖 Generated with Claude Code