This question is a variant of "Define function using variable list"
Fitting functions require a model function defined in terms of explicit symbols that should only evaluate given numeric inputs, and a list of those symbols. That is, the function needs to be SetDelayed, and the arguments must be tested patterns in the form of _?NumericQ. An example is given in the Generalizations section of NonlinearModelFit.
data = {{6.47, 3.65}, {7.43, 3.45}, {3.9, -2.94}, {4.8, -1.29}, {2.48, -0.35},
{6.32, 3.16}, {2.59, -1.19}, {9.13, -2.}, {3.81, -3.04}, {3.33, -2.68}};
model[(a_)?NumberQ, (b_)?NumberQ, (c_)?NumberQ] :=
Module[{y, x}, First[y /. NDSolve[{Derivative[2][y][x] + a*y[x] == 0, y[0] == b,
Derivative[1][y][0] == c}, y, {x, 0, 10}]]]
NonlinearModelFit[data, model[a, b, c][x], {a, b, c}, x, Method -> "Gradient"];
%["BestFitParameters"]
(* {a -> 1.04052, b -> 2.47, c -> 2.29708} *)
This is easy enough if you hard-code the function, but what if you do not know the function or symbols until runtime? For example, what if the model being fit is generated by other code? Lets say we have a piece of code elsewhere that generates the differential equation and parameter list:
diffeq = {Derivative[2][y][x] + a*y[x] == 0, y[0] == b, Derivative[1][y][0] == c};
params = {a, b, c};
Now we need to programmatically generate the model function to put into NonlinearModelFit. So far, this is the best solution I can find:
Define a function that will solve the given equations
solver[eq_] := Block[{y, x}, First[y /. NDSolve[eq, y, {x, 0, 10}]]]
Define a function that takes a list of valueless symbols and returns a list of tested patterns
numericPattern = Function[sym, sym:_?NumericQ, Listable];
Generate the function and test
Activate[
Inactive[SetDelayed][
genmodel @@ numericPattern[params],
Inactive[solver][diffeq]
]
]
Information[genmodel, "DownValues"]
(* genmodel[a:_?NumericQ, b:_?NumericQ, c:_?NumericQ] :=
solver[{a*y[x] + Derivative[2][y][x] == 0, y[0] == b, Derivative[1][y][0] == c}] *)
NonlinearModelFit[data, (genmodel @@ params)[x], params, x, Method -> "Gradient"];
%["BestFitParameters"]
(* {a -> 1.04052, b -> 2.47, c -> 2.29708} *)
This works, but seems awkward to me. I understand that there is no safety net here for symbols that have values, but I am assuming that we could not have gotten to this point if that were the case. I also do not understand this solution to the depth I would need to explain it to someone else. Is there something simple (or complicated) that I am missing, or a better solution anyone can suggest?
ParametricNDSolve(Value)[]along with indexed variables. Using the same data given in the OP:model2 = ParametricNDSolveValue[{y''[x] + c[1] * y[x] == 0, y[0] == c[2], y'[0] == c[3]}, y, {x, 0, 10}, Array[c, 3]]; fm = NonlinearModelFit[data, Apply[model2, Array[c, 3]][x], Array[c, 3], x, Method -> "Gradient"]; fm["BestFitParameters"]– J. M.'s missing motivation Nov 15 '19 at 14:54