It appears as though NonlinearModelFit
/FittedModel
are unable to compute the required gradients themselves, so you'll have to update the definition of MultiNonlinearModelFit
:
Options[MultiNonlinearModelFit] = {AccuracyGoal->Automatic,ConfidenceLevel->19/20,EvaluationMonitor->None,Gradient->Automatic,MaxIterations->Automatic,
Method->Automatic,PrecisionGoal->Automatic,StepMonitor->None,Tolerance->Automatic,VarianceEstimatorFunction->Automatic,
Weights->Automatic, WorkingPrecision->Automatic, "DatasetIndexSymbol"->\[FormalN]
}; (* \[Equal] Join[Options[NonlinearModelFit], {"DatasetIndexSymbol" -> \[FormalN]}] *)
MultiNonlinearModelFit[datasets_, form_, fitParams_, independents : Except[_List], opts : OptionsPattern[]] :=
MultiNonlinearModelFit[datasets, form, fitParams, {independents}, opts];
MultiNonlinearModelFit[datasets_, form : Except[_?AssociationQ], fitParams_, independents_, opts : OptionsPattern[]] :=
MultiNonlinearModelFit[datasets, <|"Expressions" -> form, "Constraints" -> True|>, fitParams, independents, opts];
MultiNonlinearModelFit[
datasets : {__?(MatrixQ[#1, NumericQ] &)},
assoc : KeyValuePattern[{
"Expressions" -> expressions_,
"Constraints" -> constraints_
}] /; AssociationQ[assoc],
fitParams_List,
independents_List,
opts : OptionsPattern[]
] := Module[{
fitfun, weights,
numSets = Length[datasets],
precision = Precision @ datasets,
augmentedData,
indexSymbol = OptionValue["DatasetIndexSymbol"],
grad
},
augmentedData = Join @@ MapIndexed[
Join[ConstantArray[N[#2, precision], Length[#1]], #1, 2]&,
datasets
];
fitfun = With[{
conditions = Flatten @ Map[
{#, Indexed[expressions, #]}&,
Range[numSets]
]
},
Switch @@ Prepend[conditions, Round[indexSymbol]]
];
grad=D[fitfun, {Replace[fitParams, {v_, ___} :> v , 1]}];
weights = Replace[
OptionValue[Weights],
{
(list_List)?(VectorQ[#1, NumericQ]& ) /; Length[list] === numSets :>
Join @@ MapThread[ConstantArray, {list, Length /@ datasets}],
list : {__?(VectorQ[#1, NumericQ] & )} /; Length /@ list === Length /@ datasets :>
Join @@ list,
"InverseLengthWeights" :> Join @@ Map[
ConstantArray[N[1 / #1, precision], #1]&,
Length /@ datasets
]
}
];
NonlinearModelFit[
augmentedData,
If[TrueQ[constraints], fitfun, {fitfun, constraints}],
fitParams,
Flatten[{indexSymbol, independents}],
Weights -> weights,
Sequence @@ FilterRules[{opts}, Options[NonlinearModelFit]],
Gradient->grad
]
];
This is copied almost straight from the definition notebook of MultiNonlinearModelFit
(available via ResourceFunction["MultiNonlinearModelFit", "DefinitionNotebook"]
, except that I manually supply the gradient of the function:
I compute it as (thanks @SjoerdSmit for the significant simplification!)
grad=D[fitfun, {Replace[fitParams, {v_, ___} :> v , 1]}];
This is effectively computing the gradients of each fit function separately and combining them using the same Switch[Round[...], ...]
strategy that MultiNonlinearModelFit
is using for the fitting itself.
The gradient is then supplied to NonlinearModelFit
in the last expression, which means we can now compute the prediction bands:
fit["MeanPredictionBands"]
We can now insert a value for the formal index to select the bands for one of the functions:
data1 = {#, 0.05 E^(0.075 #) + 0.005 RandomReal[]} & /@ Range[10];
data2 = {#, 0.075 E^(0.075 #)} & /@ Range[10];
model = ParametricNDSolveValue[{x'[t] == u x[t], x[0] == x0},
x, {t, 0, 100}, {u, x0}];
fit = MultiNonlinearModelFit[{data1, data2}, {model[u, x0][t],
model[u, x02][t]}, {u, x0, x02}, {t}];
Show[Plot[
fit["MeanPredictionBands"] /. \[FormalN] -> 1 // Evaluate, {t, 0,
10}, PlotStyle -> Red, Filling -> 1 -> {2}], ListPlot[data1]]
(Note that I have added some noise to data1
to make the bands visible)
MultiNonlinearModelFit
only allows (at least the last time I looked) a common error variance. While estimates of parameters might not be affected so much, if the error variance of the different models differ, then the prediction bands can be greater affected. Your example above has no error term for either model which doesn't make for a very realistic proxy for your real data. $\endgroup$MultiNonlinearModelFit
allows for different weights to be applied to each individual data point across the different datasets. You need to specify them asWeights -> {{w11, w12, ...}, {w21, w22, ...}, ...}
. $\endgroup$MultiNonlinearModelFit
is great.) Using theWeight
option will allow for independent errors with different variances but only if one "knows" the ratios of the variances. In biological applications it's almost certainly wishful thinking that one knows the ratios and that the errors would be independent. So it's about the "ability to assume" rather than the "willingness to assume". I would hope that somedayNonlinearModelFit
andLinearModelFit
would allow for mixed models (i.e., models with more than one error term and correlated errors). $\endgroup$