// file: $isip/class/algo/Kernel/kern_05.cc
// version: $Id: kern_05.cc 8475 2002-07-26 16:36:47Z hamaker $
//

// isip include files
//
#include "Kernel.h"

// method: compute
//
// arguments:
//  float& value: (output) return value of the kernel evaluation
//  const VectorFloat& x: (input) first operand
//  const VectorFloat& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the specified kernel on the input vectors
//
boolean Kernel::compute(float& value_a, const VectorFloat& x_a,
			const VectorFloat& y_a) {

  // check whether we need to initialize
  //
  if (!is_valid_d) {
    init();
  }

  // branch on the algorithm
  //
  if (algorithm_d == LINEAR) {
    return computeLinear(value_a, x_a, y_a);
  }

  else if (algorithm_d == POLYNOMIAL) {
    return computePolynomial(value_a, x_a, y_a);
  }  

  else if (algorithm_d == RBF) {
    return computeRBF(value_a, x_a, y_a);
  }  

  else if (algorithm_d == SIGMOID) {
    return computeSigmoid(value_a, x_a, y_a);
  }  
  
  // error: unknown algorithm
  //
  else {
    return Error::handle(name(), L"compute", ERR_UNKALG, __FILE__, __LINE__);
  }
}

// method: compute
//
// arguments:
//  double& value: (output) return value of the kernel evaluation
//  const VectorDouble& x: (input) first operand
//  const VectorDouble& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the specified kernel on the input vectors
//
boolean Kernel::compute(double& value_a, const VectorDouble& x_a,
			const VectorDouble& y_a) {

  // check whether we need to initialize
  //
  if (!is_valid_d) {
    init();
  }

  // branch on the algorithm
  //
  if (algorithm_d == LINEAR) {
    return computeLinear(value_a, x_a, y_a);
  }

  else if (algorithm_d == POLYNOMIAL) {
    return computePolynomial(value_a, x_a, y_a);
  }  

  else if (algorithm_d == RBF) {
    return computeRBF(value_a, x_a, y_a);
  }  

  else if (algorithm_d == SIGMOID) {
    return computeSigmoid(value_a, x_a, y_a);
  }  
  
  // error: unknown algorithm
  //
  else {
    return Error::handle(name(), L"compute", ERR_UNKALG, __FILE__, __LINE__);
  }
}

// method: computeLinear
//
// arguments:
//  float& value: (output) return value of the kernel evaluation
//  const VectorFloat& x: (input) first operand
//  const VectorFloat& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the linear kernel on the input vectors
//    value = (x . y)
//
boolean Kernel::computeLinear(float& value_a, const VectorFloat& x_a,
			      const VectorFloat& y_a) {

  // compute the final result
  //
  value_a = x_a.dotProduct(y_a);

  // exit gracefully
  //
  return true;
}

// method: computeLinear
//
// arguments:
//  double& value: (output) return value of the kernel evaluation
//  const VectorDouble& x: (input) first operand
//  const VectorDouble& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the linear kernel on the input vectors
//    value = (x . y)
//
boolean Kernel::computeLinear(double& value_a, const VectorDouble& x_a,
			      const VectorDouble& y_a) {

  // compute the final result
  //
  value_a = x_a.dotProduct(y_a);

  // exit gracefully
  //
  return true;
}

// method: computePolynomial
//
// arguments:
//  float& value: (output) return value of the kernel evaluation
//  const VectorFloat& x: (input) first operand
//  const VectorFloat& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the polynomial kernel on the input vectors
//    value = (x . y + 1) ^ p
// where 'p' is constants_d[0] in this class.
//
boolean Kernel::computePolynomial(float& value_a, const VectorFloat& x_a,
				  const VectorFloat& y_a) {

  // set the scale parameter
  //
  float p = constants_d(0);
  
  // compute the polynomial kernel
  //
  value_a = Integral::pow((x_a.dotProduct(y_a) + 1.0), p);

  // exit gracefully
  //
  return true;
}

// method: computePolynomial
//
// arguments:
//  double& value: (output) return value of the kernel evaluation
//  const VectorDouble& x: (input) first operand
//  const VectorDouble& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the polynomial kernel on the input vectors
//    value = (x . y + 1) ^ p
// where 'p' is constants_d[0] in this class.
//
boolean Kernel::computePolynomial(double& value_a, const VectorDouble& x_a,
				  const VectorDouble& y_a) {

  // set the scale parameter
  //
  double p = constants_d(0);
  
  // compute the polynomial kernel
  //
  value_a = Integral::pow((x_a.dotProduct(y_a) + 1.0), p);

  // exit gracefully
  //
  return true;
}

// method: computeRBF
//
// arguments:
//  float& value: (output) return value of the kernel evaluation
//  const VectorFloat& x: (input) first operand
//  const VectorFloat& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the radial basis function kernel on the input vectors
//    value = exp(-gamma * ((x . x) - 2 * (x . y) + (y . y))
// where 'gamma' is constants_d[0] in this class.
//
boolean Kernel::computeRBF(float& value_a, const VectorFloat& x_a,
			   const VectorFloat& y_a) {

  static VectorFloat tmp;
  tmp.sub(x_a, y_a);
  float value = tmp.sumSquare();
  
  // set the scale parameter
  //
  float gamma = constants_d(0);
  
  // compute the rbf kernel
  //
  //  value_a = Integral::exp(-gamma * (x_a.dotProduct(x_a) -
  //				    2 * x_a.dotProduct(y_a) +
  //				    y_a.dotProduct(y_a)));

  value_a = Integral::exp(-gamma * value);

  // exit gracefully
  //
  return true;
}

// method: computeRBF
//
// arguments:
//  double& value: (output) return value of the kernel evaluation
//  const VectorDouble& x: (input) first operand
//  const VectorDouble& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the radial basis function kernel on the input vectors
//    value = exp(-gamma * ((x . x) - 2 * (x . y) + (y . y))
// where 'gamma' is constants_d[0] in this class.
//
boolean Kernel::computeRBF(double& value_a, const VectorDouble& x_a,
			   const VectorDouble& y_a) {

  static VectorDouble tmp;
  tmp.sub(x_a, y_a);
  double value = tmp.sumSquare();
  
  // set the scale parameter
  //
  double gamma = constants_d(0);
  
  // compute the rbf kernel
  //
  //  value_a = Integral::exp(-gamma * (x_a.dotProduct(x_a) -
  //				    2 * x_a.dotProduct(y_a) +
  //				    y_a.dotProduct(y_a)));

  value_a = Integral::exp(-gamma * value);

  // exit gracefully
  //
  return true;
}

// method: computeSigmoid
//
// arguments:
//  float& value: (output) return value of the kernel evaluation
//  const VectorFloat& x: (input) first operand
//  const VectorFloat& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the sigmoid kernel on the input vectors
//    value = tanh(kappa * (x . y) + delta)
// where 'kappa' is constants_d[0] and 'delta' is constants_d[1] in this
// class.
//
boolean Kernel::computeSigmoid(float& value_a, const VectorFloat& x_a,
			       const VectorFloat& y_a) {

  // set the scale parameter
  //
  float kappa = constants_d(0);
  float delta = constants_d(1);
  
  // compute the sigmoid kernel
  //
  value_a = Integral::tanh(kappa * x_a.dotProduct(y_a) + delta);

  // exit gracefully
  //
  return true;
}

// method: computeSigmoid
//
// arguments:
//  double& value: (output) return value of the kernel evaluation
//  const VectorDouble& x: (input) first operand
//  const VectorDouble& y: (input) second operand
//
// return: a boolean value indicating status
//
// this method applies the sigmoid kernel on the input vectors
//    value = tanh(kappa * (x . y) + delta)
// where 'kappa' is constants_d[0] and 'delta' is constants_d[1] in this
// class.
//
boolean Kernel::computeSigmoid(double& value_a, const VectorDouble& x_a,
			       const VectorDouble& y_a) {

  // set the scale parameter
  //
  double kappa = constants_d(0);
  double delta = constants_d(1);
  
  // compute the sigmoid kernel
  //
  value_a = Integral::tanh(kappa * x_a.dotProduct(y_a) + delta);

  // exit gracefully
  //
  return true;
}
