% [order,M] = convseq(X,x[,ind]) Convergence order of a sequence
%
% Given the sequence X = x1,...,xK with convergence point x, this:
% - Computes the pairs of consecutive distances (d1,d2), (d2,d3), etc.
%   where di = ||xi-x||.
% - Fits a LSQ line to the log-distances: log(d(i+1)) = a + b.log(d(i)),
%   corresponding to a model d(i+1)/d(i)^b = M with convergence order b
%   and constant M = exp(a).
%   If ind=[a b] is given, only X([a b],:) are used for the fit.
% - Plots the pairs (with solid markers for those in "ind") and the LSQ line.
%
% Note: the convergence order is an asymptotic concept, so you may have to
% discard the first few elements in the sequence (using the argument "ind").
%
% In:
%   X: KxN list of row vectors.
%   x: 1xN row vector.
%   ind: 1x2 list of indices. Default: [1 K].
% Out:
%   order: order of convergence.
%   M: constant of convergence.
%
% Any non-mandatory argument can be given the value [] to force it to take
% its default value.

% Copyright (c) 2010 by Miguel A. Carreira-Perpinan

function [order,M] = convseq(X,x,ind)

K = size(X,1);

% ---------- Argument defaults ----------
if ~exist('ind','var') | isempty(ind) ind = [1 K]; end;
ind = [max(ind(1),1) min(ind(2),K)];
% ---------- End of "argument defaults" ----------

d = log(sum(bsxfun(@minus,X,x).^2,2))/2;	% Distances di = ||xi-x||
d1 = d(ind(1):ind(2)); K1 = length(d1);

% LSQ line
X1 = d1(1:K1-1); Y1 = d1(2:K1);
tmp = [sum(X1.^2) sum(X1);sum(X1) K1-1] \ [sum(X1.*Y1);sum(Y1)];
order = tmp(1); M = exp(tmp(2));

% Figure
XX = d(1:K-1); YY = d(2:K); XX1 = d1(1:end-1); YY1 = d1(2:end);
df = d(~isinf(d)); ax = 0.1*(max(df)-min(df))*[-1 1] + [min(df) max(df)];
if (length(ax)>1) & (diff(ax)~=0)
  plot(XX,YY,'ro',ax,tmp(2)+tmp(1)*ax,'b-');
  hold on; plot(XX1,YY1,'ro','MarkerFaceColor','r'); hold off;
  set(gca,'DataAspectRatio',[1 1 1]); axis([ax ax]);
else
  cla;
end
title(['Convergence order: ' num2str(order)...
       ';  convergence constant M = ' num2str(M)]);

