Parameters

Parameter binding

When performing a fit, it is desireable to bind certain parameters together. This ensures that they will have the same value; for example, if you were fitting two simultaneous datasets with two PowerLaw models, you may want to have different normalisations of the model components, but enforce the power law index to be the same. To achieve this, SpectralFitting has the bind! function that applies to your FittingProblem.

SpectralFitting.bind!Function
bind!(prob::FittingProblem, pairs::Pair{Int,Symbol}...)
bind!(prob::FittingProblem, symbols::Symbol...)

Bind parameters together within a FittingProblem. Parameters bound together will be mandated to have exact same value during the fit.

The binding may either be a single symbol that is present in all models in the fitting problem, or a series of pairs Int => Symbol which index the specific model and parameters to bind together. All bindings specified in a single call to bind! will be bound together. Multiple bindings are possible with repeated call to bind!.

  • Bind model 1's K_1 parameter to model 2's K_3:
bind!(prob, 1 => :K_1, 2 => :K_3)
  • Bind model 3's K_2 parameter to model4's :L_1 and model 6's a_3:
bind!(prob, 3 => :K_2, 4 => :L_1, 6 => :a_3)
  • Bind the K_1 parameter across all the models:
bind!(prob, :K_1)

Examples

Consider the following two models

model1 = PhotoelectricAbsorption() * (BlackBody() + PowerLaw())
model2 = PhotoelectricAbsorption() * (PowerLaw() + PowerLaw())

prob = FittingProblem(model1 => data1, model2 => data2)

# bind the power law indices in the two models
bind!(prob, :a_1)

# bind the normalisation of powerlaws in the 2nd model:
bind!(prob, 2 => :K_1, 2 => :K_2)
Note

Only free parameters can be bound together.

source
Note

Bindings are treated not as specific to the model but specific to the FittingProblem. This is because you may want to use the same model for multiple different datasets, and have slightly different binding requirements for each one (e.g. depending on the instruments you are using). If you do need the same binding applied to two different problems, you can do that with

append!(prob1.bindings, prob2.bindings)

Caution however, this will only make sense if you are using precisely the same model in both problems.

Let's try it out. We'll generate some arbitrary powerlaw spectra with different normalisations and fit them simultaneously.

using SpectralFitting, Plots

energy = collect(range(0.1, 10.0, 100))

# two different models with different normalisations
model1 = PowerLaw(K = FitParam(100.0), a = FitParam(1.2))
model2 = PowerLaw(K = FitParam(300.0), a = FitParam(1.22))

data1 = simulate(energy, model1, var = 1e-3)
data2 = simulate(energy, model2, var = 1e-3)

plot(data1, xscale = :log10, yscale = :log10)
plot!(data2, xscale = :log10, yscale = :log10)
Example block output

Now we want to fit a single powerlaw model to both of these spectra simultaneously, but with the powerlaw index fixed to be the same in both models.

model = PowerLaw()
prob = FittingProblem(model => data1, model => data2)

bind!(prob, :a)
prob
┌ FittingProblem:
│   . Models     : 2
│   . Datasets   : 2
│   Parameter Summary:
│   . Total      : 4
│   . Frozen     : 0
│   . Bound      : 1
│   . Free       : 3
└ 

We can get a better look at our model configuration by using the details method:

details(prob)
┌ Models:
│   Model 1: PowerLaw
│        K ->  1 ± 0.1  ∈ [ 0, Inf ]   FREE
│        a ->  2 ± 0.2  ∈ [ 0, Inf ]   FREE
│
│   Model 2: PowerLaw
│        K ->  1 ± 0.1  ∈ [ 0, Inf ]   FREE
│        a ->  2 ± 0.2  ∈ [ 0, Inf ]   ~Model 1 a

In this printout we see that the a parameter of Model 2 is bound to the a parameter of Model 1.

result = fit(prob, LevenbergMarquadt())

plot(data1, xscale = :log10, yscale = :log10)
plot!(data2, xscale = :log10, yscale = :log10)
plot!(result[1])
plot!(result[2])
Example block output

Note that these fits are not perfect, because the underlying data have subtly different power law indices, but our fit is required to enforce the models to have the same value. If we release this requirement, the fit will be better, but the models will be entirely independent.

prob = FittingProblem(model => data1, model => data2)

result = SpectralFitting.fit(prob, LevenbergMarquadt())

plot(data1, xscale = :log10, yscale = :log10)
plot!(data2, xscale = :log10, yscale = :log10)
plot!(result[1])
plot!(result[2])
Example block output