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[, pairs...])
Bind parameters together within a FittingProblem
. Parameters bound together will be mandated to have same value during the fit.
The binding must be specified in double or triple selector, which follow the format:
pair := (model_index, :component_name, :parameter_symbol)
:= (model_index, :parameter_symbol)
The model_index
is the index of the model in a multi-fit problem, i.e. 1
, 2
, and so on.
The component name is a CompositeModel
model name, e.g. :a1
, or :c3
. This can be omitted if the model is not a CompositeModel
.
The paramter symbol is a symbol representing the field of the parameter in the model. That is, :K
or :log10Flux
.
Bindings are specified using a chain of pairs (root) => (target) [=> (target)]
. The root parameter is kept as is, and all subsequent paramters are bound to the root. Multiple chains of pairs may be specified in a single call to bind!
, or, alternatively, multiple bindings may be specified with successive calls to bind!
.
Bindings can be inspected with details
.
See also bindall!
.
Examples
Bind model 1's
K
parameter to model 2's second additive model'sK
:bind!(prob, (1, :K) => (2, :a2, :K))
Bind model 3's
:a2.K
parameter to model4's:m3.L
and model 6's:a1.a
:bind!(prob, (3, :a2, :K) => (4, :m3, :K) => (6, :a1, :a))
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
bindall!(prob, :a)
# Bind the normalisation of powerlaws in the 2nd model:
bind!(prob, (2, :a1, :K) => (2, :a2, :K))
# To inspect the overall bindings.
details(prob)
Only free parameters can be bound together.
SpectralFitting.bindall!
— Functionbindall!(prob::FittingProblem, item[, item...])
Bind a common parameter across all models. The item is used to select the parameter to bind, and may either be a single symbol, or a model-symbol double.
Examples
# bind parameter `a` in all models
bindall!(prob, :a)
# bind parameter `K` in component `a3` in all models
bindall!(prob, (:a3, :K))
# multiple simultaneously
bindall!(prob, :E, (:a2, :K))
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)
bindall!(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 -> Bound to Model 1 => nothing -> 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])