# Documentation

## Fit a Model to Complex-Valued Data

This example shows how to perform nonlinear fitting of complex-valued data. While most Optimization Toolbox™ solvers and algorithms operate only on real-valued data, the levenberg-marquardt algorithm works on both real-valued and complex-valued data.

Do not set the FunValCheck option to 'on' when using complex data. The solver errors.

Data Model

The data model is a simple exponential:

$y\left(x\right)={v}_{1}+{v}_{2}{e}^{{v}_{3}x}.$

The x is input data, y is the response, and v is a complex-valued vector of coefficients. The goal is to estimate v from x and noisy observations y.

Artificial Data with Noise

Generate artificial data for the model. Take the complex coefficient vector v as [2;3+4i;-.5+.4i]. Take the observations x as exponentially distributed. Add complex-valued noise to the responses y.

rng default % for reproducibility
N = 100; % number of observations
v0 = [2;3+4i;-.5+.4i]; % coefficient vector
xdata = -log(rand(N,1)); % exponentially distributed
noisedata = randn(N,1).*exp((1i*randn(N,1))); % complex noise
cplxydata = v0(1) + v0(2).*exp(v0(3)*xdata) + noisedata;

Fit the Model to Recover the Coefficient Vector

The difference between the response predicted by the data model and an observation (xdata for x and response cplxydata for y) is:

objfcn = @(v)v(1)+v(2)*exp(v(3)*xdata) - cplxydata;

Use either lsqnonlin or lsqcurvefit to fit the model to the data. This example first uses lsqnonlin. Because the data is complex, set the Algorithm option to 'levenberg-marquardt'.

opts = optimoptions(@lsqnonlin,...
'Algorithm','levenberg-marquardt','Display','off');
x0 = (1+1i)*[1;1;1]; % arbitrary initial guess
[vestimated,resnorm,residuals,exitflag,output] = lsqnonlin(objfcn,x0,[],[],opts);
vestimated,resnorm,exitflag,output.firstorderopt
vestimated =

2.1581 + 0.1351i
2.7399 + 3.8012i
-0.5338 + 0.4660i

resnorm =

100.9933

exitflag =

3

ans =

0.0013

lsqnonlin recovers the complex coefficient vector to about one significant digit. The norm of the residual is sizable, indicating that the noise keeps the model from fitting all the observations. The exit flag is 3, not the preferable 1, because the first-order optimality measure is about 1e-3, not below 1e-6.

Alternative: Use lsqcurvefit

To fit using lsqcurvefit, write the model to give just the responses, not the responses minus the response data.

objfcn = @(v,xdata)v(1)+v(2)*exp(v(3)*xdata);

Use lsqcurvefit options and syntax.

opts = optimoptions(@lsqcurvefit,opts); % reuse the options
[vestimated,resnorm] = lsqcurvefit(objfcn,x0,xdata,cplxydata,[],[],opts)
vestimated =

2.1581 + 0.1351i
2.7399 + 3.8012i
-0.5338 + 0.4660i

resnorm =

100.9933

The results match those from lsqnonlin, because the underlying algorithms are identical. Use whichever solver you find more convenient.

Alternative: Split Real and Imaginary Parts

To use the trust-region-reflective algorithm, such as when you want to include bounds, you must split the real and complex parts of the coefficients into separate variables. For this problem, split the coefficients as follows:

$\begin{array}{c}y={v}_{1}+i{v}_{2}+\left({v}_{3}+i{v}_{4}\right)\mathrm{exp}\left(\left({v}_{5}+i{v}_{6}\right)x\right)\\ =\left({v}_{1}+{v}_{3}\mathrm{exp}\left({v}_{5}x\right)\mathrm{cos}\left({v}_{6}x\right)-{v}_{4}\mathrm{exp}\left({v}_{5}x\right)\mathrm{sin}\left({v}_{6}x\right)\right)\\ \text{ }+i\left({v}_{2}+{v}_{4}\mathrm{exp}\left({v}_{5}x\right)\mathrm{cos}\left({v}_{6}x\right)+{v}_{3}\mathrm{exp}\left({v}_{5}x\right)\mathrm{sin}\left({v}_{6}x\right)\right).\end{array}$

Write the response function for lsqcurvefit.

function yout = cplxreal(v,xdata)

yout = zeros(length(xdata),2); % allocate yout
expcoef = exp(v(5)*xdata(:)); % magnitude
coscoef = cos(v(6)*xdata(:)); % real cosine term
sincoef = sin(v(6)*xdata(:)); % imaginary sin term
yout(:,1) = v(1) + expcoef.*(v(3)*coscoef - v(4)*sincoef);
yout(:,2) = v(2) + expcoef.*(v(4)*coscoef + v(3)*sincoef);

Save this code as the file cplxreal.m on your MATLAB® path.

Split the response data into its real and imaginary parts.

ydata2 = [real(cplxydata),imag(cplxydata)];

The coefficient vector v now has six dimensions. Initialize it as all ones, and solve the problem using lsqcurvefit.

x0 = ones(6,1);
[vestimated,resnorm,residuals,exitflag,output] = ...
lsqcurvefit(@cplxreal,x0,xdata,ydata2);
vestimated,resnorm,exitflag,output.firstorderopt
vestimated =

2.1582
0.1351
2.7399
3.8012
-0.5338
0.4660

resnorm =

100.9933

exitflag =

3

ans =

0.0018

Interpret the six-element vector vestimated as a three-element complex vector, and you see that the solution is virtually the same as the previous solutions.