bool MLP::calcGradient(const dmatrix& inputs,
                         const ivector& ids,
                         dvector& grad) {

    if (inputs.rows() != ids.size()) {
      setStatusString("Number of vectors not consistent with number of ids");
      return false;
    }

    dvector tmp;
    int i;
    double tmpError;

    totalError = 0;
    calcGradient(inputs.getRow(0),ids.at(0),grad);
    computeActualError(ids.at(0),totalError);

    for (i=1;i<inputs.rows();++i) {
      calcGradient(inputs.getRow(i),ids.at(i),tmp);
      computeActualError(ids.at(i),tmpError);
      grad.add(tmp);
      totalError+=tmpError;
    }

    return true;
  }
  bool modHubertStat::apply(const std::vector<dmatrix>& clusteredData,
                            double& index, const dmatrix& centroids) const {
    index =0.0;
    int nbClusters=clusteredData.size();
    l2Distance<double> dist;
    int nbPoints=0;
    int i,j,k,l;
    for (i=0; i<nbClusters; i++) {
      for (j=0; j<clusteredData[i].rows(); j++) { 
        // count the number of points
        nbPoints++;
        // for all other clusters and all points in these clusters
        for (k=i+1; k<nbClusters; k++) {
          for (l=0; l<clusteredData[k].rows(); l++) {
            index+=dist.apply(clusteredData[i].getRow(j),
                              clusteredData[k].getRow(l))*
              dist.apply(centroids.getRow(i),centroids.getRow(k));
          }
        }
      }
    }
    double m=0.5*(nbPoints*(nbPoints-1));
    index=index/m;

    return true;
  }
 double clusteringValidity::getMaximumDistance(const dmatrix& m1,
                                               const dmatrix& m2) const {
   int i,j;
   dmatrix distances(m1.rows(),m2.rows());
   l2Distance<double> dist;
   for (i=0; i<m1.rows(); i++) {
     for (j=0; j<m2.rows(); j++) {
       distances[i][j]=dist.apply(m1.getRow(i),m2.getRow(j));
     }
   }
   return distances.maximum();
 }
 double clusteringValidity::getAverageDistance(const dmatrix& m1,
                                               const dmatrix& m2) const {
   double distance=0.0;
   int i,j;
   l2Distance<double> dist;
   for (i=0; i<m1.rows(); i++) {
     for (j=0; j<m2.rows(); j++) {
       distance+=dist.apply(m1.getRow(i),m2.getRow(j));
     }
   }
   distance=distance/((double)m1.rows()*(double)m2.rows());
   return distance;
 }
  /**
   * Adds an object to this classifier. The id is determined automatically
   * and returned in the parameter.
   */
  bool shClassifier::trainObject(const dmatrix& input, int& id) {
    id=0;
    for (std::map<int,int>::const_iterator i=rIdMap.begin(); i != rIdMap.end(); i++) {
      if (i->second >= id) {
        id=i->second+1;
      }
    }
    idMap[id]=nClasses;
    rIdMap[nClasses]=id;
    nClasses++;

    const parameters& par=getParameters();

    // do not touch min and max
    if (getParameters().binVector.size() > 0) {
      models.push_back(new sparseHistogram(getParameters().binVector,
                                           par.minimum,par.maximum));
    } else {
      models.push_back(new sparseHistogram(getParameters().numberOfBins,
                                           par.minimum,par.maximum));
    }
    // fill histograms
    int sum=0;
    for (int j=0; j<input.rows(); j++) {
      models[nClasses-1]->add(input.getRow(j));
      sum++;
    }
    models[nClasses-1]->divide(static_cast<float>(sum));

    defineOutputTemplate();

    return true;
  }
 double clusteringValidity::getCentroidDistance(const dmatrix& m1,
                                                const dmatrix& m2) const {
   l2Distance<double> dist;
   int i;
   dvector a(m1.columns());
   dvector b(m2.columns());
   for (i=0; i<m1.rows();i++) {
     a.add(m1.getRow(i));
   }
   a.divide(m1.rows());
   for (i=0; i<m2.rows();i++) {
     b.add(m2.getRow(i));
   }
   b.divide(m2.rows());
   return dist.apply(a,b);
 }
 double clusteringValidity::getAverageToCentroidDiameter(const dmatrix& m1) const {
   dvector a(m1.columns());
   int i,j;
   l2Distance<double> dist;
   double distance=0.0;
   for (i=0; i<m1.rows(); i++) {
     a.add(m1.getRow(i));
   }
   a.divide(m1.rows());
   for (j=0; j< m1.rows(); j++) {
     distance+=dist.apply(a,m1.getRow(j));
   }
   if (m1.rows()>0) {
     return (2*distance/(double)m1.rows());
   } else {
     return 2*distance;
   }
   
 }
 double clusteringValidity::getStandardDiameter(const dmatrix& m1) const {
   dmatrix distances(m1.rows(),m1.rows());
   int j,k;
   l2Distance<double> dist;
   for (j=0; j<m1.rows(); j++) {
     for (k=j+1; k<m1.rows(); k++) {
       distances[j][k]=dist.apply(m1.getRow(j),
                                  m1.getRow(k));
     }
   }
   return distances.maximum();  
 }
  // Calls the same method of the superclass.
  bool shClassifier::train(const dmatrix& input, const ivector& ids) {

    buildIdMaps(ids);

    boundsFunctor<double> bounds;
    const parameters& par=getParameters();

    dvector min,max;

    if (par.autoBounds) {
      bounds.boundsOfRows(input,min,max);
    } else {
      min=par.minimum;
      max=par.maximum;
    }

    _lti_debug("Binvector.size = " << par.binVector.size() << "\n");

    int i;

    // build one histogram per object
    models.resize(nClasses);
    for (i=0; i<nClasses; i++) {
      if (par.binVector.size() == min.size()) {
        models[i]=new sparseHistogram(par.binVector,min,max);
      } else {
        models[i]=new sparseHistogram(par.numberOfBins,min,max);
      }
    }

    ivector sum(nClasses);

    // fill histograms
    for (i=0; i<input.rows(); i++) {
      int id=idMap[ids.at(i)];
      models[id]->add(input.getRow(i));
      sum[id]++;
    }

    // normalize histograms
    for (i=0; i<nClasses; i++) {
      _lti_debug("Sum of " << i << " is " << sum.at(i) << "\n");
      if (sum.at(i) == 0) {
        delete models[i];
        models[i]=0;
      } else {
        models[i]->divide(static_cast<float>(sum.at(i)));
      }
    }
    defineOutputTemplate();
    return true;
  }
 bool daviesBouldinIndex::apply(const std::vector<dmatrix>& clusteredData,
                                double& index, 
                                const dmatrix& centroids) const {
   int nbClusters=clusteredData.size();
   int i,j;
   dvector interClusterDistance(nbClusters);
   l2Distance<double> dist;
   // compute the average inter cluster distance
   double distance;
   for (i=0; i<nbClusters; i++) {
     for (j=0; j<clusteredData[i].rows(); j++) {
       distance=dist.apply(clusteredData[i].getRow(j),
                           centroids.getRow(i));
       distance=distance*distance;
       interClusterDistance[i]+=distance;
     }
     if (clusteredData[i].rows()!=0) {
       interClusterDistance[i]=interClusterDistance[i]/
         (double)clusteredData[i].rows();}
     interClusterDistance[i]=sqrt(interClusterDistance[i]);
   }
   dmatrix indices(nbClusters,nbClusters);
   for (i=0; i<nbClusters; i++) {
     for (j=i+1; j<nbClusters; j++) {
       double distance=dist.apply(centroids.getRow(i),
                                  centroids.getRow(j));
       indices[i][j]=(interClusterDistance[i]+
                      interClusterDistance[j])/distance;
       indices[j][i]=indices[i][j];
     }
   }
   index=0.0;
   for (i=0; i<nbClusters; i++) {
     index+=indices.getRow(i).maximum();
   }
   index=index/(double)nbClusters;
  
   return true;
 }
 double clusteringValidity::getAverageInterpointDistance(const dmatrix& m1,
                                                         const dmatrix& m2) const {
   l2Distance<double> dist;
   int i;
   dvector a(m1.columns());
   dvector b(m2.columns());
   for (i=0; i<m1.rows();i++) {
     a.add(m1.getRow(i));
   }
   a.divide(m1.rows()); // centroid 1
   for (i=0; i<m2.rows();i++) {
     b.add(m2.getRow(i));
   }
   b.divide(m2.rows()); // centroid 2
   double distance=0.0;
   for (i=0; i<m1.rows(); i++) {
     distance+=dist.apply(m1.getRow(i),a);
   }
   for (i=0; i<m2.rows(); i++) {
     distance+=dist.apply(m2.getRow(i),b);
   }
   return (distance/(m1.rows()+m2.rows()));
 }
 double clusteringValidity::getAverageDiameter(const dmatrix& m1) const {
   double distance=0.0;
   int j,k;
   l2Distance<double> dist;
   for (j=0; j<m1.rows(); j++) {
     for (k=0; k<m1.rows(); k++) {
       distance+=dist.apply(m1.getRow(j),
                            m1.getRow(k));
     }
   }
   if (m1.rows()>1) {
     return (distance/((double)m1.rows()*
                       (double)(m1.rows()-1)));
   } else {
     return distance;
   }
 }
Exemple #13
0
  /*
   * compute the error of the given weights for the whole training set.
   */
  bool MLP::computeTotalError(const std::vector<dmatrix>& mWeights,
                              const dmatrix& inputs,
                              const ivector& ids,
                              double& totalError) const {

    if (ids.size() != inputs.rows()) {
      return false;
    }

    const parameters& param = getParameters();
    const int layers = param.hiddenUnits.size()+1;
    std::vector<dvector> uNet(layers),uOut(layers);
    int i;
    double tmp;
    totalError=0.0;
    for (i=0;i<ids.size();++i) {
      propagate(inputs.getRow(i),mWeights,uNet,uOut);
      computePatternError(ids.at(i),uOut.back(),tmp);
      totalError+=tmp;
    }

    return true;
  }
  bool dunnIndex::apply(const std::vector<dmatrix>& clusteredData,
                        double& index, const dmatrix& centroids) const {
    int nbClusters=clusteredData.size();
    double denominator=0.0;
    int i,j;
    l2Distance<double> dist;
    dvector diameters(nbClusters);
    parameters param;
    param=getParameters();
    // pointer to the function which implements the measure according to the 
    // parameters
    double (lti::clusteringValidity::*diamFunc)(const dmatrix&) const;
    
#ifdef _LTI_MSC_DOT_NET_2003
    // nasty bug in this version of the .NET compiler
#define QUALIFIER
#else 
#define QUALIFIER &lti::dunnIndex::
#endif

    switch (param.diameterMeasure) {
      case parameters::Standard:
        diamFunc=QUALIFIER getStandardDiameter;
        break;
      case parameters::Average:
        diamFunc=QUALIFIER getAverageDiameter;
        break;
      case parameters::Centroid:
        diamFunc=QUALIFIER getAverageToCentroidDiameter;
        break;
      default:
        diamFunc=QUALIFIER getStandardDiameter;
        setStatusString("Unknown diameterMeasure in clusteringValidity\n");
        return false;
    }
    // compute all diameters of all clusters
    for (i=0; i<nbClusters; i++) {
      diameters[i]=(this->*diamFunc)(clusteredData[i]);
    }
    denominator=diameters.maximum();
    // pointer to the function which calculates the distance of the functions
    // a pointer to a function is used, because the function will be called 
    // many times later
    double (lti::clusteringValidity::*distFunc)
      (const dmatrix&,const dmatrix&) const ;
    // set pointer to function which is set by the parameter distanceMeasure
    switch (param.distanceMeasure) {
      case parameters::Minimum:
        distFunc=QUALIFIER getMinimumDistance;
        break;
      case parameters::Maximum:
        distFunc=QUALIFIER getMaximumDistance;
        break;
      case parameters::Mean:
        distFunc=QUALIFIER getAverageDistance;
        break;
      case parameters::Centroids:
        distFunc=QUALIFIER getCentroidDistance;
        break;
      case parameters::Interpoint:
        distFunc=QUALIFIER getAverageInterpointDistance;
        break;
      default:
        distFunc=QUALIFIER getAverageDistance;
        setStatusString("Unknown distanceMeasure in clusteringValidity\n");
        return false;
    }
    // compute the distances of all clusters to each other
    int counter=0;
    dvector distanceVector(static_cast<int>(.5*(nbClusters*(nbClusters-1))));
    for (i=0; i<nbClusters; i++) {
      for (j=i+1; j<nbClusters; j++) {
        if (distFunc==QUALIFIER getCentroidDistance) {
          distanceVector[counter]=dist.apply(centroids.getRow(i),
                                             centroids.getRow(j));
        } else {
          distanceVector[counter]=(this->*distFunc)(clusteredData[i],
                                                    clusteredData[j]);
        }
        counter++;
      }
    }
    distanceVector.divide(denominator);
    index=distanceVector.minimum();

    return true;
  }
Exemple #15
0
  bool MLP::trainSteepestSequential(const dmatrix& data,
                                    const ivector& internalIds) {

    const parameters& param = getParameters();
    char buffer[256];
    bool abort = false;
    scramble<int> scrambler;
    int i,j,k;
    double tmpError;
    ivector idx;
    idx.resize(data.rows(),0,false,false);
    for (i=0;i<idx.size();++i) {
      idx.at(i)=i;
    }

    if (param.momentum > 0) {
      // with momentum
      dvector grad,delta(weights.size(),0.0);

      for (i=0; !abort && (i<param.maxNumberOfEpochs); ++i) {
        scrambler.apply(idx); // present the pattern in a random sequence
        totalError = 0;
        for (j=0;j<idx.size();++j) {
          k=idx.at(j);
          calcGradient(data.getRow(k),internalIds.at(k),grad);
          computeActualError(internalIds.at(k),tmpError);
          totalError+=tmpError;
          delta.addScaled(param.learnrate,grad,param.momentum,delta);
          weights.add(delta);
        }

        // update progress info object
        if (validProgressObject()) {
          sprintf(buffer,"Error=%f",totalError/errorNorm);
          getProgressObject().step(buffer);
          abort = abort || (totalError/errorNorm <= param.stopError);
          abort = abort || getProgressObject().breakRequested();
        }
      }
    } else {
      // without momentum
      ivector idx;
      idx.resize(data.rows(),0,false,false);
      dvector grad;

      int i,j,k;
      double tmpError;
      for (i=0;i<idx.size();++i) {
        idx.at(i)=i;
      }
      for (i=0; !abort && (i<param.maxNumberOfEpochs); ++i) {
        scrambler.apply(idx); // present the pattern in a random sequence
        totalError = 0;
        for (j=0;j<idx.size();++j) {
          k=idx.at(j);
          calcGradient(data.getRow(k),internalIds.at(k),grad);
          computeActualError(internalIds.at(k),tmpError);
          totalError+=tmpError;
          weights.addScaled(param.learnrate,grad);
        }

        // update progress info object
        if (validProgressObject()) {
          sprintf(buffer,"Error=%f",totalError/errorNorm);
          getProgressObject().step(buffer);
          abort = abort || (totalError/errorNorm <= param.stopError);
          abort = abort || getProgressObject().breakRequested();
        }
      }
    }
    return true;
  }
  bool SOFM2D::trainDot(const dmatrix& data) {

    bool b=true;

    int i,j,k,maxN;
    int startx, starty, stopx, stopy;
    kernel2D<double> facN;

    l2Distance<double> dist;
    l2Distance<double>::parameters dfp;
    dfp.rowWise=true;
    dist.setParameters(dfp);

    const parameters& param=getParameters();

    int step=0;
    int epoch;
    scramble<int> mix;
    ivector idx(data.rows());
    for (i=0; i<data.rows(); i++) {
      idx[i]=i;
    }

    dvector prod;
    dvector sum;
    int winner;

    //normalize grid
    dvector norms;
    b = b && dist.apply(grid,norms);
    for (i=0; i<grid.rows(); i++) {
      grid.getRow(i).divide(norms[i]);
    }

    // temp value needed for kernel init
    const double tfac=sqrt(-2*log(param.orderNeighborThresh));

    char buffer[256];
    bool abort=false;
    //ordering
    for (epoch=0; epoch<param.stepsOrdering; epoch++) {

      if (validProgressObject()) {
        sprintf(buffer,"ordering step %i",epoch);
        getProgressObject().step(buffer);
        abort = getProgressObject().breakRequested();
        }
      if (abort) return b;

      mix.apply(idx);
      for (i=0; i<idx.size(); i++, step++) {
        const dvector& curr = data.getRow(idx[i]);
        //find winner
        grid.multiply(curr, prod);
        winner=prod.getIndexOfMaximum();

        //find size and init neighborhood function
        maxN=static_cast<int>(sigma*tfac);
        getNeighborhoodKernel(maxN, facN);

        //find bounds
        if (winner%sizeX-maxN < 0) {
          startx=-winner%sizeX;
        } else {
          startx=-maxN;
        }
        if (winner%sizeX+maxN > sizeX) {
          stopx=sizeX-winner%sizeX;
        } else {
          stopx=maxN;
        }
        if (winner/sizeX-maxN < 0) {
          starty=-winner/sizeX;
        } else {
          starty=-maxN;
        }
        if (winner/sizeX+maxN > sizeY) {
          stopy=sizeY-winner/sizeX;
        } else {
          stopy=maxN;
        }
        for (j=starty; j<stopy; j++) {
          for (k=startx; k<stopx; k++) {
            if (facN.at(j,k)==0.) {
              continue;
            }
            dvector& winnerRow=grid[winner+j*sizeX+k];
            winnerRow.addScaled(lrOrder*facN.at(j,k), curr);
            winnerRow.divide(dist.apply(winnerRow));
          }
        }


        lrOrder-=lrOrderDelta;
        sigma-=sigmaDelta;
      }
    }

    // convergence training

    // neighborhood is fixed: calc matrix of factors.
    maxN=static_cast<int>(sigma*tfac);
    getNeighborhoodKernel(maxN, facN);

    double lrC=lrConvergeA/lrConvergeB;
    step=0;
    for (epoch=0; epoch<param.stepsConvergence; epoch++) {

      if (validProgressObject()) {
        sprintf(buffer,"convergence step %i",epoch);
        getProgressObject().step(buffer);
        abort = getProgressObject().breakRequested();
        }
      if (abort) return b;

      mix.apply(idx);
      for (i=0; i<idx.size(); i++, step++) {
        const dvector& curr = data.getRow(idx[i]);
        //find winner
        grid.multiply(curr, prod);
        winner=prod.getIndexOfMaximum();
        //find bounds
        if (winner%sizeX-maxN < 0) {
          startx=-winner%sizeX;
        } else {
          startx=-maxN;
        }
        if (winner%sizeX+maxN > sizeX) {
          stopx=sizeX-winner%sizeX;
        } else {
          stopx=maxN;
        }
        if (winner/sizeX-maxN < 0) {
          starty=-winner/sizeX;
        } else {
          starty=-maxN;
        }
        if (winner/sizeX+maxN > sizeY) {
          stopy=sizeY-winner/sizeX;
        } else {
          stopy=maxN;
        }
        for (j=starty; j<stopy; j++) {
          for (k=startx; k<stopx; k++) {
            if (facN.at(j,k)==0.) {
              continue;
            }
            dvector& winnerRow=grid[winner+j*sizeX+k];
            winnerRow.addScaled(lrC*facN.at(j,k), curr);
            winnerRow.divide(dist.apply(winnerRow));
          }
        }
        lrC=lrConvergeA/(step+lrConvergeB);
      }
    }

    return b;

  }
 bool normModHubertStat::apply(const std::vector<dmatrix>& 
                               clusteredData, double& index, 
                               const dmatrix& centroids) const {
   index =0.0;
   int nbClusters=clusteredData.size();
   l2Distance<double> dist;
   int nbPoints=0;
   int i,j,k,l;
   // count the points that are in clusteredData
   for (i=0; i<nbClusters; i++) {
     nbPoints+=clusteredData[i].rows();
   }
   // x is the distance matrix. It has in the i-th rows and j-th column
   // the distance between the i-th and j-th point
   // y is an other distance matrix. It has in the i-th row and j-th column
   // the distance of the centroids of the clusters they belong to.
   dmatrix x(nbPoints,nbPoints);
   dmatrix y(nbPoints,nbPoints);
   int row=0;
   int col=0;
   for (i=0; i<nbClusters; i++) {
     for (j=0; j<clusteredData[i].rows(); j++) {
       for (k=0; k<nbClusters; k++) {
         for (l=0; l<clusteredData[k].rows(); l++) {
           if (col>row) {
             y.at(row,col)=dist.apply(centroids.getRow(i),
                                      centroids.getRow(k));
             x.at(row,col)=dist.apply(clusteredData[i].getRow(j),
                                      clusteredData[k].getRow(l));
           }
           if (col<nbPoints-1) col++;
           else { 
             col=0;
             row++;
           }  
         }
       }
     }
   }
   double m=0.5*(nbPoints*(nbPoints-1));
   double meanX=x.sumOfElements()/m;
   double meanY=y.sumOfElements()/m;
   double tmp1=meanX*meanX;
   double tmp2=meanY*meanY;
   double varianzX=0.0;
   double varianzY=0.0;
   for (i=0; i<nbPoints; i++) {
     for (j=i+1; j<nbPoints; j++) {
       varianzX+=x[i][j]*x[i][j]-tmp1;
       varianzY+=y[i][j]*y[i][j]-tmp2;
     }
   }
   varianzX=varianzX/m;
   varianzY=varianzY/m;
   varianzX=sqrt(varianzX);
   varianzY=sqrt(varianzY);
   double varianz=varianzX*varianzY;
   for (i=0; i<nbPoints; i++) {
     for (j=i+1; j<nbPoints; j++) {
       index+=(x[i][j]-meanX)*(y[i][j]-meanY);
     }
   }
   index=index/(m*varianz); 
 
   return true;
 }
  bool SOFM2D::trainDist(const dmatrix& data) {

    bool b=true;
    int i,j,k,maxN;
    int startx, starty, stopx, stopy;
    kernel2D<double> facN;

    const parameters& param=getParameters();

    distanceFunctor<double>* dist = 0;
    if (param.metricType == parameters::L1) {
      dist = new l1Distance<double>();
    } else {
      dist = new l2Distance<double>();
    }
    distanceFunctor<double>::parameters dfp;
    dfp.rowWise=true;
    dist->setParameters(dfp);

    int step=0;
    int epoch;
    scramble<int> mix;
    ivector idx(data.rows());
    for (i=0; i<data.rows(); i++) {
      idx[i]=i;
    }

    dvector distances;
    dvector delta;
    int winner;

    char buffer[256];
    bool abort=false;

    // temp value needed for kernel init
    const double tfac=sqrt(-2*log(param.orderNeighborThresh));

    //ordering
    for (epoch=0; epoch<param.stepsOrdering; epoch++) {

      if (validProgressObject()) {
        sprintf(buffer,"ordering step %i",epoch);
        getProgressObject().step(buffer);
        abort = getProgressObject().breakRequested();
        }
      if (abort) return b;

      mix.apply(idx);
      for (i=0; i<idx.size(); i++, step++) {
        const dvector& curr = data.getRow(idx[i]);
        dist->apply(grid, curr, distances);
        winner=distances.getIndexOfMinimum();

        maxN=static_cast<int>(sigma*tfac);
        getNeighborhoodKernel(maxN, facN);

        //find bounds
        if (winner%sizeX-maxN < 0) {
          startx=-winner%sizeX;
        } else {
          startx=-maxN;
        }
        if (winner%sizeX+maxN > sizeX) {
          stopx=sizeX-winner%sizeX;
        } else {
          stopx=maxN;
        }
        if (winner/sizeX-maxN < 0) {
          starty=-winner/sizeX;
        } else {
          starty=-maxN;
        }
        if (winner/sizeX+maxN > sizeY) {
          stopy=sizeY-winner/sizeX;
        } else {
          stopy=maxN;
        }
        for (j=starty; j<stopy; j++) {
          for (k=startx; k<stopx; k++) {
            if (facN.at(j,k)==0.) {
              continue;
            }
            delta.subtract(curr, grid[winner+j*sizeX+k]);
            grid[winner+j*sizeX+k].addScaled(lrOrder*facN.at(j,k),delta);
          }
        }


        lrOrder-=lrOrderDelta;
        sigma-=sigmaDelta;
      }
    }

    // convergence training

    // neighborhood is fixed: calc matrix of factors.
    maxN=static_cast<int>(sigma*tfac);
    getNeighborhoodKernel(maxN, facN);

    double lrC=lrConvergeA/lrConvergeB;
    step=0;
    for (epoch=0; epoch<param.stepsConvergence; epoch++) {

      if (validProgressObject()) {
        sprintf(buffer,"convergence step %i",epoch);
        getProgressObject().step(buffer);
        abort = getProgressObject().breakRequested();
        }
      if (abort) return b;

      mix.apply(idx);
      for (i=0; i<idx.size(); i++, step++) {
        const dvector& curr = data.getRow(idx[i]);
        //find winner
        dist->apply(grid, curr, distances);
        winner=distances.getIndexOfMinimum();
        //find bounds
        if (winner%sizeX-maxN < 0) {
          startx=-winner%sizeX;
        } else {
          startx=-maxN;
        }
        if (winner%sizeX+maxN > sizeX) {
          stopx=sizeX-winner%sizeX;
        } else {
          stopx=maxN;
        }
        if (winner/sizeX-maxN < 0) {
          starty=-winner/sizeX;
        } else {
          starty=-maxN;
        }
        if (winner/sizeX+maxN > sizeY) {
          stopy=sizeY-winner/sizeX;
        } else {
          stopy=maxN;
        }
        for (j=starty; j<stopy; j++) {
          for (k=startx; k<stopx; k++) {
            if (facN.at(j,k)==0.) {
              continue;
            }
            delta.subtract(curr, grid[winner+j*sizeX+k]);
            grid[winner+j*sizeX+k].addScaled(lrC*facN.at(j,k),delta);
          }
        }
        lrC=lrConvergeA/(step+lrConvergeB);
      }
    }
    delete dist;
    return b;
  }
  // implements the Fuzzy C Means algorithm
  bool fuzzyCMeans::train(const dmatrix& data) {

    bool ok=true;
    int t=0;
    // create the distance functor according to the paramter norm
    distanceFunctor<double>* distFunc = 0;
    switch (getParameters().norm)  {
      case parameters::L1:
        distFunc = new l1Distance<double>;
        break;
      case parameters::L2:
        distFunc = new l2Distance<double>;
        break;
      default:
        break;
    }
    int nbOfClusters=getParameters().nbOfClusters;
    int nbOfPoints=data.rows();
    if(nbOfClusters>nbOfPoints) {
      setStatusString("more Clusters than points");
      ok = false;
    }
    double q=getParameters().fuzzifier;
    if (q<=1) {
      setStatusString("q has to be bigger than 1");
      ok = false;
    }
    // select some points of the given data to initialise the centroids
    selectRandomPoints(data,nbOfClusters,centroids);
    // initialize variables
    centroids.resize(nbOfClusters,data.columns(),0.0);
    dmatrix memberships(nbOfPoints, nbOfClusters, 0.0);
    double terminationCriterion=0;
    double newDistance;
    dvector newCenter(data.columns());
    dvector currentPoint(data.columns());
    dmatrix newCentroids(nbOfClusters,data.columns(),0.0);
    double sumOfMemberships=0;
    double membership=0;
    double dist1;
    double dist2;
    int i,j,k,m;
    do {
        // calculate new memberships
      memberships.fill(0.0);  //  clear old memberships
      for (i=0; i<nbOfPoints; i++) {
        for (j=0; j<nbOfClusters; j++) {
          newDistance=0;
          dist1=distFunc->apply(data.getRow(i),
                                centroids.getRow(j));
          for (k=0; k<nbOfClusters; k++) {
            dist2=distFunc->apply(data.getRow(i),
                                  centroids.getRow(k));
       // if distance is 0, normal calculation of membership is not possible.
            if (dist2!=0) {
              newDistance+=pow((dist1/dist2),(1/(q-1)));
            }
          }
      // if point and centroid are equal
          if (newDistance!=0)
            memberships.at(i,j)=1/newDistance;
          else {
            dvector row(memberships.columns(),0.0);
            memberships.setRow(i,row);
            memberships.at(i,j)=1;
            break;
          }
        }
      }
      t++;  // counts the iterations

     // calculate new centroids based on modified memberships
      for (m=0; m<nbOfClusters; m++) {
        newCenter.fill(0.0);
        sumOfMemberships=0;
        for (i=0; i<nbOfPoints; i++) {
          currentPoint=data.getRow(i);
          membership=pow(memberships.at(i,m),q);
          sumOfMemberships+=membership;
          currentPoint.multiply(membership);
          newCenter.add(currentPoint);
        }
        newCenter.divide(sumOfMemberships);
        newCentroids.setRow(m,newCenter);
      }
      terminationCriterion=distFunc->apply(centroids,newCentroids);
      centroids=newCentroids;
    }
    // the termination criterions
    while ( (terminationCriterion>getParameters().epsilon)
            && (t<getParameters().maxIterations));

    int nbClusters = nbOfClusters;
    //Put the id information into the result object
    //Each cluster has the id of its position in the matrix
    ivector tids(nbClusters);
    for (i=0; i<nbClusters; i++) {
      tids.at(i)=i;
    }
    outTemplate=outputTemplate(tids);
    return ok;


  }
  bool sffs::apply(const dmatrix& src,const ivector& srcIds, 
                   dmatrix& dest) const {
    bool ok=true;
    dest.clear();
    parameters param=getParameters();
    // initialize cross validator

    costFunction *cF;
    cF = param.usedCostFunction;
    cF->setSrc(src,srcIds);

    int featureToInsert(0),featureToDelete(0),i;
    double oldRate,newRate;
    bool doInclude=true;
    bool terminate=false;
    int nbFeatures=src.columns();
    std::list<int> in,out;
    std::list<int>::iterator it;
    std::map<double,int> values;
    double value;
    for (i=0; i<nbFeatures; i++) {
      out.push_back(i);
    }
    ivector posInSrc(nbFeatures,-1);//saves the position in src of the inserted
    // feature to mark it as not used if this feature is deleted later
    dvector regRate(nbFeatures);  // the recognition rates after the insertion 
                                  // of a new feature
    if (param.nbFeatures<2) {
      setStatusString("You will have to choose at least two features. Set nbFeatures=2");
      return false;
    }

    // add the first best two features; do 2 steps sfs
    for (i=0; i<2; i++ ) {
      if (dest.columns()<src.columns() && !terminate) {
        // add space for one extra feature
        for (it=out.begin(); it!=out.end(); it++) {
          in.push_back(*it);
          cF->apply(in,value);
          values[value]=*it;
          in.pop_back();
        }
        // search for maximum in regRate; all possibilities not tested are -1
        in.push_back((--values.end())->second);
        out.remove((--values.end())->second);
      }
    }
    cF->apply(in,oldRate);
    while (!terminate) {
      // STEP 1: include the best possible feature
      if (static_cast<int>(in.size())<src.columns() && 
          !terminate && doInclude) {
        values.clear();
        for (it=out.begin(); it!=out.end(); it++) {
          in.push_back(*it);
          cF->apply(in,value);
          values[value]=*it;
          in.pop_back();
        }
        featureToInsert=(--values.end())->second;
        in.push_back(featureToInsert);
        out.remove(featureToInsert);
      }
      // STEP 2: conditional exclusion
      if (in.size()>0 && !terminate) {
        values.clear();
        for (it=in.begin(); it!=in.end(); it++) {
          int tmp=*it;
          it=in.erase(it);
          cF->apply(in,value);
          values[value]=tmp;
          in.insert(it,tmp);
          it--;
        }
        featureToDelete=(--values.end())->second;

        // if the least significant feature is equal to the most significant
        // feature that was included in step 1, leave feature and 
        // include the next one
        if (featureToDelete==featureToInsert) {
          doInclude=true;
        } else {    // delete this feature and compute new recognition rate

          // if the feature to delete is not the last feature in dest,
          // change the feature against the last feature in dest and delete
          // the last column in dest, otherwise if the feature to delete 
          // is equal to the last feature in dest nothing will be done, 
          // because this is already the lacking feature in temp
          cF->apply(in,newRate);
          // if recognition rate without least significant feature is better 
          // than with this feature delete it
          if (newRate>oldRate) { 

            in.remove(featureToDelete);
            out.push_back(featureToDelete);
            // search for another least significant feature before 
            // including the next one
            doInclude=false;
            oldRate=newRate;
          } else {
            doInclude=true;
          }
          // if only two features left, include the next one
          if (dest.columns()<=2) {
            doInclude=true;    
          }
        }          
      } // end of exclusion
      // test if the predetermined number of features is reached
      terminate=(param.nbFeatures==static_cast<int>(in.size()));
    } // while (!terminate)

    // Now fill dest
    const int sz = static_cast<int>(in.size());
    dest.resize(src.rows(), sz, 0., false, false);
    ivector idvec(false, sz);
    std::list<int>::const_iterator lit = in.begin();
    for (i=0; i < sz; ++i) {
      idvec.at(i)=*lit;
      ++lit;
    }
    for (i=0; i < src.rows(); ++i) {
      const dvector& svec = src.getRow(i);
      dvector& dvec = dest.getRow(i);
      for (int j=0; j < sz; ++j) {
        dvec.at(j) = svec.at(idvec.at(j));
      }
    }

    return ok;
  };