-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Simplified implementing new models #980 #1203
Conversation
@adonath - thanks for the PR! I will review this carefully tomorrow, but in the mean time, I notice there are two classes that are currently commented out - if they get replaced by other code then they should just be removed rather than commented (or if they are commented by mistake, they should be uncommented). Once this is done, maybe @nden can review this too? |
Yes, I'll review it soon. |
@adonath - thanks! one more request - can you move |
@adonath - thanks, and one more request - I think this will need to be rebased (you can see it says 'we can't automatically merge this pull request above' - do you need instructions for how to do this? |
Hi Tom, thanks for the hints! Yes, I could need intructions how to do a Best Axel Am Di 25 Jun 2013 10:34:43 CEST schrieb Thomas Robitaille:
|
@adonath - here are instructions: http://docs.astropy.org/en/stable/development/workflow/development_workflow.html#rebase-on-trunk Basically:
However, before you start, make a backup of the folder on your computer, in case you mess up! If you have issues, we can do it over chat or hangout later - just let me know. |
@adonath - I noticed you managed to rebase the branch - however, it looks like the conflicts were not dealt with cleanly (in part due to the incomplete documentation on rebasing). The idea is that if there is a commit that conflicts, then the conflicted files will contain something like
but before you Do you still have a backup of the code as it was before the rebase so that we can try it again? (if you want we can do it interactively on chat or hangout). Another alternative if rebasing becomes too much of a pain is to simply start from a new clean branch from the upstream master, and copy over the files are you want them in their final state in this PR, then just add them in a single commit and force push to the pull request. You will lose the commit history if you do this, but on the plus side the history will be cleaner and not have residual conflict markers in it. Does this make sense? |
By the way, it's always hard to manage a rebase the first time, so no worries! It takes a few times to get used to, especially when there are conflicts :) |
I've created a new branch from the current trunk and copied my changes, Am Di 25 Jun 2013 14:23:56 CEST schrieb Thomas Robitaille:
|
@adonath - yes, this looks good and ready for review! Next time you need to do a rebase, let me know and we can do it together over a Hangout (it's good experience to have) |
|
||
def __init__(self, param_dict, **cons): | ||
# Get parameter dimension | ||
param_dim = np.size(param_dict[self.param_names[0]]) |
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.
I have to admit I don't quite understand the concept of some of the parameters having multiple (as opposed to scalar) values - for example amplitude
in the Gaussian. What is the intent of this? (this is more of a question for @nden). If it is the case that parameters can have array values, what are the rules? Should all parameters have array values? If so, maybe we should sanity-check that here?
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.
As far as I understood, the concept of parameters as arrays is, that the models can be evaluated with several parameter sets. So parameters can be scalar, lists or numpy arrays. The dimension of the parameter is stored in "param_dim". I have made a short check and np.size() should be save, it gives the appropriate value for scalars as well as lists and arrays.
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.
I will review the PR later today but just to answer this question now (I've been asked this many times).
The intent of multiple parameters is to be able to simultaneously evaluate (or fit in some cases) the same type of model on multiple data sets. A good example is a cube with up-the-ramp IR data and performing a linearity correction. Fitting of polynomials with the LinearLSQFitter
can be performed simultaneously and this is a huge improvement over looping through all pixels.
The current rules are:
- linear and non-linear models can be evaluated simultaneously
- linear models can be fit simultaneously if the the
LinearLSQFitter
is used - because of the nature of non-linear fitting algorithms non-linear models can be fit currently only one at a time
(But may be this can be changed in the future by using multiple processes?)
This is in contrast of having a parameter of type array. Parameters of type array are supported (although there's no model like this, only a test case in test_models.py.) If a parameter is an array and param_dim
is > 1, the parameter is going to be a list of arrays.
However, the parameters
attribute of a model is always a flat list. It's real purpose is to communicate with the fitter.
Does this make sense?
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 - so in cases where one parameter is an array, should all of them be arrays? If not, and it's possible for some to be scalars and some arrays, then you can't just assume the first is going to give you the dimension - you would need to loop through all the parameters to check that all those that are arrays/lists have the same dimension and save that as the dimension.
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.
It is possible (in theory) to have mixed parameters (one array and one scalar). But say param_dim
=2, then each parameter is a list, the first parameter is a list of 2 arrays and the second a list of 2 scalars. So checking the first one is sufficient.
If the user makes a mistake and gives 2 arrays but 1 scalar, I think this will be caught by the Parameter
class but I have to double check.
This does look like it reduces the amount of duplicated code when writing new models, so I think this is ready for @nden to review. We should also ensure that the Travis tests are passing. |
@@ -47,6 +47,7 @@ | |||
from . import constraints | |||
from .utils import InputParameterError | |||
|
|||
|
|||
__all__ = ['Model', 'ParametricModel', 'PCompositeModel', 'SCompositeModel', | |||
'LabeledInput', '_convert_input', '_convert_output'] | |||
|
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.
Can you add Parametric1DModel
to __all__
.
@adonath The documentation build is failing because some of the examples use |
@nden Thanks for the review and annotations. I will fix the documentation and the other points you have mentioned. So we keep the evaluataion of the model with "self.param_sets"? |
@adonath Yeah we keep The |
@nden That was what I meant. Thanks for the quick answer! |
I've fixed the discussed issues and added new tests. The Travis builds passed. So is it ready to merge? |
if self._model.deriv is None: | ||
self.dfunc = None | ||
else: | ||
self.dfunc = self._wrap_deriv |
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.
@adonath I am wondering what the reason for removing this section is. I see it was moved to the call function of NonLinearLSQFitter
but it was meant to be applicable to all fitters. The SLSQPFitter
can also be passed an analytical derivative and supposedly other fitters in the future should be able to take one too. Isn't moving this section of the code to the NonLinearLSQFitter
only going to make this impossible?
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.
You're right of course, so far it works only for NonLinearLSQFitter. I had two ideas in mind:
(1) This option was only available for Gaussian1DModel, but I thought it could be reasonable to have it for
all models, thats why I moved this option to the Fitter class.
(2) When you're fitting data, you may want to check if the result changes, whether you take the analytical
or estimated derivative (especially when you're working in an interactive shell). Having this option in the call function, you don't have to setup a new Fitter instance to change the "derivative mode". That was the only reason for putting it in the call function.
Is this reasonable? Thanks for checking the changes!
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.
OK, that sounds good. Changing a parameter is better. Were you able to get this working with the other fitter?
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.
I've had a look on the code again and it seems that currently only the NonLinearLSQFitter uses the analytical
derivative. All the other fitters don't, except for the LinearLSQFitter to set up the coefficient matrix. In principle the other Scipy functions take a derivative function, but currently this seems not to be implemented for the JointFitter and SLSQPFitter: No analytical derivative is passed to the minimizers and also the wrappers for the derivatives, that manage the constraints etc. are missing. Is this correct?
At first sight the handling of constraints seemed quite complicated, so I didn't want to work on this...
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.
@adonath I remembered I had a problem passing a derivative to slsqp and wondered if you've tried it. But that's fine, I'll work on it. I am also working on a largish PR to simplify handling of constraints. Thanks for this work.
Either fwhm or stddev must be specified | ||
fwhm : float | ||
Full width at half maximum | ||
Either fwhm or stddev must be specified | ||
jacobian_func : callable, 'estimated' or None | ||
if callable - a function to compute the Jacobian of | ||
func with derivatives across the rows. | ||
if 'estimated' - the Jacobian will be estimated | ||
if None - if the model has a deriv method, it will be used, | ||
if not the Jacobian will be estimated. |
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.
Remove jacobian_func
from the Parameters
section
@astrofrog This looks good to me, ready to merge? |
Simplified implementing new models #980
@adonath - for any other changes or adding tests, just open a new PR, but I wanted to make sure this was merged so that the main code doesn't conflict with any other pull requests in progress. |
Hi everyone,
during the last week I've worked on simplifying the implementation of new models.
So far this only works with 1D models, but I'd like to get some feedback, before I continue working on this.
At first I've introduced a new Parametric1DModel class, which is the base class for all
parametric 1D models. It mainly implements the appropriate call() function and does some "bookkeeping" in the constructor, to check whether analytical derivatives are implemented etc. The model parameters are currently set automatically by getting the
arguments from the model's constructor. I wanted a way to avoid setting all model parameters "by hand", as the Parameter class may still change.
Furthermore I've made a few changes in the models deriv() function. So far it used a dictionary and list comprehension for the derivatives, which I changed to an ordinary list. This which should be faster. Scipy's lsqr fitter has an option "col_deriv" which can be set, when the derivatives are given across the columns. So I removed the transpose step in deriv(). Finally I've changed the function call of deriv(), so that no dummy variables are necessary. It is only important that the order of parameters is consistent in the model definition. I hope that I've not overlooked something, which made this things necessary. But so far all tests passed.
I've implemented a few simple new models. Among a "Custom1DModel" which can be used to create new models "on the fly" just from a function. This may be useful when
working with astropy interactively.
Since this is my first "real" pull request, I would be happy for any kind of feedback! Thanks in advance!