14
$\begingroup$

Inspired by this previous question: Findfit doesn't give the good fit; Changing the starting values will not change the results.

Consider the following complex-valued dataset.

f[t_] := b + a/(c + t)
data = Table[f[t] /. {a -> -1 - I, b -> 1 + I, c -> -5 + 2 I}, {t, 1, 10}] 
  + 0.1 RandomComplex[{-1 - I, 1 + I}, 10];
plot = ListPlot[Through@{Re, Im}@data]

enter image description here

Mathematica has no trouble fitting a linear model to it, and successfully finds complex values for the fit parameters.

g[t_] := u + v t;
lfit = FindFit[data, g[t], {u, v}, t]
(* {u -> 1.06445 + 1.59084 I, v -> -0.0604794 - 0.0647658 I} *)
Show[plot, Plot[Evaluate@Through@{Re, Im}@(g[t] /. lfit), {t, 1, 10}]]

enter image description here

It likewise has no trouble fitting the original nonlinear model to it if $c$ is held fixed.

fit = FindFit[data, f[t] /. c -> -5 + 2 I, {a, b}, t]
(* {a -> -1.00813 - 0.888072 I, b -> 0.960261 + 1.01034 I} *)
Show[plot, Plot[Evaluate@Through@{Re, Im}@(f[t] /. c -> -5 + 2 I /. fit), {t, 1, 10}]]

enter image description here

But make $c$ variable and suddenly it can't deal with complex numbers any more.

FindFit[data, f[t], {a, b, c}, t]

FindFit::nrlnum: The function value {0.318749 -1.36562 I,0.294047 -1.31665 I,0.306199 -1.5257 I,0.339789 -1.54803 I,<<3>>,0.59458 -1.00036 I,0.343269 -0.994435 I,0.400584 -0.92625 I} is not a list of real numbers with dimensions {10} at {a,b,c} = {1.,1.,1.}.

What gives?

$\endgroup$
  • 2
    $\begingroup$ Strongly related: "Fitting complex data with NonlinearModelfit." $\endgroup$ – Alexey Popkov Jul 15 '14 at 6:22
  • $\begingroup$ @Alexey: Ah, thanks for the link. I hadn't looked at that question carefully before; it does look like a duplicate. Although none of the answers there have realized why FindFit works in some cases but not in others, as Jens's answer here explains. $\endgroup$ – user484 Jul 15 '14 at 6:44
13
$\begingroup$

I think what you're seeing is a consequence of the special model that you're using. The parameters a and b appear only linearly, so as long as they are the only fit parameters it is clear that the best approach for FindFit would be to perform a simple LeastSquares calculation. This is a matrix method that works over the complex numbers, and that's why you get complex answers for the fit parameters a and b when you do

fit = FindFit[data, f[t] /. c -> -5 + 2 I, {a, b}, t]

However, when you add c as a parameter, it's no longer possible to go this route. Now, the parameters that are varied in FindFit have to be real. This is because for a nonlinear problem, FindFit uses FindMinimum to minimize a norm function (Norm), and FindMinimum allows only real (or integer) variables.

| improve this answer | |
$\endgroup$
  • $\begingroup$ So this is the reason why explicitly using real-valued parameters and a custom norm-function works. +1 $\endgroup$ – halirutan Jul 15 '14 at 5:39
13
$\begingroup$

For the reason why find fit fails when c is variable, please see the answer of Jens. One way to solve this is to use create the complex target function from real-valued parameters and set the NormFunction explicitely:

f1[t_] := (br + I bi) + (ar + I ai)/(cr + I ci + t)
fit = FindFit[data, f1[t], {ar, ai, br, bi, cr, ci}, t, 
   NormFunction -> (Norm[Re[#*Conjugate[#]]] & )];

Show[plot, Plot[Evaluate@Through@{Re, Im}@(f1[t] /. fit), {t, 1, 10}]]

Mathematica graphics

| improve this answer | |
$\endgroup$
  • $\begingroup$ The strangeness is that FindFit can determine the appropriate norm function in some cases but not in others. $\endgroup$ – user484 Jul 15 '14 at 5:28
  • $\begingroup$ I know this is beyond the scope of your answer but I was trying to use your method to find the data fit for the original linked question (using his data instead of the generated data in this question), and I got the error of "Failed to converge to the requested accuracy or precision within 100 iterations." screenshot Do you know what I can do to fix this? $\endgroup$ – seismatica Jul 15 '14 at 7:10
  • 3
    $\begingroup$ @seismatica, halirutan: NormFunction -> (Re[#].Re[#] + Im[#].Im[#] &) seems to work better. $\endgroup$ – user484 Jul 15 '14 at 16:24
  • $\begingroup$ Works like a charm (though I'd love to have someone explain to me why that would work). Thanks @RahulNarain! $\endgroup$ – seismatica Jul 15 '14 at 22:08
  • 1
    $\begingroup$ According to the Documentation, "FindFit by default finds a least-squares fit" and "FindFit effectively uses FindMinimum to minimize a norm of the residual function". But actually they return slightly different answers even if one supplies identical NormFunction and objective function for FindMinimum. But one thing is clear: specifying Method -> "LevenbergMarquardt" for FindFit does not change the output and does not produce an error as it does when one specifies Norm[residual] as objective function for Findminimum. So FindFit internally converts Norm into sum of squares. $\endgroup$ – Alexey Popkov Jul 16 '14 at 7:58

Your Answer

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