support arrays as parameters#2149
Conversation
There was a problem hiding this comment.
Can params be None here? I thought with **params you would always get a dictionary, only that that dictionary may sometimes be empty.
There was a problem hiding this comment.
Good catch! This was an oversight on my part.
But it exposes a functionality which I have to question. There's a test which goes something like:
class MockModel(ParametricModel):
def __call__(self):
pass
p = Parameter(name="alpha", default_value=42, model=MockModel())
This test works in this PR because params is being tested for None.
But I am not sure what use case this attaching of a parameter to a fittable model supports.
Aren't the parameters of a model usually known?
@embray please clarify.
There was a problem hiding this comment.
This brings me back to wondering if MatrixRotation2D shouldn't be split into two separate classes. I mean, when it's used with the angle parameter then it's just the angle we're concerned with (as an external user) and not the rotation matrix. Meanwhile if using the matrix parameter it's not just limited to rotations--it can take any arbitrary linear transformation. So calling it MatrixRotation is a bit of a misnomer IMO.
As this additional option was only added to support the oddity in this special case, I wonder if maybe the special case itself should be reconsidered...
There was a problem hiding this comment.
I am fine with supporting only angle as a parameter and moving support for a more general transformation to an AffineTransformation class. The ultimate goal of this PR is to be able to support such class.
As for the optional attribute of Parameter, I don't see it as oddity. There are other models that can be defined in terms of alternative parameters. But I agree that other designs of Parameter may make this unnecessary.
There was a problem hiding this comment.
Yeah, I think that might be better. If that were done that there wouldn't currently be any models with parameters using this "optional" argument. I'm sure other uses for it could be found.
The name "optional" I think might be a bit misleading too. Currently one can make a parameter optional in the sense that it has a default value, and does not need to be explicitly given a value when creating an instance of that model. So now we have two senses in which a parameter is "optional". I think maybe "alternative" might be better, and it makes me wonder if there shouldn't be a way for a model to have one or more alternative sets of parameters...
…ByAngle2D and change it to take only one parameter - angle. Rotation by a matrix will be covered in a future AffineTransformation model
|
Any objections to merging this PR next week? |
|
In principle this seems good to me (I can't comment on the fine details of the code since I'm not really familiar with the internals at the moment). Just to make sure I understand, Also, this pull request does not currently change the documentation - should this behavior not be described in the docs? One last thing - this needs a changelog entry. |
|
Just to add to my previous comment, I wonder whether we want to allow and then have |
|
Now a few questions from a user's point of view: This error is not obvious to me. In this situation, I would expect something like 'amplitude should be a scalar value', and from your description above, it should not be assuming there are two models, correct? In the following example: maybe one could broadcast the scalar parameter values to Finally, if doing: users might wonder why in the following case: there are four values returned, not 8, so this needs to be made very clear in the documentation. |
|
I also don't like the name param_dim. If it were me, I would just have a boolean flag keyword that indicated that multiple parameter sets are present, and to take the length from the length of the the first parameter. modelsets, multimodels,... I'm not sure what name I would choose but this leads to less room for inconsistency (which is still possible if all parameters aren't consistent in length). |
|
@astrofrog creates two polynomials. It is true that the name is unintuitive but Also there was a suggestion to change For this example: I think we should avoid such nebulous model definitions. Is there a model which doesn't "know" what its parameters are? Such generic models are useful for testing but should not be allowed or it becomes very messy. The error message in the example is a generic message on the base class. In this case amplitude is a scalar but it's not in the general case. How about In the next example the current code broadcasts the parameters to There are some examples of using http://docs.astropy.org/en/stable/modeling/models.html http://docs.astropy.org/en/stable/modeling/parameters.html In the example with the input 2 models are created because One question that remains is what should Thanks for the comments @astrofrog . |
|
I'm still not very familiar with the models, but worry about increasing complexity. In particular, I felt @embray's arguments against
|
|
@nden - just regarding this message: for this example: I disagree with the error message. For the name of |
|
@astrofrog Yes, you are right. (I guess I'm partially thinking in terms of the current API which broadcasts parameters.) Let's decide first on whether to make Does this sound better? |
|
@mhvk I am fine with removing The question is does anyone see a use case for I will remove |
|
@nden - those error messages look good to me! Presumably this will only appear for models where amplitude is not expected to be e.g. a 2-element array, correct? |
|
👍 to removing |
|
|
|
@pllim - right, that was one of the issues I also brought up above when I suggested |
|
@nden - plan for removal of As for the model name, perhaps |
|
@astrofrog @pllim I didn't elaborate on Currently My two polynomial instances are The suggestion is to keep the functionality but change the API a bit. The new parameter will be Internally the model checks that the parameter values are consistent with having multiple parameter sets. The number of models is inferred from len(par) and if all parameters dont have the same length one of the errors discussed above is raised. But at the end the two models are the same and can be evaluated and fitted to multiple data sets. |
|
@mhvk |
|
@nden, your example of model dimension of Should I expect it to magically broadcast |
|
@pllim If you allow this, how do you distinguish a case where c1 is meant to be an array of two elements and c0 is meant to be a scalar? |
|
That's my point: Guessing can have undesired effects. |
|
@plim There's no guessing in this PR. Here are some use cases: The valid case with 2 models The invalid case which raises an error A valid case with 1 model Another valid case with 1 model |
|
Okay, @nden, as long as it is documented and I can look that up, that's fine. Personally, I would just like to tell |
|
@nden @pllim - I think the real decision to be made here is whether we only allow 1-d sets of models, or if we want to allow model grids like @pllim was suggesting. Take the following example: To me, this is ambiguous, and could mean either a 2x2 grid of models with scalar parameters: or two models with parameters with length 2: and what I was suggesting before is that we could distinguish these with: If we don't allow 'grids' of models with more than one dimension, then @nden's suggestion is fine. However, if we want to allow 2- or higher-dimensional 'grids' of models, then a simple |
|
Just to add, I'm not saying I definitely want multi-dimensional grids of models to be supported, as it might make things more complicated in terms of model evaluation, but I just want to make sure we at least discuss the possibility. |
|
I'd like to keep it simple and only support 1D sets (it's easy enough to flatten higher dimensions for the purposes of using models, and reshaping when done). Also, I'd rather not have implied broadcasting to make all the parameters consistent. I don't see much harm in making the user explicitly make each parameter have a matching number of values for multiple parameter sets. |
|
@perrygreenfield - that's fine by me - just wanted to make sure it was a conscious decision. The only thing I would say is that this should be made clear in the documentation. If we go down that road, then I think the argument should not be |
|
I still feel like the need for this argument could be eliminated entirely if a parameter has a required dimensionality. By default it would be scalar, but if we could define something like: then if an I remember at one point hearing an argument that any parameter should be able to accept an array of arbitrary size/shape as its value, but it's still not clear on how that should even be interpreted (since depending on the function it might not even make sense to accept an array for arbitrary parameter values). I agree with Perry that flat sets of parameter values are sufficient. |
|
Also,
Well, not automatically no, because we can't determine for an arbitrary model what parameter values are "valid". But when creating a model its parameters can be given a |
|
@nden - on the names: it just struck me that a better name than |
|
My question about this: why |
|
@embray On the topic of adding a shape to |
|
There's one case where the current code cannot catch a user input error: I see two options
I am tempted to go with the second option but it may be a bit dangerous because there may be a case where the @embray Any suggestions how to fix this? |
|
On the separation of The advantage of this is that |
It already can. Bound and unbound parameters are both handled by the same class currently and there's nothing gained from changing that. |
Perhaps in addition to shape, one could specify the number of dimensions (or just the number of dimensions--obviously in one specifies the shape then that fixes the number of dimensions as well). Or, if we did include an option to models like Though I still think there's a place for required shapes for parameter values. That way it's easy to ensure that a 2x2 array is always a 2x2 array, etc. |
<https://github.com/astropy/astropy/pull/2149/files#r10402422> that different classes of models had different mechanisms for storing their parameter values, creating a fair amount of unncessary complexity. This change attempts to smooth out some of the differences by merging the old ParametricModel class into the base Model class, so that all models more or less operate on the same basis. An added advantage to this is that it adds full support for parameter constraints on *all* models, which can be useful even outside the context of fitting. This standardizes on the way ParametricModel stored parameters, where all the parameter values are stored in a single flat array along with a simple _param_metrics data structure providing information how to extract individual parameter values from that array. As @nden points out this may not be the *best* way. In fact, it might be difficult to really make this work with parameters as Quantities. But this was the path of least resistance for the first iteration. For now I've kept around a class named ParametricModel and its subclasses ParametricModel1D and ParametricModel2D and the subclasses thereof--the only thing that really distinguishes them at the moment is the fact that they have the `fittable=True` flag. This does *not* fully resolve the issue addressed in astropy#2149, but it will make the implementation for astropy#2149 simpler. This also adds a few inefficiences, particularly in any models that have angular parameter values that need to be converted to radians for compuations. I have a separate branch running parallel to this one that will attempt to address that issue.
<https://github.com/astropy/astropy/pull/2149/files#r10402422> that different classes of models had different mechanisms for storing their parameter values, creating a fair amount of unncessary complexity. This change attempts to smooth out some of the differences by merging the old ParametricModel class into the base Model class, so that all models more or less operate on the same basis. An added advantage to this is that it adds full support for parameter constraints on *all* models, which can be useful even outside the context of fitting. This standardizes on the way ParametricModel stored parameters, where all the parameter values are stored in a single flat array along with a simple _param_metrics data structure providing information how to extract individual parameter values from that array. As @nden points out this may not be the *best* way. In fact, it might be difficult to really make this work with parameters as Quantities. But this was the path of least resistance for the first iteration. For now I've kept around a class named ParametricModel and its subclasses ParametricModel1D and ParametricModel2D and the subclasses thereof--the only thing that really distinguishes them at the moment is the fact that they have the `fittable=True` flag. This does *not* fully resolve the issue addressed in astropy#2149, but it will make the implementation for astropy#2149 simpler. This also adds a few inefficiences, particularly in any models that have angular parameter values that need to be converted to radians for compuations. I have a separate branch running parallel to this one that will attempt to address that issue.
|
Resolved by alternative in #2634. |
<https://github.com/astropy/astropy/pull/2149/files#r10402422> that different classes of models had different mechanisms for storing their parameter values, creating a fair amount of unncessary complexity. This change attempts to smooth out some of the differences by merging the old ParametricModel class into the base Model class, so that all models more or less operate on the same basis. An added advantage to this is that it adds full support for parameter constraints on *all* models, which can be useful even outside the context of fitting. This standardizes on the way ParametricModel stored parameters, where all the parameter values are stored in a single flat array along with a simple _param_metrics data structure providing information how to extract individual parameter values from that array. As @nden points out this may not be the *best* way. In fact, it might be difficult to really make this work with parameters as Quantities. But this was the path of least resistance for the first iteration. For now I've kept around a class named ParametricModel and its subclasses ParametricModel1D and ParametricModel2D and the subclasses thereof--the only thing that really distinguishes them at the moment is the fact that they have the `fittable=True` flag. This does *not* fully resolve the issue addressed in astropy#2149, but it will make the implementation for astropy#2149 simpler. This also adds a few inefficiences, particularly in any models that have angular parameter values that need to be converted to radians for compuations. I have a separate branch running parallel to this one that will attempt to address that issue.
This PR adds support for arrays as parameters to
Model(#2049) and restores the original functionality:Examples:
Create one model with 2 parameters: an array and a scalar. (param_dim=1 by default)
The equivalent model with multiple parameter set will be created only if param_dim is passed
to the model, no assumptions based on the size of the parameters.
This is different from the current behavior where the first example creates one model with
nparameter sets, where n is the size of the array andparam2is repeated n times.The internal changes are:
Parameterclass initializer takes a new boolean argumentoptional. It indicates that the parameter is not used internally by the model and exists only for the user API. For an example of its use seeMatrix2DRotation, whereangleis an optional parameter but internally the model works with the rotation matrix. This argument was added to avoid errors when validating parameters.param_metricsfromParametricModel._initialize_parametersinto a separate method. Also moved it fromParametricModeltoModelso that non-fittable models also have this attribute now. This allows to do parameter validation for all models based onparam_metricsand not usingParameter.shapeas is currently done.Parameter.shapesince it's unclear how it's defined when parameters are class variables, i.e. it persists between instances.custom_model_1dwas changed to require parameter values when initialized.Notes:
optionalargument was removed.Matrix2DRotationwas changedto work only with angles and was renamed to
RotateByAngle2D.