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[, 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's K:

    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)
Note

Only free parameters can be bound together.

source
SpectralFitting.bindall!Function
bindall!(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))
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)

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])
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