/*---------------------------------------------------------------------------*
 *                                   IT++			             *
 *---------------------------------------------------------------------------*
 * Copyright (c) 1995-2003 by Tony Ottosson, Thomas Eriksson, Pål Frenger,   *
 * Tobias Ringström, and Jonas Samuelsson.                                   *
 *                                                                           *
 * Permission to use, copy, modify, and distribute this software and its     *
 * documentation under the terms of the GNU General Public License is hereby *
 * granted. No representations are made about the suitability of this        *
 * software for any purpose. It is provided "as is" without expressed or     *
 * implied warranty. See the GNU General Public License for more details.    *
 *---------------------------------------------------------------------------*/

/*! 
  \file 
  \brief Vector quantizer training
  \author Thomas Eriksson
*/

#include "srccode/vqtrain.h"
#include "base/random.h"
#include "base/timing.h"
#include "base/elmatfunc.h"
#include "base/stat.h"
#include "base/matfunc.h"

namespace itpp {

// the cols contains codevectors
double kmeansiter(Array<vec> &DB, mat &codebook)
{
	int				DIM=DB(0).length(),SIZE=codebook.cols(),T=DB.length();
	vec				x,xnum(SIZE);
	mat				xsum(DIM,SIZE);
	int				n,MinIndex,i,j,k;
	double			MinS,S,D,Dold,*xp,*cp;
	
	xsum.clear();
	xnum.clear();

	n=0;
	D=1E20;
	Dold=D;
	D=0;
	for (k=0;k<T;k++) {
		x=DB(k);
		xp=x._data();
		MinS=1E20;
		MinIndex=0;
		for (i=0;i<SIZE;i++) {
			cp=&codebook(0,i);
			S=sqr(xp[0]-cp[0]);
			for (j=1;j<DIM;j++) {
				S+=sqr(xp[j]-cp[j]);
				if (S>=MinS) goto sune;
			}
			MinS=S;
			MinIndex=i;
sune:			i=i;
		}
		D+=MinS;
		cp=&xsum(0,MinIndex);
		for (j=0;j<DIM;j++) {
			cp[j]+=xp[j];
		}
		xnum(MinIndex)++;
	}
	for (i=0;i<SIZE;i++) {
		for (j=0;j<DIM;j++) {
			codebook(j,i)=xsum(j,i)/xnum(i);
		}
	}
	return D;
}

mat kmeans(Array<vec> &DB, int SIZE, int NOITER, bool VERBOSE)
{
	int				DIM=DB(0).length(),T=DB.length();
	mat				codebook(DIM,SIZE);
	int				n,i,j;
	double			D,Dold;
	ivec			ind(SIZE);
	
	for (i=0;i<SIZE;i++) {
		ind(i)=randi(0,T-1);
		j=0;
		while (j<i) {
			if (ind(j)==ind(i)) {
				ind(i)=randi(0,T-1);
				j=0;
			}
			j++;
		}
		codebook.set_col(i,DB(ind(i)));
	}
	

	if (VERBOSE) cout << "Training VQ..." << endl ;

	D=1E20;
	for (n=0;n<NOITER;n++) {
		Dold=D;
		D=kmeansiter(DB,codebook);
		if (VERBOSE) cout << n << ": " << D/T << " ";
		if (std::abs((D-Dold)/D) < 1e-4) break;
	}
	return codebook;
}

mat lbg(Array<vec> &DB, int SIZE, int NOITER, bool VERBOSE)
{
	int		S=1,DIM=DB(0).length(),T=DB.length(),i,n;
	mat		cb;
	vec		delta=0.001*randn(DIM),x;
	double	D,Dold;

	x=zeros(DIM);
	for (i=0;i<T;i++) {
		x+=DB(i);
	}
	x/=T;
	cb.set_size(DIM,1);
	cb.set_col(0,x);
	while (cb.cols()<SIZE) {
		S=cb.cols();
		if (2*S<=SIZE) cb.set_size(DIM,2*S,true);
		else cb.set_size(DIM,2*S,true);
		for (i=S;i<cb.cols();i++) {
			cb.set_col(i,cb.get_col(i-S)+delta);
		}
		D=1E20;
		for (n=0;n<NOITER;n++) {
			Dold=D;
			D=kmeansiter(DB,cb);
			if (VERBOSE) cout << n << ": " << D/T << " ";
			if (std::abs((D-Dold)/D) < 1e-4) break;
		}
	}

	return cb;
}

mat vqtrain(Array<vec> &DB, int SIZE, int NOITER, double STARTSTEP, bool VERBOSE)
{
	int				DIM=DB(0).length();
	vec				x;
	vec				codebook(DIM*SIZE);
	int				n,MinIndex,i,j;
	double			MinS,S,D,step,*xp,*cp;
	
	for (i=0;i<SIZE;i++) {
		codebook.replace_mid(i*DIM,DB(randi(0,DB.length()-1)));
	}
	if (VERBOSE) cout << "Training VQ..." << endl ;

res: D=0; 
	for (n=0;n<NOITER;n++) {
		step=STARTSTEP*(1.0-double(n)/NOITER);if (step<0) step=0;
		x=DB(randi(0,DB.length()-1)); // seems unnecessary! Check it up.
		xp=x._data();

		MinS=1E20;
		MinIndex=0;
		for (i=0;i<SIZE;i++) {
			cp=&codebook(i*DIM);
			S=sqr(xp[0]-cp[0]);
			for (j=1;j<DIM;j++) {
				S+=sqr(xp[j]-cp[j]);
				if (S>=MinS) goto sune;
			}
			MinS=S;
			MinIndex=i;
sune:		i=i;
		}
		D+=MinS;
		cp=&codebook(MinIndex*DIM);
		for (j=0;j<DIM;j++) {
			cp[j]+=step*(xp[j]-cp[j]);
		}
		if ((n%20000==0) && (n>1)) {
			if (VERBOSE) cout << n << ": " << D/20000 << " ";
			D=0;
		}
	}

// checking training result
	vec	dist(SIZE),num(SIZE);

	dist.clear();num.clear();
	for (n=0;n<DB.length();n++) {
		x=DB(n);
		xp=x._data();
		MinS=1E20;
		MinIndex=0;
		for (i=0;i<SIZE;i++) {
			cp=&codebook(i*DIM);
			S=sqr(xp[0]-cp[0]);
			for (j=1;j<DIM;j++) {
				S+=sqr(xp[j]-cp[j]);
				if (S>=MinS) goto sune2;
			}
			MinS=S;
			MinIndex=i;
sune2:		i=i;
		}
		dist(MinIndex)+=MinS;
		num(MinIndex)+=1;
	}
	dist=10*log10(dist*length(dist)/sum(dist));
	if (VERBOSE) cout << endl << "Distortion contribution: " << dist << endl ;
	if (VERBOSE) cout << "Num spread: " << num/DB.length()*100 << " %" << endl << endl ;
	if (min(dist)<-30) {
		cout << "Points without entries! Retraining" << endl ;
		j=min_index(dist);
		i=max_index(num);
		codebook.replace_mid(j*DIM,codebook.mid(i*DIM,DIM));
		goto res;
	}

	mat	cb(DIM,SIZE);
	for (i=0;i<SIZE;i++) {
		cb.set_col(i,codebook.mid(i*DIM,DIM));
	}
	return cb;
}
  


} // namespace itpp


syntax highlighted by Code2HTML, v. 0.9.1