[hist] Seed the parameters of a pol1 fit when coordinate errors are used - #22967
[hist] Seed the parameters of a pol1 fit when coordinate errors are used#22967tekinertekin wants to merge 2 commits into
Conversation
Test Results 23 files 23 suites 3d 18h 19m 6s ⏱️ Results for commit 0dd922e. ♻️ This comment has been updated with latest results. |
hageboeck
left a comment
There was a problem hiding this comment.
Hello, thanks for the contribution! I think that's a great idea, but could you check the suggestions below?
Furthermore, we typically split code changes and tests into different commits, so could you add a second commit that adds the test, please?
Coordinate errors turn the linear fitter off in HFitImpl.cxx, so a polN fit went to the minimizer carrying whatever parameters the function already had. From a starting point far from the solution, a line of negative slope could converge with the sign reversed. Add ROOT::Fit::InitPolynom, which seeds the fit from an unweighted least-squares line, and call it from the same block that already seeds gaus, expo and landau.
…rrors Covers both slope signs with coordinate errors, the plain case without them, and the case where ex is zero so the linear fitter stays on.
28f0aa6 to
0dd922e
Compare
Thanks a lot for your reviews @hageboeck . All three addressed:
|
A
pol1fit of aTGraphErrorsdiverges when the x-errors dominate the y-errors and the true slope is negative. On exactly collinear data (y = 7 - x) the fit returns a slope with the wrong sign:Cause
HFitImpl::Fitmarks a polynomial as linear (special == 299 + npar), but a few lines further down coordinate errors switch the linear fitter off again:The polynomial therefore reaches the minimizer, and the seeding block that follows covers only gaus (100), 2D-gaus (110/112), landau (400/410) and expo (200) — there is no polynomial case. The fit starts from whatever parameters the function happens to carry, and from the defaults it converges into the wrong valley.
That the missing seed is the cause can be shown on the data above by varying only the starting point:
pol1parametersTGraphfitThis also matches the observation in the issue that setting the initial parameters by hand is a working workaround.
Fix
ROOT::Fit::InitPolynomcomputes an unweighted least-squares line through the data and sets it as the starting point, following the existingInitExpo/InitGauspattern (those are unweighted too — the aim is a rough starting point, not a solved fit). It is wired in with the same polynomial test already used above,special == 299 + npar; writingspecial >= 300would also capture landau (400) and 2D-landau (410) and override their gaus seeding.Only the first-degree case is seeded —
InitPolynomreturns unchanged for a higher degree, sopol2and above keep their current behaviour. Generalising means changing that one function, with no change at the call site; happy to do it here if you would prefer.Tests
Four cases in
hist/hist/test/test_TGraphErrors_polFit.cxx: the diverging fit, the positive-slope fit that already converged from the defaults, the same data without coordinate errors, and the same data withEX0. The last three pass before the change as well, so they guard the paths the new seeding must not disturb.Each case builds its own
TF1rather than using the globalpol1: apol1fit performed earlier in the same process leaves good parameters behind and hides the bug entirely.Verified locally with the fix (4/4 pass), and with only the call in
HFitImpl.cxxremoved (exactly the first case fails, the other three still pass).testTFractionFitter,testTH1andtestTGraphSortingalso pass.git-clang-formatagainst master is clean with clang-format 20.1.8.Related: #22651 touched the same file for multithreaded
TGraphErrorsfits with x errors.I used an AI tool to help with this change. It created the patch and the regression test, reproduced the bug, and ran the test suite and lint. I have reviewed, understood, and verified the change and take full responsibility for it.
Changes or fixes:
Fixes #13895.
Checklist: