Skip to content

Refactor chisquare function in least squares #158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 3, 2023
Merged

Conversation

fjosw
Copy link
Owner

@fjosw fjosw commented Mar 1, 2023

This is the second step in refactoring the fit module in which I removed redundant formulations of the chisquare function. Please have another close look @s-kuberski & @PiaLJP

@fjosw fjosw requested a review from s-kuberski March 1, 2023 16:54
@s-kuberski
Copy link
Collaborator

Thanks. I would like to discuss the general ansatz we use to distinguish between correlated and uncorrelated fits. In principle, we could use a single chisquare function and shift the distinction between the two variants to the determination of the inverse covariance matrix. This would mean using np.diag(np.diag(corr)) for uncorrelated fits or just skipping the whole inversion via Cholesky decomposition and using the inverse of the diagonal entries.
This would make future changes to the chisquared function easier since we do not have to maintain two versions of the chisquared functions anymore.

Also, as a next step, we could allow the user to provide a covariance matrix on their own. This would make the whole fit routine more general and would allow, e.g., to use Ledoit-Wolf shrinkage or similar techniques to regulate the covariance matrix, before the fit is performed (ensuring a correct interpretation in combination with the expected chisquare). Scans of fit ranges relying on the covariance matrix, either because one uses correlated fits or because the expected chisquare is computed, could be significantly sped up if the user could just pre-compute the covariance matrix and pass the (sub-)matrix to the fit routine.

As last point, for future reference, one could even allow the user to pass chisquared functions on their own, making the whole routine completely flexible. We don't have to integrate such feature now, but it would certainly easier to do so when there is just a single default routine.

@fjosw
Copy link
Owner Author

fjosw commented Mar 2, 2023

I think one of the reasons why we have a separate chisq function for uncorrelated fits is that the arithmetic intensity is lower when one can avoid the matrix multiplication ($N^2$ or $(N^2+N)/2$ vs $N$ multiplications). I never tested how big the impact of this actually is, it might be negligible.

I agree that it could be useful to provide the covariance matrix and that should also be easy to implement. I would propose to do that in a separate pull request though.

Providing a custom chisq function can be a bit tricky because it has to fulfill a few constraints for the correct error propagation but if it is useful we could still add it with adequate documentation.

But do the changes I made look okay to you?

@fjosw
Copy link
Owner Author

fjosw commented Mar 2, 2023

I did a quick benchmark of the two ways of computing the uncorrelated chisquare function for different problem sizes:

image

Copy link
Collaborator

@s-kuberski s-kuberski left a comment

Choose a reason for hiding this comment

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

Could we simplify the current structure even futher? I imagine a situation where we only have one if checking if we want to do a correlated fit or not. We could define the functions general_chisqfunc_corr, chisqfunc_residuals_corr (if needed ) and general_chisqfunc_nocorr, 'chisqfunc_residuals_nocorr and then just use something like

chisqfunc = general_chisqfunc_corr
chisqfunc_residuals = chisqfunc_residuals_corr

to, once and for all, fix these two functions for later use. This would remove the if clauses in lines 602 and 705 and streamline the code.

You could also just define the two functions in the if of line 583 and use an else for the uncorrelated case.

@s-kuberski
Copy link
Collaborator

I did a quick benchmark of the two ways of computing the uncorrelated chisquare function for different problem sizes:

image

Thanks for the timings! If the evaluation of the chisquare function is a relevant part of the computational effort (and this is most likely the case), we should really stick to having to distinct functions.

Co-authored-by: Simon Kuberski <simon.kuberski@uni-muenster.de>
@fjosw
Copy link
Owner Author

fjosw commented Mar 2, 2023

I tried to follow up on your suggestion @s-kuberski and simplified the logic in least_squares. I moved the functions with the suffix _residuals to the Levenberg-Marquardt block as they are only needed there. I also removed a few if clauses.

@fjosw fjosw mentioned this pull request Mar 3, 2023
@fjosw fjosw merged commit cb289a5 into develop Mar 3, 2023
@fjosw fjosw deleted the refactor/fits2 branch March 3, 2023 16:34
@fjosw fjosw mentioned this pull request Mar 6, 2023
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