Skip to content

Conversation

@fjosw
Copy link
Owner

@fjosw fjosw commented Feb 3, 2026

scipy.odr is being deprecated, see https://discuss.scientific-python.org/t/rfc-deprecating-scipy-odr/2166

  • Migrate from deprecated scipy.odr to the odrpack library for orthogonal distance regression (ODR) fitting
  • Update total_least_squares function to use the new odrpack.odr_fit API
  • Add odrpack>=0.4 as a new dependency

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates orthogonal distance regression (total least squares) away from deprecated scipy.odr to odrpack’s odr_fit API.

Changes:

  • Replace scipy.odr usage with odrpack.odr_fit in pyerrors.fits.total_least_squares.
  • Update test reference implementations/helpers to use odr_fit (including adapting function signature expectations).
  • Add odrpack>=0.4 to runtime dependencies.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
pyerrors/fits.py Switches total_least_squares backend from scipy.odr to odrpack.odr_fit and adapts output handling.
tests/fits_test.py Updates ODR-based tests/helpers to call odr_fit and adjusts initial guess dtypes.
setup.py Adds odrpack>=0.4 to install_requires.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

out = odr.run()
x0 = np.ones(n_parms, dtype=np.float64)

# odrpack expects f(x, beta), but pyerrors convention is f(beta, x)
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation now uses odrpack.odr_fit, but total_least_squares still documents itself as being based on SciPy ODR (see this function’s docstring “Notes” section) and the package-level docs in pyerrors/__init__.py still reference SciPy’s ODR (and even point readers to fits.least_squares). Please update the documentation to reflect the new odrpack backend and reference fits.total_least_squares where appropriate, so users aren’t directed to deprecated/incorrect APIs.

Suggested change
# odrpack expects f(x, beta), but pyerrors convention is f(beta, x)
# The total_least_squares backend uses odrpack.odr_fit, which expects
# a model signature f(x, beta), while pyerrors conventions use f(beta, x).

Copilot uses AI. Check for mistakes.
@s-kuberski
Copy link
Collaborator

Hi!

Thanks for monitoring the deprecation of various packages... I started to look into odrpack and the underlying package. It seems that one could possibly extend the functionality of the current routine total_least_squares, by giving access to new features, compared to the old scipy implementation.

On your changes: So far, I have only compared the two code versions by staring at the differences and did not find any issue, apart from the missing change in the docstring that copilot indicated.

However, the tests reveal a problem, namely that the new package does not like fits with vanishing $\chi^2$:

from odrpack import odr_fit
import numpy as np

def wrapped_func(x, beta):
    return beta[0] + beta[1] * x


for lim in [3, 2]:
    ox = [1, 2, 3][:lim]
    oy = [1, 2, 3.1][:lim]
    
    output = odr_fit(
            wrapped_func,
            np.array([o for o in ox]),
            np.array([o for o in oy]),
            beta0=np.array([0.0, 0.0]),
            weight_x=1.0 / np.array([o for o in ox]) ** 2,
            weight_y=1.0 / np.array([o for o in oy]) ** 2,
            partol=np.finfo(np.float64).eps,
            task='explicit-ODR',
            diff_scheme='central'
        )
    
    print(lim, output.stopreason)

gives

3 Sum of squares convergence.
2 Questionable results or fatal errors detected. See report and error message.

and therefore, we get an error in the fit routine.

This is clearly not the behavior that we would like to have.

Curently, I only see the solution to explicitly check the stop reason in combination with the details of output in combination with the check if the number of observations is equal to the number of parameters.
I've tried to understand the documentation of ODRPACK95, regarding the error codes in output.info. In the case of the trivial fit we get info=12 and I understand this as indication for "problem is not full rank at solution". We also have irank=1, inv_condnum=1.0 here.

Once it is clear that all/any of these information are sufficient to catch this special case, we can implement the exception to the error.

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.

2 participants