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!
— Functionbind!(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'sK_3
:
bind!(prob, 1 => :K_1, 2 => :K_3)
- Bind model 3's
K_2
parameter to model4's:L_1
and model 6'sa_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)
Only free parameters can be bound together.
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)
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])
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])