question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Setting bounds on model can cause fit to diverge

See original GitHub issue

Description

When using astropy models to perform astro/photometry, fitting a model to a star image with bounds causes the fit to diverge to the edges of the parameter space. A fit without bounds works fine and converges to a value within the bounds. With the a more complex PSF model, with stronger side-maxima than a plain Airy disc, this happens basically every time. Both with the LevenbergMarquardt and Simplex fitters.

My suspicion without having fully understood the fitting framework is that these lines in astropy/modeling/fitting.py effectively “brickwall” the parameters, throwing off the gradient calculation / simplex direction and cause the fit to “stick” to the edge of the parameter bounds.

# Check bounds constraints
if model.bounds[name] != (None, None):
    _min, _max = model.bounds[name]
    if _min is not None:
        values = np.fmax(values, _min)
    if _max is not None:
        values = np.fmin(values, _max)

I found a possible explanation/solution to the issue described here: https://lmfit.github.io/lmfit-py/bounds.html
More recent versions of scipy.optimize.least_squares seem to support bounds on parameters for trf directly.

Expected behavior

Fit with bounds should not perform worse than a unbounded fit that finds the minimum within the bounds.

Steps to Reproduce

from astropy.modeling.functional_models import AiryDisk2D
from astropy.modeling.fitting import LevMarLSQFitter
import numpy as np

np.random.seed(999999)

fitter = LevMarLSQFitter()
y, x = np.mgrid[-5:5:50j, -5:5:50j]

# Airy Disk mostly works, bigger problems with models that have more sidelobes/wings
input_model = AiryDisk2D(radius=2)

# build noisy image from input model
img = np.random.poisson(1e5*input_model(x, y)) + np.random.normal(0, 20)

fit_model = input_model.copy()

# bad guess for amplitude, decent guesses for position causes fitter to want to explore
# out of bounds, gets stuck there if bounds enabled
fit_model.x_0 = 0.1
fit_model.y_0 = -0.08
fit_model.amplitude = 1e3


fitted = fitter(fit_model, x, y, img)
print(f'without bounds: {fitted.x_0=} {fitted.y_0=} {fitted.amplitude=}')

fit_model.bounds['x_0'] = (-0.2, 0.2)
fit_model.bounds['y_0'] = (-0.2, 0.2)

fitted = fitter(fit_model, x, y, img)
print(f'with bounds: {fitted.x_0=} {fitted.y_0=} {fitted.amplitude=}')

prints

without bounds: fitted.x_0=Parameter('x_0', value=0.00019776080448936212) fitted.y_0=Parameter('y_0', value=-9.740265185190977e-05) fitted.amplitude=Parameter('amplitude', value=99967.10092998871)
with bounds: fitted.x_0=Parameter('x_0', value=-0.2, bounds=(-0.2, 0.2)) fitted.y_0=Parameter('y_0', value=0.2, bounds=(-0.2, 0.2)) fitted.amplitude=Parameter('amplitude', value=92364.16417303146)

System Details

Linux-5.12.15-arch1-1-x86_64-with-glibc2.33
Python 3.9.6 (default, Jun 30 2021, 10:22:16) 
[GCC 11.1.0]
Numpy 1.20.3
astropy 4.2.1
Scipy 1.7.0

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
krachyoncommented, Nov 29, 2021

@WilliamJamieson Sorry for not responding earlier, I got a bit sidetracked on my fitting problem. I finally got around to verify that the new fitter seems to work fine and does not cause any issues with stuck bounds anymore.

Repro script is here in case you’re interested, output is a pandas dataframe for analysis: https://gist.github.com/krachyon/e94c46f2abd65351f77f9d00084e87b1

Thanks a lot for the fix!

0reactions
WilliamJamiesoncommented, Aug 13, 2021

Do you have the prototype on a branch somewhere? Maybe I can figure out what’s going on for the unit-test and if it could work better if I play around with it a bit…

I just opened a draft PR #12051 based on my prototype.

Read more comments on GitHub >

github_iconTop Results From Across the Web

2021 Specialized Diverge Expert Carbon review - CyclingTips
In the fully open setting, the Future Shock 2.0 is soft and supple, greatly enhancing not only comfort, but also control and traction...
Read more >
3D divergence theorem (article) | Khan Academy
Also known as Gauss's theorem, the divergence theorem is a tool for translating between surface integrals and triple integrals.
Read more >
Polynomial Models - MATLAB & Simulink - MathWorks
The main disadvantage is that high-degree fits can become unstable. Additionally, polynomials of ... You can exclude any term by setting its bounds...
Read more >
User Manual - MY20 Diverge
Installation of a crown-mounted cargo rack, such as the Specialized Pizza Rack, can result in failure, which can result in serious injury or...
Read more >
Runtime warnings and convergence problems - Stan
Diagnosing a model that takes a long time to fit and has many parameters is difficult, so working with a suitable subset of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found