/** 
* @file KernelExp.cpp
* @brief Interface for the popular exponential mercer kernels
* @author Erik Rodner
* @date 10/24/2007

*/

#include <iostream>

#include <math.h>
#include "KernelExp.h"

using namespace OBJREC;

using namespace std;
using namespace NICE;



KernelExp::KernelExp( double loggamma, double logsv, double _noise )
{
    gammasq = exp(2*loggamma);
	sv = exp(2*logsv);
	noise = _noise;
}

KernelExp::KernelExp ( const KernelExp & src )
{
	gammasq = src.gammasq;
	sv = src.sv;
	noise = src.noise;
}

KernelExp::~KernelExp()
{
}

	
double KernelExp::K (const NICE::Vector & x, const NICE::Vector & y) const
{
	if ( x.size() != y.size() ) 
		fthrow(Exception, "Vector dimensions do not match!");

	double dist = 0.0;
	for ( size_t i = 0 ; i < x.size() ; i++ )
	{
		double d = x[i] - y[i];
		dist += d*d;
	}
    return sv * exp( - dist / gammasq );
}

void KernelExp::updateKernelData ( KernelData *kernelData ) const
{
	const NICE::Matrix & quadraticDistances = kernelData->getCachedMatrix ( KernelData::QUADRATIC_DISTANCES );
	NICE::Matrix & kernelMatrix = kernelData->getKernelMatrix();
	kernelMatrix.resize ( quadraticDistances.rows(), quadraticDistances.cols() );

	for ( size_t i = 0 ; i < kernelMatrix.rows(); i++ )
		for ( size_t j = 0 ; j < kernelMatrix.cols(); j++ ) 
		{
			double dist = quadraticDistances(i,j);
			kernelMatrix(i,j) = sv * exp ( - dist / gammasq  );
		}
	
	kernelMatrix.addIdentity ( noise );
}
	
void KernelExp::calcKernelData ( const VVector & X, KernelData *kernelData ) const
{
	CachedQuadraticDistances::calcKernelData ( X, kernelData );
	if ( noise != 0.0 ) {
		cerr << "KernelExp: adding some noise" << endl;
		kernelData->getKernelMatrix().addIdentity(noise);
	}
}

void KernelExp::setParameters( const NICE::Vector & newParameters ) 
{
	gammasq = exp(2*newParameters[0]);
	sv = exp(2*newParameters[1]);
}

void KernelExp::getParameters( NICE::Vector & newParameters ) const
{
	newParameters.resize(2);
	newParameters[0] = 0.5 * log(gammasq);
	newParameters[1] = 0.5 * log(sv);
}

void KernelExp::getKernelJacobi ( size_t parameter, const NICE::Vector & parameters, const KernelData *kernelData, NICE::Matrix & jacobiMatrix ) const
{
	// double _gammasq = exp(2*parameters[0]); kernel jacobi is independent of gammasq
	double _sv = exp(2*parameters[1]);
	const NICE::Matrix & kernelMatrix = kernelData->getKernelMatrix();

	jacobiMatrix.resize ( kernelMatrix.rows(), kernelMatrix.cols() );
	if ( parameter == 0 ) {
		for ( size_t i = 0 ; i < kernelMatrix.rows(); i++ )
			for ( size_t j = 0 ; j < kernelMatrix.cols(); j++ ) 
			{
				double K = kernelMatrix(i,j);
				if ( almostZero(K) ) 
					jacobiMatrix(i,j) = - 2.0 * K * log(_sv);
				else
					jacobiMatrix(i,j) = - K * 2.0 * log (K / _sv);
			}
	} else if ( parameter == 1 ) {
		for ( size_t i = 0 ; i < kernelMatrix.rows(); i++ )
			for ( size_t j = 0 ; j < kernelMatrix.cols(); j++ ) 
			{
				double K = kernelMatrix(i,j);
				jacobiMatrix(i,j) = 2 * K;
			}

	} else{
		fthrow(Exception, "Wrong parameter index!");
	}
}

KernelExp *KernelExp::clone(void) const 
{
	return new KernelExp(*this);
}
