From 8b3b1f3c9544b8cb3d32d24b9534f886e817d6ca Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Sun, 29 Mar 2026 20:41:19 +0200 Subject: [PATCH] [Fix] Improve rank-deficient detection and bump odrpack to >=0.5 Fix incorrect ODRPACK95 info code parsing: rank deficiency is encoded in the tens digit (info // 10 % 10), not the hundreds digit. Add irank and inv_condnum to the warning message for diagnostics. --- pyerrors/fits.py | 16 ++++++++++------ setup.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pyerrors/fits.py b/pyerrors/fits.py index 98763b9d..928634d8 100644 --- a/pyerrors/fits.py +++ b/pyerrors/fits.py @@ -670,13 +670,17 @@ def total_least_squares(x, y, func, silent=False, **kwargs): print('Residual variance:', output.residual_variance) if not out.success: - # info % 5 gives the convergence status: 1=sum-of-sq, 2=param, 3=both - # If odrpack reports rank deficiency (e.g. vanishing chi-squared when - # n_obs == n_parms), convergence was still achieved – allow with a warning. - if out.info % 5 in [1, 2, 3]: + # ODRPACK95 info code structure (see User Guide §4): + # info % 10 -> convergence: 1=sum-of-sq, 2=param, 3=both + # info // 10 % 10 -> 1 = problem not full rank at solution + convergence_status = out.info % 10 + rank_deficient = (out.info // 10 % 10) == 1 + + if convergence_status in [1, 2, 3] and rank_deficient: warnings.warn( - "ODR fit is rank deficient. This may indicate a vanishing " - "chi-squared (n_obs == n_parms). Results may be unreliable.", + f"ODR fit is rank deficient (irank={out.irank}, inv_condnum={out.inv_condnum:.2e}). " + "This may indicate a vanishing chi-squared (n_obs == n_parms). " + "Results may be unreliable.", RuntimeWarning ) else: diff --git a/setup.py b/setup.py index def5666f..a8879675 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ setup(name='pyerrors', license="MIT", packages=find_packages(), python_requires='>=3.10.0', - install_requires=['numpy>=2.0', 'autograd>=1.7.0', 'numdifftools>=0.9.41', 'matplotlib>=3.9', 'scipy>=1.13', 'iminuit>=2.28', 'h5py>=3.11', 'lxml>=5.0', 'python-rapidjson>=1.20', 'pandas>=2.2', 'odrpack>=0.4'], + install_requires=['numpy>=2.0', 'autograd>=1.7.0', 'numdifftools>=0.9.41', 'matplotlib>=3.9', 'scipy>=1.13', 'iminuit>=2.28', 'h5py>=3.11', 'lxml>=5.0', 'python-rapidjson>=1.20', 'pandas>=2.2', 'odrpack>=0.5'], extras_require={'test': ['pytest', 'pytest-cov', 'pytest-benchmark', 'hypothesis', 'nbmake', 'flake8']}, classifiers=[ 'Development Status :: 5 - Production/Stable',