computeLatencies.m 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. function [ latTimes, lsqCurveTimes, lsqFittedCurve, lsqFittedParam, lsqCurveFitErr] = computeLatencies( MuaeData, MuaeTimes, lsqTimeRes )
  2. % COMPUTE LATENCY function written by Demetrio Ferro demetrio.ferro@iit.it
  3. % Based on (Roelfsema, Tolboom, Khayat, Neuron 2007).
  4. % developed for (Ferro, van Kempen, Boyd, Panzeri, Thiele, PNAS 2021)
  5. %
  6. % The function computes Latency indices base don MUAes fitted by cumulative
  7. % gaussian function with 5 (degrees of freedom) parameters (mu, sigma, alpha, c, d).
  8. % The best fit is assigned based on Least Squares (LS) of iterative estimation.
  9. %
  10. % Inputs
  11. % MuaeData [NxT] Multi-channel (N channels) MUAes in time (T points)
  12. % MuaeTimes [1xT] Timestamps of MUAe recordings (T points)
  13. % lsqTimeRes [1x1] Resolution of time series for Latency estimation
  14. %
  15. % Outputs
  16. % latTimes [1xN] Latency indices at t*:={f(t*)=1/3*max(f(t)),t in MuaeTimes}
  17. % lsqCurveTimes [1x(T*lsqTimeRes)] Timestamps of fitted curves
  18. % lsqFittedCurve [Nx(T*lsqTimeRes)] Fitted curves at LS parameters
  19. % lsqFittedParam [Nx5] LS parameters
  20. % lsqFitErr [1xN] LS error of the fit
  21. [N,T]=size(MuaeData);
  22. latTimes=zeros(1,N);
  23. lsqCurveTimes=linspace(MuaeTimes(1),MuaeTimes(end),ceil(T*lsqTimeRes));
  24. lsqFittedCurve=zeros(N,ceil(T*lsqTimeRes));
  25. lsqFittedParam=zeros(N,5);
  26. lsqCurveFitErr=zeros(1,N);
  27. % LsqParam0 = [mu, sigma, alpha, c, d];
  28. % LsqParam0 is the starting point for the fitting routine;
  29. lsqParam0=[0 0 0 0 0]; % starts with naive values for V1;
  30. % Ex-Gaussian Function to fit
  31. exGfunToFit = @(p,tdata)(p(5)*exp(p(1)*p(3)+0.5*p(2)^2*p(3)^2-p(3)*tdata)...
  32. .*normcdf(tdata,p(1)+p(2)^2*p(3),p(2))...
  33. +p(4)*normcdf(tdata,p(1),p(2)));
  34. movMeanResolution=5;
  35. mu0ValuesOrigVec=MuaeTimes;
  36. options = optimoptions('lsqcurvefit','Display','off');
  37. % LOOP OVER CHANNELS
  38. for ch=1:N
  39. MuaeDataMovMean=movmean(MuaeData(ch,:),movMeanResolution);
  40. IntMuaeDataMM=interp1(MuaeTimes,MuaeDataMovMean,mu0ValuesOrigVec);
  41. DiffMuaeDataMM=diff([IntMuaeDataMM IntMuaeDataMM(end)]);
  42. % EMPIRICAL SETTING: TAKE ONLY POSITIVE SLOPES TO SPEED UP
  43. mu0ValuesVec=mu0ValuesOrigVec(DiffMuaeDataMM>=0); %.2*min(DiffMuaeDataMM));
  44. lsqMuErrMovMean=-1*ones(1,length(mu0ValuesVec));
  45. lsqFittedParamMovMean=zeros(length(mu0ValuesVec),5);
  46. % ATTENTION: FIT THE MOVMEAN THEN USE THE MIN MSE PARAMETERS
  47. for muIdx=1:length(mu0ValuesVec)
  48. lsqParam0(1)=mu0ValuesVec(muIdx);
  49. try
  50. lsqFittedParamMovMean(muIdx,:)=lsqcurvefit(exGfunToFit,lsqParam0,MuaeTimes,MuaeDataMovMean,[],[],options);
  51. lsqMuErrMovMean(muIdx)=norm(feval(exGfunToFit,lsqFittedParamMovMean(muIdx,:),MuaeTimes)-MuaeData(ch,:));
  52. catch
  53. warning('lsqcurvefit does not converge for the parameters chosen!');
  54. end
  55. end
  56. % Remove non-tested mu parameters
  57. lsqMuErrMovMean(lsqMuErrMovMean==-1)=[];
  58. lsqFittedParamMovMean(lsqMuErrMovMean==-1)=[];
  59. [~,lsqMinMuErrIdxMovMean]=min(lsqMuErrMovMean);
  60. lsqFittedCurve(ch,:)=feval(exGfunToFit,lsqFittedParamMovMean(lsqMinMuErrIdxMovMean,:),lsqCurveTimes);
  61. lsqCurveFitErr(ch)=norm(interp1(lsqCurveTimes,lsqFittedCurve(ch,:),MuaeTimes)-MuaeData(ch,:));
  62. lsqFittedParam(ch,:)=lsqFittedParamMovMean(lsqMinMuErrIdxMovMean,:);
  63. latIndex=find(lsqFittedCurve(ch,:)>=1/3*max(lsqFittedCurve(ch,:)),1,'first');
  64. if ~isempty(latIndex)
  65. latTimes(ch)=lsqCurveTimes(latIndex);
  66. else
  67. latTimes(ch)=NaN;
  68. end
  69. end
  70. end