Shark Machine Learning Library
  • About Shark
  • Sourceforge
    • Project Summary
    • Downloads
    • Subversion Repository
  • Getting Started
  • Tutorials
  • FAQ
  • Main Modules
    • ReClaM
    • EALib
    • MOO-EALib
    • Fuzzy
  • Tools
    • Mixture
    • Array
    • Rng
    • LinAlg
    • FileUtil
  • Main Page
  • Related Pages
  • Classes

KernelFunction.cpp

Go to the documentation of this file.
00001 //===========================================================================
00041 //===========================================================================
00042 
00043 
00044 #include <SharkDefs.h>
00045 #include <ReClaM/KernelFunction.h>
00046 #include <math.h>
00047 
00048 
00050 
00051 
00052 KernelFunction::KernelFunction()
00053 {
00054 }
00055 
00056 KernelFunction::~KernelFunction()
00057 {
00058 }
00059 
00060 
00061 double KernelFunction::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00062 {
00063     SIZE_CHECK(x1.ndim() == 1);
00064     SIZE_CHECK(x2.ndim() == 1);
00065     SIZE_CHECK(x1.nelem() == x2.nelem());
00066     KernelFunction* pT = const_cast<KernelFunction*>(this);
00067 
00068     int i, ic = getParameterDimension();
00069     double temp;
00070     double ret = eval(x1, x2);
00071     derivative.resize(ic, false);
00072     for (i = 0; i < ic; i++)
00073     {
00074         temp = getParameter(i);
00075         pT->setParameter(i, temp + epsilon);
00076         derivative(i) = (eval(x1, x2) - ret) / epsilon;
00077         pT->setParameter(i, temp);
00078     }
00079     return ret;
00080 }
00081 
00082 void KernelFunction::model(const Array<double>& input, Array<double> &output)
00083 {
00084     output.resize(1, false);
00085     output(0) = eval(input[0], input[1]);
00086 }
00087 
00088 void KernelFunction::modelDerivative(const Array<double>& input, Array<double>& derivative)
00089 {
00090     evalDerivative(input[0], input[1], derivative);
00091 }
00092 
00093 void KernelFunction::modelDerivative(const Array<double>& input, Array<double> &output, Array<double>& derivative)
00094 {
00095     output.resize(1, false);
00096     output(0) = evalDerivative(input[0], input[1], derivative);
00097 }
00098 
00099 
00101 
00102 
00103 LinearKernel::LinearKernel()
00104 {
00105     parameter.resize(0, false);
00106 }
00107 
00108 LinearKernel::~LinearKernel()
00109 {
00110 }
00111 
00112 
00113 double LinearKernel::eval(const Array<double>& x1, const Array<double>& x2) const
00114 {
00115     unsigned int i, ic = x1.nelem();
00116     SIZE_CHECK(x1.ndim() == 1);
00117     SIZE_CHECK(x2.ndim() == 1);
00118     SIZE_CHECK(x2.nelem() == ic);
00119 
00120     double ret = 0.0;
00121     for (i = 0; i < ic; i++) ret += x1(i) * x2(i);
00122     return ret;
00123 }
00124 
00125 double LinearKernel::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00126 {
00127     derivative.resize(0, false);
00128     return eval(x1, x2);
00129 }
00130 
00131 
00133 
00134 
00135 PolynomialKernel::PolynomialKernel(int degree, double offset)
00136 {
00137     parameter.resize(2, false);
00138     parameter(0) = degree;
00139     parameter(1) = offset;
00140 }
00141 
00142 PolynomialKernel::~PolynomialKernel()
00143 {
00144 }
00145 
00146 
00147 double PolynomialKernel::eval(const Array<double>& x1, const Array<double>& x2) const
00148 {
00149     unsigned int i, ic = x1.nelem();
00150     SIZE_CHECK(x1.ndim() == 1);
00151     SIZE_CHECK(x2.ndim() == 1);
00152     SIZE_CHECK(x2.nelem() == ic);
00153 
00154     double dot = 0.0;
00155     for (i = 0; i < ic; i++) dot += x1(i) * x2(i);
00156     return pow(dot + parameter(1), parameter(0));
00157 }
00158 
00159 void PolynomialKernel::setParameter(unsigned int index, double value)
00160 {
00161     if (index == 0)
00162     {
00163         parameter(0) = (int)value;
00164         if (parameter(0) < 1.0) throw SHARKEXCEPTION("[PolynomialKernel::setParameter] invalid degree");
00165     }
00166     else if (index == 1)
00167     {
00168         parameter(1) = value;
00169     }
00170     else throw SHARKEXCEPTION("[PolynomialKernel::setParameter] invalid parameter index");
00171 }
00172 
00173 bool PolynomialKernel::isFeasible()
00174 {
00175     return (parameter(0) > 0.0 && (int)parameter(0) == parameter(0));
00176 }
00177 
00178 
00180 
00181 
00182 RBFKernel::RBFKernel(double gamma)
00183 {
00184     parameter.resize(1, false);
00185     parameter(0) = gamma;
00186 }
00187 
00188 RBFKernel::~RBFKernel()
00189 {
00190 }
00191 
00192 
00193 double RBFKernel::eval(const Array<double>& x1, const Array<double>& x2) const
00194 {
00195     unsigned int i, ic = x1.nelem();
00196     SIZE_CHECK(x1.ndim() == 1);
00197     SIZE_CHECK(x2.ndim() == 1);
00198     SIZE_CHECK(x2.nelem() == ic);
00199 
00200     double tmp, dist2 = 0.0;
00201     for (i = 0; i < ic; i++)
00202     {
00203         tmp = x1(i) - x2(i);
00204         dist2 += tmp * tmp;
00205     }
00206     return exp(-parameter(0) * dist2);
00207 }
00208 
00209 double RBFKernel::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00210 {
00211     unsigned int i, ic = x1.nelem();
00212     SIZE_CHECK(x1.ndim() == 1);
00213     SIZE_CHECK(x2.ndim() == 1);
00214     SIZE_CHECK(x2.nelem() == ic);
00215 
00216     derivative.resize(1, false);
00217     double tmp, dist2 = 0.0;
00218     for (i = 0; i < ic; i++)
00219     {
00220         tmp = x1(i) - x2(i);
00221         dist2 += tmp * tmp;
00222     }
00223     double ret = exp(-parameter(0) * dist2);
00224     derivative(0) = -dist2 * ret;
00225     return ret;
00226 }
00227 
00228 bool RBFKernel::isFeasible()
00229 {
00230     return (parameter(0) > 0.0);
00231 }
00232 
00233 double RBFKernel::getSigma()
00234 {
00235     return sqrt(.5 / getParameter(0));
00236 };
00237 
00238 void RBFKernel::setSigma(double sigma)
00239 {
00240     setParameter(0, .5 / (sigma * sigma));
00241 }
00242 
00243 
00245 
00246 
00247 NormalizedRBFKernel::NormalizedRBFKernel()
00248 {
00249     parameter.resize(1, false);
00250     parameter = 1.;
00251 }
00252 
00253 NormalizedRBFKernel::NormalizedRBFKernel(double s)
00254 {
00255     parameter.resize(1, false);
00256     parameter = s;
00257 }
00258 
00259 
00260 void NormalizedRBFKernel::setSigma(double s)
00261 {
00262     setParameter(0, s);
00263 }
00264 
00265 double NormalizedRBFKernel::eval(const Array<double> &x,  const Array<double> &z) const
00266 {
00267     double sum = 0;
00268     double sigma = parameter(0);
00269 
00270     for (unsigned i = 0; i < x.dim(0); i++) sum += (x(i) - z(i)) * (x(i) - z(i));
00271     return exp(-sum / (2*sigma*sigma)) / (sigma * Sqrt2PI);
00272 }
00273 
00274 double NormalizedRBFKernel::evalDerivative(const Array<double> &x,  const Array<double> &z, Array<double>& derivative) const
00275 {
00276     double sum = 0;
00277     double sigma = parameter(0);
00278 
00279     derivative.resize(1, false);
00280 
00281     for (unsigned i = 0; i < x.dim(0); i++) sum += (x(i) - z(i)) * (x(i) - z(i));
00282     derivative(0) = (-exp(-sum / (2 * sigma * sigma)) + exp(-sum / (2 * sigma * sigma)) * sum / (sigma * sigma)) / (sigma * sigma * sqrt(2. * M_PI));
00283     return exp(-sum / (2*sigma*sigma)) / (sigma * Sqrt2PI);
00284 }
00285 
00286 
00288 
00289 
00290 NormalizedKernel::NormalizedKernel(KernelFunction* base)
00291 {
00292     baseKernel = base;
00293     int i, ic = baseKernel->getParameterDimension();
00294     parameter.resize(ic, false);
00295     for (i = 0; i < ic; i++) setParameter(i, baseKernel->getParameter(i));
00296 }
00297 
00298 NormalizedKernel::~NormalizedKernel()
00299 {}
00300 
00301 
00302 void NormalizedKernel::setParameter(unsigned int index, double value)
00303 {
00304     parameter(index) = value;
00305     baseKernel->setParameter(index, value);
00306 }
00307 
00308 double NormalizedKernel::eval(const Array<double>& x1, const Array<double>& x2) const
00309 {
00310     return baseKernel->eval(x1, x2) / sqrt(baseKernel->eval(x1, x1) * baseKernel->eval(x2, x2));
00311 }
00312 
00313 double NormalizedKernel::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00314 {
00315     int i, ic = getParameterDimension();
00316     Array<double> der11(ic);
00317     Array<double> der12(ic);
00318     Array<double> der22(ic);
00319 
00320     // compute the single kernel values with derivatives
00321     double k11 = baseKernel->evalDerivative(x1, x1, der11);
00322     double k12 = baseKernel->evalDerivative(x1, x2, der12);
00323     double k22 = baseKernel->evalDerivative(x2, x2, der22);
00324     double s = sqrt(k11 * k22);
00325 
00326     // compute the derivative using the quotient rule
00327     derivative.resize(ic, false);
00328     for (i = 0; i < ic; i++)
00329     {
00330         derivative(i) = (der12(i) * k11 * k22 - 0.5 * k12 * (k11 * der22(i) + der11(i) * k22)) / (s * k11 * k22);
00331     }
00332 
00333     // return the normalized kernel value
00334     return k12 / s;
00335 }
00336 
00337 bool NormalizedKernel::isFeasible()
00338 {
00339     return baseKernel->isFeasible();
00340 }
00341 
00342 
00344 
00345 
00346 WeightedSumKernel::WeightedSumKernel(const std::vector<KernelFunction*>& base)
00347 {
00348     int i, ic = base.size();
00349     if (ic == 0) throw SHARKEXCEPTION("[WeightedSumKernel] There must be at least one sub-kernel.");
00350     int j, jc;
00351     int p, pc = ic - 1;
00352     baseKernel.resize(ic);
00353     weight.resize(ic);
00354     for (i = 0; i < ic; i++)
00355     {
00356         pc += base[i]->getParameterDimension();
00357         baseKernel[i] = base[i];
00358         weight[i] = 1.0;
00359     }
00360     weightsum = ic;
00361     parameter.resize(pc, false);
00362 
00363     p = 0;
00364     for (i = 0; i < ic - 1; i++)
00365     {
00366         parameter(p) = 0.0;
00367         p++;
00368     }
00369     for (i = 0; i < ic; i++)
00370     {
00371         jc = base[i]->getParameterDimension();
00372         for (j = 0; j < jc; j++)
00373         {
00374             parameter(p) = base[i]->getParameter(j);
00375             p++;
00376         }
00377     }
00378 }
00379 
00380 WeightedSumKernel::~WeightedSumKernel()
00381 {
00382 }
00383 
00384 
00385 void WeightedSumKernel::setParameter(unsigned int index, double value)
00386 {
00387     parameter(index) = value;
00388 
00389     unsigned int ic = baseKernel.size();
00390     if (index < ic - 1)
00391     {
00392         // compute the weights
00393         unsigned int i;
00394         double a;
00395         weight[0] = 1.0;
00396         weightsum = 1.0;
00397         for (i = 0; i < ic - 1; i++)
00398         {
00399             a = exp(parameter(i));
00400             weight[i+1] = a;
00401             weightsum += a;
00402         }
00403     }
00404     else
00405     {
00406         // redirect the parameter to the corresponding sub-kernel
00407         index -= ic - 1;
00408         unsigned int i, jc;
00409         for (i = 0; i < ic; i++)
00410         {
00411             jc = baseKernel[i]->getParameterDimension();
00412             if (index < jc)
00413             {
00414                 baseKernel[i]->setParameter(index, value);
00415                 break;
00416             }
00417             else index -= jc;
00418         }
00419     }
00420 }
00421 
00422 double WeightedSumKernel::eval(const Array<double>& x1, const Array<double>& x2) const
00423 {
00424     double k = 0.0;
00425     int i, ic = baseKernel.size();
00426     for (i = 0; i < ic; i++) k += weight[i] * baseKernel[i]->eval(x1, x2);
00427     return k / weightsum;
00428 }
00429 
00430 double WeightedSumKernel::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00431 {
00432     derivative.resize(getParameterDimension(), false);
00433 
00434     int i, ic = baseKernel.size();
00435     std::vector<double> k_result(ic);
00436     Array<double> der;
00437     double k = 0.0;
00438     int p = ic - 1;
00439     int j, jc;
00440     for (i = 0; i < ic; i++)
00441     {
00442         k_result[i] = baseKernel[i]->evalDerivative(x1, x2, der);
00443         k += weight[i] * k_result[i];
00444 
00445         // compute the derivatives w.r.t. the parameters of the sub kernels
00446         jc = baseKernel[i]->getParameterDimension();
00447         for (j = 0; j < jc; j++)
00448         {
00449             derivative(p) = weight[i] * der(j) / weightsum;
00450             p++;
00451         }
00452     }
00453 
00454     // compute the derivatives w.r.t. the kernel weights
00455     for (i = 0; i < ic - 1; i++)
00456     {
00457         derivative(i) = weight[i+1] * (k_result[i+1] - k / weightsum) / weightsum;
00458     }
00459 
00460     return k / weightsum;
00461 }
00462 
00463 bool WeightedSumKernel::isFeasible()
00464 {
00465     int i, ic = baseKernel.size();
00466     for (i = 0; i < ic; i++)
00467     {
00468         if (! baseKernel[i]->isFeasible()) return false;
00469     }
00470     return true;
00471 }
00472 
00473 
00475 
00476 
00477 WeightedSumKernel2::WeightedSumKernel2(const std::vector<KernelFunction*>& base)
00478 {
00479     int i, ic = base.size();
00480     if (ic == 0) throw SHARKEXCEPTION("[WeightedSumKernel2] There must be at least one sub-kernel.");
00481     int j, jc;
00482     int p, pc = ic;
00483     baseKernel.resize(ic);
00484     weight.resize(ic);
00485     for (i = 0; i < ic; i++)
00486     {
00487         pc += base[i]->getParameterDimension();
00488         baseKernel[i] = base[i];
00489         weight[i] = 1.0;
00490     }
00491     weightsum = ic;
00492     parameter.resize(pc, false);
00493 
00494     p = 0;
00495     for (i = 0; i < ic; i++)
00496     {
00497         parameter(p) = 0.0;
00498         p++;
00499     }
00500     for (i = 0; i < ic; i++)
00501     {
00502         jc = base[i]->getParameterDimension();
00503         for (j = 0; j < jc; j++)
00504         {
00505             parameter(p) = base[i]->getParameter(j);
00506             p++;
00507         }
00508     }
00509 }
00510 
00511 WeightedSumKernel2::~WeightedSumKernel2()
00512 {
00513 }
00514 
00515 
00516 void WeightedSumKernel2::setParameter(unsigned int index, double value)
00517 {
00518     parameter(index) = value;
00519 
00520     unsigned int ic = baseKernel.size();
00521     if (index < ic)
00522     {
00523         // compute the weights
00524         unsigned int i;
00525         double a;
00526         weightsum = 1.0;
00527         for (i = 0; i < ic; i++)
00528         {
00529             a = exp(parameter(i));
00530             weight[i] = a;
00531             weightsum += a;
00532         }
00533     }
00534     else
00535     {
00536         // redirect the parameter to the corresponding sub-kernel
00537         index -= ic;
00538         unsigned int i, jc;
00539         for (i = 0; i < ic; i++)
00540         {
00541             jc = baseKernel[i]->getParameterDimension();
00542             if (index < jc)
00543             {
00544                 baseKernel[i]->setParameter(index, value);
00545                 break;
00546             }
00547             else index -= jc;
00548         }
00549     }
00550 }
00551 
00552 double WeightedSumKernel2::eval(const Array<double>& x1, const Array<double>& x2) const
00553 {
00554     double k = 0.0;
00555     int i, ic = baseKernel.size();
00556     for (i = 0; i < ic; i++) k += weight[i] * baseKernel[i]->eval(x1, x2);
00557     return k / weightsum;
00558 }
00559 
00560 double WeightedSumKernel2::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00561 {
00562     derivative.resize(getParameterDimension(), false);
00563 
00564     int i, ic = baseKernel.size();
00565     std::vector<double> k_result(ic);
00566     Array<double> der;
00567     double k = 0.0;
00568     int p = ic;
00569     int j, jc;
00570     for (i = 0; i < ic; i++)
00571     {
00572         k_result[i] = baseKernel[i]->evalDerivative(x1, x2, der);
00573         k += weight[i] * k_result[i];
00574 
00575         // compute the derivatives w.r.t. the parameters of the sub kernels
00576         jc = baseKernel[i]->getParameterDimension();
00577         for (j = 0; j < jc; j++)
00578         {
00579             derivative(p) = weight[i] * der(j) / weightsum;
00580             p++;
00581         }
00582     }
00583 
00584     // compute the derivatives w.r.t. the kernel weights
00585     for (i = 0; i < ic; i++)
00586     {
00587         derivative(i) = weight[i] * (k_result[i] - k / weightsum) / weightsum;
00588     }
00589 
00590     return k / weightsum;
00591 }
00592 
00593 bool WeightedSumKernel2::isFeasible()
00594 {
00595     int i, ic = baseKernel.size();
00596     for (i = 0; i < ic; i++)
00597     {
00598         if (! baseKernel[i]->isFeasible()) return false;
00599     }
00600     return true;
00601 }
00602 
00603 
00605 
00606 
00607 PrototypeKernel::PrototypeKernel(unsigned int setsize, unsigned int prototypeDim)
00608 {
00609     if (prototypeDim == 0) prototypeDim = setsize;
00610 
00611     m_setsize = setsize;
00612     m_prototypeDim = prototypeDim;
00613     unsigned int i, total = setsize * prototypeDim;
00614     parameter.resize(total, false);
00615     parameter = 0.0;
00616     for (i=0; i<Shark::min(setsize, prototypeDim); i++) parameter(i * (prototypeDim + 1)) = 1.0;
00617 }
00618 
00619 PrototypeKernel::~PrototypeKernel()
00620 {
00621 }
00622 
00623 
00624 double PrototypeKernel::eval(const Array<double>& x1, const Array<double>& x2) const
00625 {
00626     unsigned int i = (unsigned int)floor(x1(0));
00627     unsigned int j = (unsigned int)floor(x2(0));
00628     RANGE_CHECK(i < m_setsize);
00629     RANGE_CHECK(j < m_setsize);
00630 
00631     double ret = 0.0;
00632     unsigned int p;
00633     for (p=0; p<m_prototypeDim; p++) ret += parameter(m_prototypeDim*i+p) * parameter(m_prototypeDim*j+p);
00634     return ret;
00635 }
00636 
00637 double PrototypeKernel::evalDerivative(const Array<double>& x1, const Array<double>& x2, Array<double>& derivative) const
00638 {
00639     unsigned int i = (unsigned int)floor(x1(0));
00640     unsigned int j = (unsigned int)floor(x2(0));
00641     RANGE_CHECK(i < m_setsize);
00642     RANGE_CHECK(j < m_setsize);
00643 
00644     derivative.resize(m_setsize * m_prototypeDim);
00645     derivative = 0.0;
00646 
00647     double ret = 0.0;
00648     unsigned int p;
00649     for (p=0; p<m_prototypeDim; p++)
00650     {
00651         unsigned int ii = m_prototypeDim * i + p;
00652         unsigned int jj = m_prototypeDim * j + p;
00653         double pi = parameter(ii);
00654         double pj = parameter(jj);
00655         ret += pi * pj;
00656         derivative(ii) += pj;
00657         derivative(jj) += pi;
00658     }
00659     return ret;
00660 }
00661 
00662 void PrototypeKernel::getPrototype(unsigned int n, Array<double>& vec)
00663 {
00664     RANGE_CHECK(n < m_setsize);
00665 
00666     vec.resize(m_prototypeDim, false);
00667     unsigned int p;
00668     for (p=0; p<m_prototypeDim; p++) vec(p) = parameter(m_prototypeDim * n + p);
00669 }
00670 
00671 void PrototypeKernel::setPrototype(unsigned int n, const Array<double>& vec)
00672 {
00673     SIZE_CHECK(vec.ndim() == 1);
00674     SIZE_CHECK(vec.dim(0) == m_prototypeDim);
00675     RANGE_CHECK(n < m_setsize);
00676 
00677     unsigned int p;
00678     for (p=0; p<m_prototypeDim; p++) parameter(m_prototypeDim * n + p) = vec(p);
00679 }
00680 
00681 
00683 
00684 
00685 JointKernelFunction::JointKernelFunction(KernelFunction& inputkernel, KernelFunction& labelkernel)
00686 : m_inputkernel(inputkernel)
00687 , m_labelkernel(labelkernel)
00688 {
00689     parameter.resize(inputkernel.getParameterDimension() + labelkernel.getParameterDimension(), false);
00690     unsigned int i;
00691     for (i=0; i<inputkernel.getParameterDimension(); i++)
00692     {
00693         parameter(i) = inputkernel.getParameter(i);
00694     }
00695     for (i=0; i<labelkernel.getParameterDimension(); i++)
00696     {
00697         parameter(inputkernel.getParameterDimension() + i) = labelkernel.getParameter(i);
00698     }
00699 }
00700 
00701 JointKernelFunction::~JointKernelFunction()
00702 {
00703 }
00704 
00705 
00706 double JointKernelFunction::eval(const Array<double>& x1, const Array<double>& y1, const Array<double>& x2, const Array<double>& y2) const
00707 {
00708     double k1 = m_inputkernel.eval(x1, x2);
00709     double k2 = m_labelkernel.eval(y1, y2);
00710     return k1 * k2;
00711 }
00712 
00713 double JointKernelFunction::evalDerivative(const Array<double>& x1, const Array<double>& y1, const Array<double>& x2, const Array<double>& y2, Array<double>& derivative) const
00714 {
00715     Array<double> der1(m_inputkernel.getParameterDimension());
00716     Array<double> der2(m_labelkernel.getParameterDimension());
00717     double k1 = m_inputkernel.evalDerivative(x1, x2, der1);
00718     double k2 = m_labelkernel.evalDerivative(y1, y2, der2);
00719 
00720     unsigned int i;
00721     derivative.resize(getParameterDimension(), false);
00722     for (i=0; i<m_inputkernel.getParameterDimension(); i++)
00723         derivative(i) = k2 * der1(i);
00724     for (i=0; i<m_labelkernel.getParameterDimension(); i++)
00725         derivative(m_inputkernel.getParameterDimension() + i) = k1 * der2(i);
00726 
00727     return k1 * k2;
00728 }
00729 
00730 void JointKernelFunction::model(const Array<double>& input, Array<double> &output)
00731 {
00732     throw SHARKEXCEPTION("[JointKernelFunction::model] never call this function - it is undefined.");
00733 }
00734 
00735 void JointKernelFunction::setParameter(unsigned int index, double value)
00736 {
00737     Model::setParameter(index, value);
00738     if (index < m_inputkernel.getParameterDimension()) m_inputkernel.setParameter(index, value);
00739     else m_labelkernel.setParameter(index - m_inputkernel.getParameterDimension(), value);
00740 }