Skip to content

Fix real-input Matsubara fit and complex DLR at the ctypes boundary - #81

Merged
shinaoka merged 1 commit into
mainlinefrom
fix-complex-ffi-paths
Sep 4, 2026
Merged

Fix real-input Matsubara fit and complex DLR at the ctypes boundary#81
shinaoka merged 1 commit into
mainlinefrom
fix-complex-ffi-paths

Conversation

@shinaoka

@shinaoka shinaoka commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

Fixes two ctypes-boundary defects that make real-input Matsubara fitting and complex-coefficient DLR unusable, plus a patch version bump.

1. MatsubaraSampling.fit with real-dtype input produced silent garbage

src/sparse_ir/sampling.py, MatsubaraSampling.fit (around line 316) passed the input buffer straight to spir_sampling_fit_zz (the complex128 C entry point) without normalizing dtype first. A float64 array was reinterpreted as complex128 — 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 practice nan) values.

Failure mode: silent numerical garbage, no exception.

Fix: normalize ax to complex128 via np.ascontiguousarray(ax, dtype=np.complex128) before taking the pointer, matching the pattern already used by TauSampling.fit/evaluate and MatsubaraSampling.evaluate. Added a dtype guard (ax.dtype.kind not in ("f", "c")) that raises ValueError for unsupported dtypes instead of silently misreading them.

2. Complex DLR (from_IR/to_IR) raised a ctypes.ArgumentError

src/sparse_ir/dlr.py, DiscreteLehmannRepresentation.from_IR/to_IR (lines ~137–148 and ~194–205) cast complex128 buffers as ctypes.c_double pointers (with a leftover # TODO: use complex data) when calling spir_ir2dlr_zz/spir_dlr2ir_zz, which actually expect c_double_complex pointers.

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 to complex128 contiguous arrays, allocate the output as a c_double_complex-dtyped structured array, pass proper POINTER(c_double_complex) pointers to the _zz entry points, and reassemble the output as output['real'] + 1j * output['imag']. Confirmed pylibsparseir exposes the needed complex-capable bindings (spir_ir2dlr_zz, spir_dlr2ir_zz) with LP_c_double_complex argtypes.

Regression tests

  • tests/test_sampling.py: added test_evaluate_fit_roundtrip_complex to both TestTauSampling and TestMatsubaraSampling (rng seed 42, asserts np.iscomplexobj and max error < 1e-12 including .imag).
  • tests/test_sampling.py: added TestMatsubaraSampling::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 produced nan).
  • tests/test_dlr.py: added test_complex_roundtrip, the regression test for defect 2 — builds complex IR coefficients from known poles (mirroring test_compression's construction so the round trip is expected to be near-exact) and checks from_IR/to_IR round-trips within 300*eps (pre-fix this raised ctypes.ArgumentError).

Verified both defects experimentally against the pre-fix code: defect 1's test failed with nan values (silent garbage), and defect 2's test failed with ctypes.ArgumentError (TypeError), exactly matching the reported failure modes.

Version bump

pyproject.toml: 2.1.22.1.3 (patch bump for bugfix release). .conda/meta.yaml reads its version dynamically from pyproject.toml via load_file_regex, so no separate edit was needed there. check_libsparseir_version_consistency.py passes.

Test results

  • Before fix (new regression tests only, run against unfixed code): 2 failed (test_fit_real_input_matches_complex_reference, test_complex_roundtrip), matching the described failure modes.
  • After fix: full suite 101 passed, 8 skipped (pre-existing skips in test_sve_advanced.py, unrelated to this change).

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shinaoka
shinaoka merged commit 2ebfac0 into mainline Sep 4, 2026
9 checks passed
@shinaoka
shinaoka deleted the fix-complex-ffi-paths branch September 4, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant