-
Notifications
You must be signed in to change notification settings - Fork 5
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
Improve Rainbow fitting #314
Conversation
…d method for getting parameters errors
for more information, see https://pre-commit.ci
CodSpeed Performance ReportMerging #314 will not alter performanceComparing Summary
|
Thank you, @karpov-sv! I'll look into code a bit later, here I'll try to address some of your general points.
I'm sorry that you've found code structure to be too abstract. The key idea in this library that user constructs feature extractor in advance and apply it to one, or many, light curves. This means that all the feature parameters must be passed in advance, they are currently handled as
I understand this point, but I against it. The library is supposed to provide the even interface over feature extractors, so they could be combined and abstracted from evaluation step. In this particular case I see no way how user would approach with boundaries individual for each light curve, not using the light curve data itself. If we can do better job estimating boundaries from light curve, it would be nice
All ideas you propose about Minuit sound very valid for me. |
# Unscale errors | ||
# FIXME: is there any better but generic way to unscale all relevant errors without shifting?.. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scaler
has undo_scale
which doesn't shift
light-curve-python/light-curve/light_curve/light_curve_py/features/rainbow/_scaler.py
Lines 47 to 48 in bc1039d
def undo_scale(self, x): | |
return x * self.scale |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but there is no way to use it without knowing the underlying data - _unscale_parameters
method is defined in subclasses, and there is no way to know which parameter was transformed using which scaler from the base class apart from using it. So there is no clean way to apply just undo_scale
method if the subclass uses undo_shift_scale
(or even undo_shift_scale_band
). Well, of course apart from defining new virtual method like _unscale_parameters_errors
for the subclasses to implement - but that's probably too much?..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see now what did you mean, it is about parameter errors, not about flux errors. Sorry for the confusion
Yes, I understand your design choices and I agree with them in general. So I will modify the PR accordingly - as soon as I understand how to do it better. One more point of consideration here, related to immutability - I am no expert in Minuit, but it seems to me that What is important is to have some sort of error reporting - that's why I need Then, if error handling will be implemented this way - I may remove all mutable fields from the PR, and move the rest of features I need to the arguments of specific methods. (E.g. I might try to add some support for initial parameters, as for some tasks you have some educated guess about them, like rough peak time, or rough rise/fall timescales - but I do not yet know whether it is possible to do it in any generic way due to all this scaling/unscaling of parameters inside). |
Let's just let user set it
Raising exception is fine. If user set
We do scaling/unscaling to make all parameters to be the same order of magnitude. We consider that user could use crazy units for inputs, let's say years for time and W/cm^2/Hz for fluxes, so it would be better to normalize everything before using the optimizer. This normalization is specific for this feature, so we can reimplement it in any other way, or even drop it if we believe that optimizer could handle these different orders of magnitudes properly. |
Ok, so here is new version, with all side effects removed (apart from single optional argument for Minuit parameters ( I did not yet come with any generic enough way to pass initial parameters to the fitter (as they should be normalized alongside with the data, which cannot be done in base class as the underlying model is unknown, and there is no method for that in subclasses either), so I will give up on that for now. Thus, probably the code is ready for your review now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay and thank you!
Do you have time to add some tests for the code? It is fine to say "no", I could make it myself a bit later
light-curve/light_curve/light_curve_py/features/rainbow/_scaler.py
Outdated
Show resolved
Hide resolved
light-curve/light_curve/light_curve_py/features/rainbow/symmetric.py
Outdated
Show resolved
Hide resolved
light-curve/light_curve/light_curve_py/features/rainbow/symmetric.py
Outdated
Show resolved
Hide resolved
for more information, see https://pre-commit.ci
Ok, I added some tests in a similar manner to existing Rainbow ones. Also incorporated the fixes you requested. |
Head branch was pushed to by a user without write access
The old test for standard |
It is now published as a part of |
* Symmetric Bazin, with toggleable temperature evolution and rising part only * Utility method for getting bolometric peak position added, and couple of typos fixed * Expose Minuit minimization result, and make it more verbose. Also, add method for getting parameters errors * Correctly unscale parameter errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Make the fitter pure, removing side-effects introduced earlier * Changes from PR review * Tests added * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove unused variable in the test * "Fix" standard Rainbow test that stopped to converge with minuit.strategy=2 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This PR adds new sub-class for Rainbow,
RainbowSymmetricFit
that implements symmetric form of Bazin function for bolometric term, and slightly modified sigmoid function for temperature term, allowing for both rising and falling temperatures:These are better suited for constrained minimization, as they do not require complex constraints combining several parameters as in current implementation (not actually implemented there, leading to nonsensical results sometimes).
New class also has options for fitting fixed temperature models, as well as rising only bolometric function (effectively just a sigmoid). Both may be toggled independently.
In principle, this implementation supersedes both
basin.py
andrising.py
, and may replace them completely.PR also modifies the base class by exposing some internals from Minuit - new field
valid
reflects whether the minimization succeeded or not, and new fieldminuit
allows directly accessing Minuit instance, e.g. to see its diagnostic output.I took a liberty of also increasing the verbosity level of Minuit itself (
print_level=1
) so that it prints some warnings during the minimization when it is problematic. Also, number of iterations is increased (ncall=10000
,iterate=10
), and default minimization strategy is changed to slightly more stable, but probably slower one (strategy=2
).It should be, of course, made optional, but I found no easy way to pass them as additional keyword parameters for the
_eval
method (too many levels of abstraction above it).Related to the last point - as there is no easy way to add extra keywords to
_eval
method, I had to implement additional entry point for fitting with also getting back the parameter errors (instead of just making it an option). The method isfit_and_get_errors
, with identical signature to__call__
, and it returns the list containing arrays of parameters and their errors.In general, the minimization on real data is not very stable, and strongly depends on initial parameters (as it is local minimizer only). I adjusted the heuristics for initial parameters to be slightly better, but it would be desirable to be able to also directly pass them during the feature evaluation. It comes down, again, to passing extra parameters to
__call__
(as well as to proper normalization of the values there, but that's easily doable).Tests are not (yet) implemented for new class, as it will probably require some changes anyway.