image thumbnail
from Seekpeaks by Per Sundqvist
find maxima of an arbitrary function

[xm,ym,xx,yy]=Seekpeaks(fun,x,y,y_tol,Nsteps,varargin)
function [xm,ym,xx,yy]=Seekpeaks(fun,x,y,y_tol,Nsteps,varargin)
%     [xm,ym,xx,yy]=Seekpeaks(fun,x,y,y_tol,Nsteps,varargin)
% Searches the maxima of the function 'fun', which is an object such as
% produced by inline or by a m-function. y=fun(x,P1,P2,...), where P1, P2,...
% are additional parameters used in the function 'fun' and are given in
% 'varargin' in Seekpeaks as p1,p2,... (see example below)
% Note: use 'fun' for m-function and (just) fun for inline function
%     The INPUT x and y (=fun(x)) are the samples used to locate the maxima and the program 
% will refine iterativeley around the local maxima to find the exact
% maxima. y_tol gives the relative stop criteria for stopping the maxima
% search and Nsteps is a number to terminate the session in case it takes
% long time to find the maxima. Default are 1e-6 and 50 and will be used
% in case [],[] are used as arguments in the function.
%     The OUTPUT xm gives the positions of the maxima and ym the corresponding
% values (vectors). xx and yy gives the union of all values that are 
% found during the refinement including the input sample x and y (it is
% useful for plot-purposes).
%
% %%% Example:
%
% % define a function fun(x) with an additional parameter s
% fun1=inline('s^2*(1./(s^2+(x-1).^2)+1./(s^2+(x-2).^2))','x','s')
% % calculate a rough sample (x,y) of the function
% x=linspace(0,3,20);
% s=0.001;    %parameter
% y=fun1(x,s);
% % Seek the peaks of the function fun1
% [xm,ym,xx,yy]=Seekpeaks(fun1,x,y,[],[],s);
% xm
% plot(xx,yy,x,y,'.');


%START fix default values
if nargin <4
    y_tol=1e-6; %default
    Nsteps=50; %default
elseif nargin <5
    if isempty(y_tol)
        y_tol=1e-6; %default
    end
    Nsteps=50; %default
end
if and(nargin>=5,isempty(Nsteps))
    Nsteps=50; %default
end
if isempty(y_tol)
    y_tol=1e-6; %default
end
%END fix default

%sort values and make them rows
x=x(:)';y=y(:)';
[x,ix]=sort(x);y=y(ix);
%scale y-axis to avoid zero-division in error-estimation
%(scale back at the end of the program)
ymin=min(y);ymax=max(y);
y=(y-ymin)/(ymax-ymin);

%first find local maxima of (x,y)
nm=0;   %number of maxima (exluded end-points)
p1=y(2)<y(1);
pN=y(end-1)<y(end);
for j=2:length(x)-1
    if or(and(y(j)>=y(j-1),y(j)>y(j+1)),and(y(j)>=y(j+1),y(j)>y(j-1)))
        nm=nm+1;
        idx(nm)=j;  %index for maxima;
    end
end

if and(nm==0,not(or(p1,pN)))
    sprintf(['There are no local maxima.','\n',...
        'Empty xm and ym returned'])
    xx=x;yy=y;xm=[];ym=[];
    return
end
sprintf(['looking for ',int2str(nm),' maxima'])

%now check if maxima at ends
xm=[];ym=[];
cc=1;   %maxima counter
if p1
    xm(cc)=x(1);ym(cc)=y(1);        %max values
    cc=cc+1;
end
if pN
    xm(cc)=x(end);ym(cc)=y(end);    %max values
    cc=cc+1;
end

xs=[];ys=[];    %accumulated search values
pex=0;          %extra found maxima
warn=0;         %warning message
for j=1:nm
    x1=x(idx(j)-1);x2=x(idx(j));x3=x(idx(j)+1);%init values
    y1=y(idx(j)-1);y2=y(idx(j));y3=y(idx(j)+1);
    Noterminate=true;cp=1;
    while Noterminate
        %calculate intermediate values (scaled)
        x4=(x1+x2)/2;
        x5=(x2+x3)/2;
        y4=(feval(fun,x4,varargin{:})-ymin)/(ymax-ymin);
        y5=(feval(fun,x5,varargin{:})-ymin)/(ymax-ymin);
        %add new data to output
        xs=[xs,[x4 x5]];ys=[ys,[y4 y5]];
        %seek maxima
        xvec=[x1 x4 x2 x5 x3];
        yvec=[y1 y4 y2 y5 y3];
        if and(y4>y2,y5>y2)
            pex=pex+1;
            %2 maxima
            [junk,wh1]=sort([-1 y4 y2 y5 -2]);
            wh1=wh1(end);
            x1=xvec(wh1-1);y1=yvec(wh1-1);
            x2=xvec(wh1);y2=yvec(wh1);
            x3=xvec(wh1+1);y3=yvec(wh1+1);
            err=(max([y2 y4 y5])-min(abs([y2 y4 y5])))/min(abs([y2 y4 y5]));
        elseif and(y4~=y5,and(y4~=y2,y5~=y2))   %no equal
            %only 1 maximum
            [junk,wh1]=sort([-1 y4 y2 y5 -2]);
            wh1=wh1(end);
            x1=xvec(wh1-1);y1=yvec(wh1-1);
            x2=xvec(wh1);y2=yvec(wh1);
            x3=xvec(wh1+1);y3=yvec(wh1+1);
            err=(max([y2 y4 y5])-min(abs([y2 y4 y5])))/min(abs([y2 y4 y5]));
        elseif and(y4==y5,and(y4==y2,y5==y2))   %all equal
            wh1=3;
            err=0;
        else
            [junk,wh1]=sort([-1 y4 y2 y5 -2]);
            wh1=wh1(end);
            x1=xvec(wh1-1);y1=yvec(wh1-1);
            x2=xvec(wh1);y2=yvec(wh1);
            x3=xvec(wh1+1);y3=yvec(wh1+1);
            err=(max([y2 y4 y5])-min(abs([y2 y4 y5])))/min(abs([y2 y4 y5]));
        end
        cp=cp+1;    %increase counter for seekpeaks
        if cp>Nsteps
            warn=warn+1;
        end
        Noterminate=not(or(cp>Nsteps,err<=y_tol));
    end
    %errvec(j)=err
    xm(cc)=xvec(wh1);
    ym(cc)=yvec(wh1);cc=cc+1;
end

%sort output values and scale back
xx=[x,xs];
yy=[y,ys];
[xx,ix]=sort(xx);
yy=ymin+(ymax-ymin)*yy(ix);
[xm,ix]=sort(xm);
ym=ymin+(ymax-ymin)*ym(ix);

%write result text
sprintf([int2str(length(xm)),' maxima was found','\n',...
         int2str(warn),' peaks exceeded maximum iterations!','\n',...
         int2str(pex),' extra peaks found -> run again to find (if >0)','\n'])

Contact us at files@mathworks.com