Tying parameters in astropy.modeling
Asked Answered
S

1

6

I am trying to use the Model.tied (or Parameter.tied) attribute in astropy.modelling, but can't seem to figure out how it works. For example, let's say I wanted to create a compound model with two parameters: flux_0 and flux_1. However, I only want flux_0 to be used in fitting: flux_1 should always carry the value 1 - flux_0. (Eventually, I need to extend this functionality so that flux_0 + flux_1 + ... + flux_n = 1.)

I define a model class and a "callable" for the tied attribute like so:

>>> from astropy.modeling import Fittable1DModel, Parameter
>>>
>>> class MyModel(Fittable1DModel):
...     flux = Parameter()
...     @staticmethod
...     def evaluate(x, flux):
...         return flux
...
>>> def tie_fluxes(model):
...     flux_1 = 1 - model.flux_0
...     return flux_1
...
>>> TwoModel = MyModel + MyModel
>>>
>>> TwoModel
<class '__main__.CompoundModel0'>
Name: CompoundModel0
Inputs: ('x',)
Outputs: ('y',)
Fittable parameters: ('flux_0', 'flux_1')
Expression: [0] + [1]
Components:
    [0]: <class '__main__.MyModel'>
    Name: MyModel
    Inputs: ('x',)
    Outputs: ('y',)
    Fittable parameters: ('flux',)

    [1]: <class '__main__.MyModel'>
    Name: MyModel
    Inputs: ('x',)
    Outputs: ('y',)
    Fittable parameters: ('flux',)

Then I inspect the tied attribute. My understanding is that this should be a dictionary (see footnote), but it is not:

>>> TwoModel.tied
<property object at 0x109523958>
>>>
>>> TwoModel.tied['flux_1'] = tie_fluxes
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'property' object does not support item assignment

If I try to set it as a dictionary, it does not update the appropriate Parameter:

>>> TwoModel.tied = {'flux_1': tie_fluxes}
>>>
>>> TwoModel.flux_1.tied
False

But when I try creating an object right off the bat instead of a compound model class (this is not what I want to do in the end), the object's tied attribute is a dictionary. Unfortunately, setting this dictionary still does not produce the desired effect:

>>> TwoSetModel = MyModel(0.2) + MyModel(0.3)
>>>
>>> TwoSetModel
<CompoundModel1(flux_0=0.2, flux_1=0.3)>
>>>
>>> TwoSetModel.tied
{'flux_1': False, 'flux_0': False}
>>>
>>> TwoSetModel.tied['flux_1'] = tie_fluxes
>>>
>>> TwoSetModel
<CompoundModel1(flux_0=0.2, flux_1=0.3)>
>>>
>>> TwoSetModel.flux_1.tied
<function tie_fluxes at 0x102987730>

So in this example, the tied attribute does hold the correct function, but the parameter's value does not update accordingly.

What am I doing wrong here? Am I completely misunderstanding the tied attribute?

(I am using Python 3.5.2 with Astropy 1.3.3 in the above examples)


Footnote:

Running help(TwoModel), I get the following information:

⁝
 |  tied : dict, optional
 |      Dictionary ``{parameter_name: callable}`` of parameters which are
 |      linked to some other parameter. The dictionary values are callables
 |      providing the linking relationship.
 |
 |      Alternatively the `~astropy.modeling.Parameter.tied` property of a
 |      parameter may be used to set the ``tied`` constraint on individual
 |      parameters.
⁝
 |  Examples
 |  --------
 |  >>> from astropy.modeling import models
 |  >>> def tie_center(model):
 |  ...         mean = 50 * model.stddev
 |  ...         return mean
 |  >>> tied_parameters = {'mean': tie_center}
 |
 |  Specify that ``'mean'`` is a tied parameter in one of two ways:
 |
 |  >>> g1 = models.Gaussian1D(amplitude=10, mean=5, stddev=.3,
 |  ...                        tied=tied_parameters)
 |
 |  or
 |
 |  >>> g1 = models.Gaussian1D(amplitude=10, mean=5, stddev=.3)
 |  >>> g1.mean.tied
 |  False
 |  >>> g1.mean.tied = tie_center
 |  >>> g1.mean.tied
 |  <function tie_center at 0x...>
⁝
Shearin answered 4/8, 2017 at 19:7 Comment(3)
Good question--I wrote most of this code so I should be able to answer your question. I'm about to head out for the day but I'll try to get back ASAP. IIRC dealing with constraints with compound models can still be a bit tricky, unfortunately.Cymene
One thing I can tell you now--you appear to be falling into a common misconception that model constraints are applied when manually updating the values of parameters. This is not the case. Constraints are only used by fitters when fitting the models, to set constraints on the parameters. Outside of fitting constraints are not enforced. I've considered changing this before, so that it works the way you seem to expect. There was an issue open for that at one point but I can't seem to find it now...Cymene
I think github.com/astropy/astropy/issues/2265 makes some reference to this.Cymene
E
0

The example below is similar to the one given in the astropy documentation.

Compound Model=Sum of two 1D gaussian functions.

Constrain: mean_1=2*mean_0

import numpy as np
import matplotlib.pyplot as plt
from astropy.modeling import models, fitting


def tie_center(model):
    mean = 2* model.mean_0
    return mean

tied_parameters = {'mean_1': tie_center}
np.random.seed(42)
g1 = models.Gaussian1D(2, 0.4, 0.3)
g2 = models.Gaussian1D(2.5, 0.2, 0.2)
TwoGaussians = (models.Gaussian1D + 
models.Gaussian1D).rename('TwoGaussians')
x = np.linspace(-1, 1, 200)
y = g1(x) + g2(x) + np.random.normal(0., 0.2, x.shape)

gg_init = TwoGaussians(amplitude_0=1.4, mean_0=1.2, stddev_0=0.1,\
amplitude_1=1.0,stddev_1=0.2, tied=tied_parameters)
fitter = fitting.SLSQPLSQFitter()
gg_fit = fitter(gg_init, x, y)


plt.figure(figsize=(8,5))
plt.plot(x, y, 'ko')
plt.plot(x, gg_fit(x))
plt.xlabel('Position')
plt.ylabel('Flux')
plt.show()
print(gg_fit.mean_0,gg_fit.mean_1)

Tying parameters in astropy when Compund_model= sum of three 1D gaussian functions Constrain: Sum of all three means should always be equal to one.

def tie_center(model):
    mean = 1-(model.mean_0+ model.mean_1)
    return mean
tied_parameters = {'mean_2': tie_center}

np.random.seed(42)
g1 = models.Gaussian1D(2, 0.4, 0.3)
g2 = models.Gaussian1D(2.5, 0.2, 0.2)
g3 = models.Gaussian1D(1.5, 0.4, 0.1)
ThreeGaussians = (models.Gaussian1D + models.Gaussian1D + 
models.Gaussian1D).rename('ThreeGaussians')
x = np.linspace(-1, 1, 200)
y = g1(x) + g2(x) + g3(x) + np.random.normal(0., 0.2, x.shape)

gg_init = ThreeGaussians(amplitude_0=1.4, mean_0=0.3, stddev_0=0.1, 
amplitude_1=1.0, mean_1=0.3,stddev_1=0.2, \
amplitude_2=1.5,stddev_2=0.1,tied=tied_parameters)
fitter = fitting.SLSQPLSQFitter()
gg_fit = fitter(gg_init, x, y)
plt.figure(figsize=(8,5))
plt.plot(x, y, 'ko')
plt.plot(x, gg_fit(x))
plt.xlabel('Position')
plt.ylabel('Flux')
plt.show()
print(gg_fit.mean_0,gg_fit.mean_1, gg_fit.mean_2)
Elum answered 19/9, 2017 at 3:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.