FittingData and ModelFunctions objects
For the construction of objective functions the data needs to be summarized in a FittingData object and information about the model needs to be summarized in a ModelFunctions object.
The FittingData struct
The FittingData struct has the following general constructor:
data = FittingData(X,Y,ΔY, distributions = distribution_functions)The resulting object has the following fields:
data.independent == X
data.dependent == Y
data.errors == ΔY
data.distributions == distributions_functionsIf no measurement errors ΔY are available, the shortened constructor can be used:
FittingData(X,Y, distributions = distribution_functions)In this case, the default errors ones(length(X)) are used (leading e.g. to the standard least squares objective function: Background: LSQ)
The optional distributions keyword is used to specify the likelihood distributions (see Background: Posterior probability). If distributions are not specified explicitly, the constructor defaults to normal distributions.
The distributions can be specified as an array of functions, or a single function (the same distribution for all data points) with the signature (y,m,Δy), where
yis the measured dependent variableΔyis the corresponding errormare the values that the model function returns for a given parameter
The ModelFunctions struct
ModelFunctions objects have the following general constructor:
model = ModelFunctions(model_function, partial_derivatives = [derivative_functions...])The resulting object has the following fields:
model.model == model_function
model.partials == [derivative_functions...]The model function (and the partial derivatives) must have the signature (x,λ), where x is the independent variable and λ is the parameter (array).
The keyword partials is optional, but is required for analytical partial derivatives and analytical gradient functions. The partial derivatives w.r.t. the parameter are defined as array of functions.
\[\text{m}(x,\lambda) = \lambda_1 x + \lambda_2 \ ,\quad \frac{\partial \text{m}(x,\lambda)}{\partial \lambda_1} = x\ , \quad \frac{\partial \text{m}(x,\lambda)}{\partial \lambda_1} = 1\]
ModelFunctions((x,λ)-> λ[1]*x + λ[2], partials = [(x,λ)-> x, (x,λ)-> 1])Additional remarks
When a FittingData/ModelFunctions object is created, some rudimentary consistency checks are done, e.g. that all arrays have the same lengths.
julia> FittingData([1,2,3],[1,2],[1,1,1])ERROR: DimensionMismatch: Arrays for independent data and dependent data do not have the same length.
Since the objects are mutable, the same consistency checks are repeated before objective functions are created. However, it can be useful to make a comprehensive consistency check. For this, a representative parameter (array) is needed:
data = FittingData(X,Y)
model = ModelFunctions((x,λ)-> λ*x)
consistency_check(data,model,1)If everything works, nothing is returned, i.e. nothing happens. However, in case of a problem, an error is thrown:
julia> consistency_check(data,model,"1")ERROR: "Could not evaluate the model function."
Both FittingData objects and ModelFunctions objects are mutable for convenience, as they are not performance relevant (only their fields are). However, when objective functions are created, the object fields are copied and enclosed in the objective function, to avoid accidental mutation.
I.e. once an objective function is created, it does not change, even if the FittingData/ModelFunctions objects are changed. To apply changes, a new objective function needs to be created.