A Prony series is similar to a Fourier series but can have fewer terms. It takes the form
$\sum_{i=1}^{M} A_i e^{\sigma _i t} \cos \left(\omega _i t+\phi _i\right)$
Note that unlike Fourier series there is a decay term and further, the frequency does not have to be equally spaced increments. Details may be found here.
The problem I am addressing here is how to find the terms of this series when approximating a function.
Building on this answer from Daniel Lichtblau I first generated some data as follows:
ClearAll[amp, freq]
amp = Interpolation[{{0, 9.870000000000001`}, {0.1795`,
6.69`}, {0.41150000000000003`, 3.04`}, {0.6385000000000001`,
0.96`}, {1, 0.25`}}];
freq = Interpolation[{{0, 79.2`}, {0.2545`,
99.80000000000001`}, {0.4985`, 109.2`}, {0.7395`,
113.60000000000001`}, {1, 115.60000000000001`}}];
sr = 1500; data =
Table[{t, amp[t] Cos[2 π freq[t] t]}, {t, 0, 1 - 1/sr, 1/sr}];
ListLinePlot[data, Frame -> True]
Note this is not an exponential decay. If it was exponential then only two terms in the Prony series would be needed. Here we need many more.
th = data[[All, 2]];
tt = data[[All, 1]];
nn = Length@data;
nc = 300; (* number of terms *)
mat = Most[Partition[th, nc, 1]];
rhs = Drop[th, nc];
soln = PseudoInverse[mat].rhs;
roots = xx /. NSolve[xx^nc - soln.xx^Range[0, nc - 1] == 0, xx];
e = roots^(t sr);
mat2 = Table[e, {t, tt}];
coeffs = LeastSquares[mat2, th];
eqn = coeffs.e;
This fit has 300 terms. It throws out an error that precision may be lost. So that need fixing. The data can be regenerated as follows. I plot the fit and the original data and the difference between the two.
fit = Table[eqn, {t, tt}];
ListLinePlot[{data, Transpose[{tt, fit}]}, Frame -> True,
PlotRange -> All]
ListLinePlot[Transpose[{tt, data[[All, 2]] - fit}], Frame -> True,
PlotRange -> All]
This is not bad but we need more terms. Here I try with 500 terms and also set the precision to avoid the error in the first try.
sp = 50; (* precision *)
th = data[[All, 2]];
tt = SetPrecision[data[[All, 1]], sp];
nn = Length@data;
nc = 500; (* number of terms *)
mat = Most[Partition[th, nc, 1]];
rhs = Drop[th, nc];
soln = PseudoInverse[mat].rhs;
roots = xx /. NSolve[xx^nc - soln.xx^Range[0, nc - 1] == 0, xx];
e = SetPrecision[roots^(t sr), sp];
mat2 = Table[e, {t, tt}];
coeffs = LeastSquares[mat2, th];
eqn = coeffs.e;
Now to plot the fit and look at the error
fit = Table[eqn, {t, tt}];
err = Transpose[{tt, th - fit}];
ListLinePlot[{data, Transpose[{tt, fit}]}, Frame -> True,
PlotRange -> All]
ListLinePlot[err, Frame -> True, PlotRange -> All
]
This is getting better with an order of magnitude smaller error. However, I am struggling with the loss of precision and need more terms. Can this fitting method using Prony series be made better? Is more precision the only solution?
t>0.9
, is this intended? $\endgroup$