Quantcast
Viewing latest article 1
Browse Latest Browse All 2

Answer by KennyColnago for Fitting a Multi-Exponential Decay With Constraints

There is much to exponential fitting that is obscured by non-linear fits to a number of discrete components. Unless you have thousands of densely sampled points (the vector t in your notation) you will never truly resolve 6 components. If 6 spikes appear, it is merely a convenient fiction. Beware. A true model containing a wide block of continuous signal has a least-squares fit composed of a few delta-functions. Nothing like the input continuous model. Remember, exponentials are hopelessly non-orthogonal functions.

It is vitally important that you abandon a non-linear approach to finding multiple components. It is far better to use the Mathematica implementation of non-negative least-squares by Lawson and Hanson. See my answer and the comments to this question. Provide a list of k values (in your notation) and let a function calculate amplitudes at these many locations. A non-linear simultaneous calculation of both k values and amplitudes will never converge in practice.

First a function for log sampling of the relaxation time axis, T2 in magnetic resonance, otherwise k in your notation.

T2sample[n_Integer, min_, max_, ilog_Integer] :=   If[ilog == 0,      min + Range[0., n - 1]*(max - min)/(n - 1.),       min * 10.^(Range[0, n - 1]*Log[10, max/min]/(n - 1.))]

The following t2l1con1 uses linear programming to return a list of discrete components at the input list of relaxation times T2, or k values in your notation. There is a constraint to unit total amplitude.

  • t: list of measurement times
  • dat: list of data amplitudes at the measurement times
  • stn: list of standard deviations of the data dat
  • t2: list of relaxation times at which amplitudes are to be calculated

The code.

t2l1con1[t_List, dat_List, stn_List, t2_List, opts___] :=   Block[{nt = Length[t], nt2 = Length[t2], c, m, rhs, soln},      (* objective function *)      c = Flatten[{Table[0., {nt2}], Table[1., {2*nt}]}];      (* data constraints *)      m = Table[0., {nt + 1}, {nt2 + 2*nt}];      m[[Range[nt],Range[nt2]]] = DiagonalMatrix[1/stn].E^Outer[Times,-t,1/t2];      Do[m[[i, {nt2 + 2*i - 1, nt2 + 2*i}]] = {1., -1.}, {i, 1, nt}];      (* weighting requiring unit sum of amplitudes *)      m[[nt + 1, Range[nt2]]] = 1.;      (* right hand side *)      rhs = Join[Transpose[{dat/stn, Table[0., {nt}]}], {{1., 0.}}];      (* find solution *)      soln = Chop[LinearProgramming[c, m, rhs, opts]];      Transpose[{t2, soln[[Range[nt2]]]}] ]

A bi-exponential model with no noise. Note the split peak due to the model relaxation time (0.100) lying between two input T2 values. Simply add the two amplitudes. Split peaks may be merged by re-running with an additional T2 value inserted into the input list. Running a linear method many times will still be faster than a non-linear attempt.

Block[{t, dat, stn, t2},   t = Range[0.01, 0.5, 0.005];   dat = 0.3*E^(-t/0.05) + 0.7*E^(-t/0.100);   stn = ConstantArray[0.01, Length[dat]];   t2 = T2sample[250, 0.010, 1.00, 1];   ListLogLinearPlot[      t2l1con1[t, dat, stn, t2],      Filling -> Axis, Frame -> True,       FrameLabel -> {"T2 Relaxation Time", "Amplitude"}]]

Image may be NSFW.
Clik here to view.
bi-exp no noise

The same bi-exponential model with about 5% noise. Note the variation from the true, input model. Such is life with exponential fitting.

Block[{t, dat, stn, t2},   t = Range[0.01, 0.5, 0.005];   dat = 0.3*E^(-t/0.05) + 0.7*E^(-t/0.100) +               0.05*RandomReal[{-1, 1}, Length[t]];   stn = ConstantArray[0.01, Length[dat]];   t2 = T2sample[250, 0.010, 1.00, 1];   ListLogLinearPlot[      t2l1con1[t, dat, stn, t2],      Filling -> Axis, Frame -> True,       FrameLabel -> {"T2 Relaxation Time", "Amplitude"}]]

Image may be NSFW.
Clik here to view.
bi-exp 5% noise


Viewing latest article 1
Browse Latest Browse All 2

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>