Using FindFit for a non-trivial complex function [duplicate]

I have data set which I would like to fit to the function

$\quad \quad f(r) = A\cos(kr - \pi/4 + \phi_1)\times\exp(i(-2\log(r) + \phi_2))$,

where $k$ is a constant I know and $(A,\phi_1,\phi_2)$ are the fitting parameters. In fact, if I split my data set into real and imaginary parts and fit these separately to the function above, using a command like

solRe = FindFit[ReU, A*Cos[k*x - π/4 + B] Cos[-2 Log[x] + C], {A, B, C}, x]
solIm = FindFit[ImU, A*Cos[k*x - π/4 + B] Sin[-2 Log[x] + C], {A, B, C}, x],


everything works well. I get the same value for B for the two fits, which is encouraging. However, the values for A and C are different. Therefore I would like to fit to the complex ansatz. I tried doing this with

soltot = FindFit[U, A*Cos[k*x - π/4 + B] Exp[I (-2 Log[x] + C)], {A, B, C}, x]


but I get errors. Does anyone have an idea how I could do such a fit?

marked as duplicate by MarcoB, dr.blochwave, m_goldberg, Bob Hanlon, Michael E2Jul 13 '15 at 17:57

If all you want is a least-squares fit, the easiest way to do might just be to construct a function that is the sum of the squares of the residuals, and then feed it into FindMinimum. As a example, let's construct a data set of complex variables as a function of a real variable x:

data = Table[{x, (0.719) x^(0.907 I) + RandomComplex[{-0.005 (1 + I), 0.005 (1 + I)}]}, {x, 0.1, 1, 0.1}]

{{0.1, -0.352264 - 0.622697 I}, {0.2, 0.0750264 - 0.712474 I},
{0.3, 0.334793 - 0.642127 I}, {0.4, 0.479876 - 0.534359 I},
{0.5, 0.586353 - 0.422211 I}, {0.6, 0.647432 - 0.32101 I},
{0.7, 0.679054 - 0.23206 I}, {0.8, 0.70489 - 0.140092 I},
{0.9, 0.71389 - 0.0680025 I}, {1., 0.719136 - 0.00133755 I}}


Let's try to extract the values of a and b from the data. We can construct a function that gives us the square of the magnitude of the difference between the data points and the model, as a function of a and b for our model $f(x) = a x^{i b}$:

sqres[a_, b_] := Norm[Transpose[data][[2]] - (a x^(b I) /. x -> Transpose[data][[1]])]^2


Note that if the data fits the model perfectly, sqres should have an exact minimum of 0 at $a = 0.719$ and $b = 0.907$. However, since we threw random complex noise into the data, the minimum of sqres will not be zero, and may be somewhere other than the nominal values of a and b. Still, it gets pretty close:

FindMinimum[sqres[a, b], {a, b}]
{0.000190037, {a -> 0.719187, b -> 0.907388}}


As usual with non-linear fits, you might have to provide initial "guesses" for the values of a and b before Mathematica can find a useful minimum of the sqres function.

• FindArgMin[]/NArgMin[] can be used if you're only interested in the parameters and not the $\chi^2$ value. – J. M. will be back soon Jul 13 '15 at 15:50

I have to fit complex functions all the time and it is annoying that NonlinearModelFit does not take complex values. However, it will take a function of two variables so I split into real and imaginary parts and introduce a dummy second variable. Here is your function

ClearAll[f];
f[r_] := A Cos[k r - \[Pi]/4 + \[Phi]1] Exp[I (-2 Log[r] + \[Phi]2)]


Now take real and imaginary parts and build a 3D function with y as the dummy variable.

re = ComplexExpand[Re[f[r]]];
im = ComplexExpand[Im[f[r]]];
model = (1 - y) re + y im;


Thus when y = 0 we have the real part and when y = 1 we have the imaginary part.

I have tried this out. Here are some parameter values which we can use to make some data (pure guesses I don't know your data).

vals = {A -> 1.5, k -> 2, \[Phi]1 -> \[Pi]/2., \[Phi]2 -> \[Pi]/4.};
data = Table[Evaluate[{r, f[r]} /. vals], {r, 1, 10, 0.1}];


We now need a function to turn your data into a function of r and y.

ClearAll[make3D];
make3D[data_] := Join[
Transpose[{data[[All, 1]], ConstantArray[0, Length[data]],
Re[data[[All, 2]] ]}],
Transpose[{data[[All, 1]], ConstantArray[1, Length[data]],
Im[data[[All, 2]] ]}]
]


now make the data 3D

data3D = make3D[data];


I am fitting using NonlinearModelFit so that you get all the good statistics.

fit = NonlinearModelFit[data3D,
model /. k -> 2, {A, \[Phi]1, \[Phi]2}, {r, y}];


I have put in k since you stated you knew this; would it be better to get k from fitting?

The fit is very good but I did not put in any noise.

fit["ParameterConfidenceIntervalTable"]


Show[ListPlot[Transpose[{data[[All, 1]], Re[data[[All, 2]]]}]],
Plot[fit[r, 0], {r, 1, 10}]]


Show[ListPlot[Transpose[{data[[All, 1]], Im[data[[All, 2]]]}]],
Plot[fit[r, 1], {r, 1, 10}]]


Hope that helps.