1
$\begingroup$

If I have a function of list argument (presumably with large number of entries), how can I pass it to NonlinearModelFit? I don't want to pass it by pattern checking (f[x_?(VectorQ[#, Numeric!]&)]) because apparently it turns off some intermediate operations and heavily slows down the fitting.

Example:

f[x_, a_] := Exp[-Sin[{x, x}.a]];

NonlinearModelFit[RandomReal[{0, 1}, {10, 3}], f[x, a], {{a, {1, 2}}},
  x]
$\endgroup$
  • 2
    $\begingroup$ What are your fit parameters? $\endgroup$ – BlacKow Apr 13 '16 at 19:34
  • $\begingroup$ @BlacKow I corrected, does my question make sense? $\endgroup$ – Al Guy Apr 13 '16 at 22:05
  • $\begingroup$ What are dimensions of x and a? $\endgroup$ – BlacKow Apr 13 '16 at 22:13
  • 2
    $\begingroup$ Oh... I think I understand now, you want to find a matrix that fits your data $\endgroup$ – BlacKow Apr 13 '16 at 22:21
2
$\begingroup$

I've always done it like this, and I've done fits with a few hundred parameters so it isn't that much of a pain.

The point is that you define the function f to take a vector input for x and a. Then you define a vector of variables, {x1, x2,....} and that is the fourth argument to NonlinearModelFit. The second argument to NonlinearModelFit needs to be a scalar function, where all the matrix expansions have happened already. In the case here, f[variables, parameters] evaluates the matrix product before it does any fitting.

parameters = Array[a, {7}]
variables = Array[x, {7}]
guess = Range[7]
f[x_, a_] := Exp[-Sin[x.a]];
nlm = NonlinearModelFit[RandomReal[{0, 1}, {40, 8}], 
  f[variables, parameters], Transpose[{parameters, guess}], 
  Evaluate@variables]
nlm["BestFitParameters"]


(* {a[1], a[2], a[3], a[4], a[5], a[6], a[7]} *)

(* {x[1], x[2], x[3], x[4], x[5], x[6], x[7]} *)

(* {1, 2, 3, 4, 5, 6, 7} *)

enter image description here

(* {a[1] -> 3.47592, a[2] -> 0.155765, a[3] -> 4.18935, a[4] -> 1.59981, 
 a[5] -> 7.34998, a[6] -> 1.60615, a[7] -> 10.3245} *)
$\endgroup$
  • $\begingroup$ But your x is a scalar and a is a vector. I think he wants x as vector and a as a matrix. $\endgroup$ – BlacKow Apr 14 '16 at 14:46
  • $\begingroup$ That isn't how I read his code, he gives an initial guess for a that is a vector. Likewise he takes the Sin of {x, x}.a which to me implies {x, x}.a is a scalar. Sure, he could be taking advantage of the listability of Sin and Exp, but the way it is written x should be a scalar. $\endgroup$ – Jason B. Apr 14 '16 at 14:50
  • $\begingroup$ Well if you look at it your way, there is no need to have several as, because you could just replace it with Exp[-Sin[ax]]. Look at the edit history of his question, his last edit confused it a bit. $\endgroup$ – BlacKow Apr 14 '16 at 14:58
  • $\begingroup$ Right, but I'm sure this is just a minimal simple working example. I'm gonna go on the assumption that the argument of Sin and Exp should be a scalar, but this can be easily extended to account for a vector x as well. Whether it's x.a.x or x.a, it's easily extended. I think the main point is that you want to do the matrix alagebra on your function rather than asking FindFit to do it. $\endgroup$ – Jason B. Apr 14 '16 at 15:02
  • 1
    $\begingroup$ You don't, you define the function f to take a vector input for x and a. Then you define a vector of variables, {x[1], x[2],....} and that is the fourth argument to NonlinearModelFit. The second argument to NonlinearModelFit needs to be a scalar function, where all the matrix expansions have happened already. In the case above, f[variables, parameters] evaluates the matrix product before it does any fitting. $\endgroup$ – Jason B. Apr 14 '16 at 15:11
2
$\begingroup$

This is not a real answer, but applying @JasonB's approach to 2D case. So x is a vector and a is a matrix. I simplified the function to see how good is the fit in simple case.

We have noisy data and we want to try to find fit and compare it with test

f[x_, a_] := Exp[-x.a.x];
test = {{0.75, 0.}, {0, 0.25}};
data = Flatten[{#, f[#, test] + RandomReal[{-0.05, 0.05}]}] & /@ 
   Tuples[Range[-4, 4, 0.1], 2];

parameters = Array[a, {2, 2}];
z = Array[x, {2}];
paramFull = Flatten[#, 1] &@Map[{#, 0} &, parameters, {2}]; (* sets initial guess to zero for all parameters *)

fit = NonlinearModelFit[data, f[z, parameters], paramFull, z];
parameters /.fit["BestFitParameters"]

(*{{0.748501, 96.3969}, {-96.3989, 0.248822}}*)

So the fit is pretty good (although the non diagonal elements are way off it doesn't matter, because antisymmetric matrix gives zero in $x^TAx$).

Show[ListPointPlot3D[data, PlotRange -> Full], 
 Plot3D[fit[x, y], {x, -4, 4}, {y, -4, 4}, PlotRange -> Full, 
  Mesh -> None]]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.