/**
   * Outputs the distribution for the given output.
   *
   * <p>Pipes output of SVM through sigmoid function.
   *
   * @param inst the instance for which distribution is to be computed
   * @return the distribution
   * @exception Exception if something goes wrong
   */
  public double[] distributionForInstance(Instance inst) throws Exception {

    // Filter instance
    m_ReplaceMissingValues.input(inst);
    m_ReplaceMissingValues.batchFinished();
    inst = m_ReplaceMissingValues.output();

    m_NominalToBinary.input(inst);
    m_NominalToBinary.batchFinished();
    inst = m_NominalToBinary.output();

    // Get probabilities
    double output = 0, sumSoFar = 0;
    if (m_K > 0) {
      for (int i = 0; i <= m_K; i++) {
        if (sumSoFar < 0) {
          output -= m_Weights[i];
        } else {
          output += m_Weights[i];
        }
        if (m_IsAddition[i]) {
          sumSoFar += innerProduct(m_Train.instance(m_Additions[i]), inst);
        } else {
          sumSoFar -= innerProduct(m_Train.instance(m_Additions[i]), inst);
        }
      }
    }
    double[] result = new double[2];
    result[1] = 1 / (1 + Math.exp(-output));
    result[0] = 1 - result[1];

    return result;
  }
  /**
   * Builds the ensemble of perceptrons.
   *
   * @exception Exception if something goes wrong during building
   */
  public void buildClassifier(Instances insts) throws Exception {

    if (insts.checkForStringAttributes()) {
      throw new UnsupportedAttributeTypeException("Cannot handle string attributes!");
    }
    if (insts.numClasses() > 2) {
      throw new Exception("Can only handle two-class datasets!");
    }
    if (insts.classAttribute().isNumeric()) {
      throw new UnsupportedClassTypeException("Can't handle a numeric class!");
    }

    // Filter data
    m_Train = new Instances(insts);
    m_Train.deleteWithMissingClass();
    m_ReplaceMissingValues = new ReplaceMissingValues();
    m_ReplaceMissingValues.setInputFormat(m_Train);
    m_Train = Filter.useFilter(m_Train, m_ReplaceMissingValues);

    m_NominalToBinary = new NominalToBinary();
    m_NominalToBinary.setInputFormat(m_Train);
    m_Train = Filter.useFilter(m_Train, m_NominalToBinary);

    /** Randomize training data */
    m_Train.randomize(new Random(m_Seed));

    /** Make space to store perceptrons */
    m_Additions = new int[m_MaxK + 1];
    m_IsAddition = new boolean[m_MaxK + 1];
    m_Weights = new int[m_MaxK + 1];

    /** Compute perceptrons */
    m_K = 0;
    out:
    for (int it = 0; it < m_NumIterations; it++) {
      for (int i = 0; i < m_Train.numInstances(); i++) {
        Instance inst = m_Train.instance(i);
        if (!inst.classIsMissing()) {
          int prediction = makePrediction(m_K, inst);
          int classValue = (int) inst.classValue();
          if (prediction == classValue) {
            m_Weights[m_K]++;
          } else {
            m_IsAddition[m_K] = (classValue == 1);
            m_Additions[m_K] = i;
            m_K++;
            m_Weights[m_K]++;
          }
          if (m_K == m_MaxK) {
            break out;
          }
        }
      }
    }
  }