content: /*
   *    This program is free software; you can redistribute it and/or modify
   *    it under the terms of the GNU General Public License as published by
   *    the Free Software Foundation; either version 2 of the License, or
   *    (at your option) any later version.
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *    GNU General Public License for more details.
   *
   *    You should have received a copy of the GNU General Public License
   *    along with this program; if not, write to the Free Software
   *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  /*
   *    Regression.java
   *    Copyright (C) 2008 University of Waikato, Hamilton, New Zealand
   *
   */
  
  package weka.classifiers.pmml.consumer;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import weka.core.Attribute;
  import weka.core.Instance;
  import weka.core.Instances;
  import weka.core.RevisionUtils;
  import weka.core.Utils;
  import weka.core.pmml.*;
  
  /**
   * Class implementing import of PMML Regression model. Can be
   * used as a Weka classifier for prediction (buildClassifier()
   * raises an Exception).
   *
   * @author Mark Hall (mhall{[at]}pentaho{[dot]}com
   * @version $Revision: 6018 $
   */
  public class Regression extends PMMLClassifier
    implements Serializable {
  
    /** For serialization */
    private static final long serialVersionUID = -5551125528409488634L;
  
    /**
     * Inner class for encapsulating a regression table
     */
    static class RegressionTable implements Serializable {
  
      /** For serialization */
      private static final long serialVersionUID = -5259866093996338995L;
  
      /**
       * Abstract inner base class for different predictor types.
       */
      abstract static class Predictor implements Serializable {
  
        /** For serialization */
        private static final long serialVersionUID = 7043831847273383618L;
        
        /** Name of this predictor */
        protected String m_name;
        
        /** 
         * Index of the attribute in the mining schema that corresponds to this
         * predictor
         */
        protected int m_miningSchemaAttIndex = -1;
        
        /** Coefficient for this predictor */
        protected double m_coefficient = 1.0;
        
        /**
         * Constructs a new Predictor.
         * 
         * @param predictor the <code>Element</code> encapsulating this predictor
         * @param miningSchema the mining schema as an Instances object
         * @throws Exception if there is a problem constructing this Predictor
         */
        protected Predictor(Element predictor, Instances miningSchema) throws Exception {
          m_name = predictor.getAttribute("name");
          for (int i = 0; i < miningSchema.numAttributes(); i++) {
            Attribute temp = miningSchema.attribute(i);
            if (temp.name().equals(m_name)) {
              m_miningSchemaAttIndex = i;
            }
          }
          
          if (m_miningSchemaAttIndex == -1) {
            throw new Exception("[Predictor] unable to find matching attribute for "
                                + "predictor " + m_name);
          }
  
          String coeff = predictor.getAttribute("coefficient");
          if (coeff.length() > 0) {
            m_coefficient = Double.parseDouble(coeff);
          }
        }
  
        /**
         * Returns a textual description of this predictor applicable
         * to all sub classes.
         */
        public String toString() {
          return Utils.doubleToString(m_coefficient, 12, 4) + " * ";
        }
  
        /**
         * Abstract add method. Adds this predictor into the sum for the
         * current prediction.
         * 
         * @param preds the prediction computed so far. For regression, it is a
         * single element array; for classification it is a multi-element array
         * @param input the input instance's values
         */
        public abstract void add(double[] preds, double[] input);
      }
  
      /**
       * Inner class for a numeric predictor
       */
      protected class NumericPredictor extends Predictor {
        /**
         * For serialization
         */
        private static final long serialVersionUID = -4335075205696648273L;
        
        /** The exponent*/
        protected double m_exponent = 1.0;
  
        /**
         * Constructs a NumericPredictor.
         * 
         * @param predictor the <code>Element</code> holding the predictor
         * @param miningSchema the mining schema as an Instances object
         * @throws Exception if something goes wrong while constructing this
         * predictor
         */
        protected NumericPredictor(Element predictor, 
                                Instances miningSchema) throws Exception {
          super(predictor, miningSchema);
          
          String exponent = predictor.getAttribute("exponent");
          if (exponent.length() > 0) {
            m_exponent = Double.parseDouble(exponent);
          }
        }
  
        /**
         * Return a textual description of this predictor.
         */
        public String toString() {
          String output = super.toString();
          output += m_name;
          if (m_exponent > 1.0 || m_exponent < 1.0) {
            output += "^" + Utils.doubleToString(m_exponent, 4);
          }
          return output;
        }
  
        /**
         * Adds this predictor into the sum for the
         * current prediction.
         * 
         * @param preds the prediction computed so far. For regression, it is a
         * single element array; for classification it is a multi-element array
         * @param input the input instance's values
         */
        public void add(double[] preds, double[] input) {
          if (m_targetCategory == -1) {
            preds[0] += m_coefficient * Math.pow(input[m_miningSchemaAttIndex], m_exponent);
          } else {
            preds[m_targetCategory] += 
              m_coefficient * Math.pow(input[m_miningSchemaAttIndex], m_exponent);
          }
        }
      }
  
      /**
       * Inner class encapsulating a categorical predictor.
       */
      protected class CategoricalPredictor extends Predictor {
        
        /**For serialization */
        private static final long serialVersionUID = 3077920125549906819L;
        
        /** The attribute value for this predictor */
        protected String m_valueName;
        
        /** The index of the attribute value for this predictor */
        protected int m_valueIndex = -1;
  
        /**
         * Constructs a CategoricalPredictor.
         * 
         * @param predictor the <code>Element</code> containing the predictor
         * @param miningSchema the mining schema as an Instances object
         * @throws Exception if something goes wrong while constructing
         * this predictor
         */
        protected CategoricalPredictor(Element predictor,
                                    Instances miningSchema) throws Exception {
          super(predictor, miningSchema);
          
          String valName = predictor.getAttribute("value");
          if (valName.length() == 0) {
            throw new Exception("[CategoricalPredictor] attribute value not specified!");
          }
          
          m_valueName = valName;
  
          Attribute att = miningSchema.attribute(m_miningSchemaAttIndex);
          if (att.isString()) {
            // means that there were no Value elements defined in the 
            // data dictionary (and hence the mining schema).
            // We add our value here.
            att.addStringValue(m_valueName);
          }
          m_valueIndex = att.indexOfValue(m_valueName);
          /*        for (int i = 0; i < att.numValues(); i++) {
            if (att.value(i).equals(m_valueName)) {
              m_valueIndex = i;
            }
            }*/
  
          if (m_valueIndex == -1) {
            throw new Exception("[CategoricalPredictor] unable to find value "
                                + m_valueName + " in mining schema attribute "
                                + att.name());
          }
        }
  
        /**
         * Return a textual description of this predictor.
         */
        public String toString() {
          String output = super.toString();
          output += m_name + "=" + m_valueName;
          return output;
        }
  
        /**
         * Adds this predictor into the sum for the
         * current prediction.
         * 
         * @param preds the prediction computed so far. For regression, it is a
         * single element array; for classification it is a multi-element array
         * @param input the input instance's values
         */
        public void add(double[] preds, double[] input) {
          
          // if the value is equal to the one in the input then add the coefficient
          if (m_valueIndex == (int)input[m_miningSchemaAttIndex]) {
            if (m_targetCategory == -1) {
              preds[0] += m_coefficient;
            } else {
              preds[m_targetCategory] += m_coefficient;
            }
          }
        }
      }
  
      /**
       * Inner class to handle PredictorTerms.
       */
      protected class PredictorTerm implements Serializable {
  
        /** For serialization */
        private static final long serialVersionUID = 5493100145890252757L;
  
        /** The coefficient for this predictor term */
        protected double m_coefficient = 1.0;
  
        /** the indexes of the terms to be multiplied */
        protected int[] m_indexes;
  
        /** The names of the terms (attributes) to be multiplied */
        protected String[] m_fieldNames;
  
        /**
         * Construct a new PredictorTerm.
         * 
         * @param predictorTerm the <code>Element</code> describing the predictor term
         * @param miningSchema the mining schema as an Instances object
         * @throws Exception if something goes wrong while constructing this
         * predictor term
         */
        protected PredictorTerm(Element predictorTerm, 
                                Instances miningSchema) throws Exception {
  
          String coeff = predictorTerm.getAttribute("coefficient");
          if (coeff != null && coeff.length() > 0) {
            try {
              m_coefficient = Double.parseDouble(coeff);
            } catch (IllegalArgumentException ex) {
              throw new Exception("[PredictorTerm] unable to parse coefficient");
            }
          }
          
          NodeList fields = predictorTerm.getElementsByTagName("FieldRef");
          if (fields.getLength() > 0) {
            m_indexes = new int[fields.getLength()];
            m_fieldNames = new String[fields.getLength()];
  
            for (int i = 0; i < fields.getLength(); i++) {
              Node fieldRef = fields.item(i);
              if (fieldRef.getNodeType() == Node.ELEMENT_NODE) {
                String fieldName = ((Element)fieldRef).getAttribute("field");
                if (fieldName != null && fieldName.length() > 0) {
                  boolean found = false;
                  // look for this field in the mining schema
                  for (int j = 0; j < miningSchema.numAttributes(); j++) {
                    if (miningSchema.attribute(j).name().equals(fieldName)) {
                      
                      // all referenced fields MUST be numeric
                      if (!miningSchema.attribute(j).isNumeric()) {
                        throw new Exception("[PredictorTerm] field is not continuous: "
                                            + fieldName);
                      }
                      found = true;
                      m_indexes[i] = j;
                      m_fieldNames[i] = fieldName;
                      break;
                    }
                  }
                  if (!found) {
                    throw new Exception("[PredictorTerm] Unable to find field "
                                        + fieldName + " in mining schema!");
                  }
                }
              }
            }
          }
        }
  
        /**
         * Return a textual description of this predictor term.
         */
        public String toString() {
          StringBuffer result = new StringBuffer();
          result.append("(" + Utils.doubleToString(m_coefficient, 12, 4));
          for (int i = 0; i < m_fieldNames.length; i++) {
            result.append(" * " + m_fieldNames[i]);
          }
          result.append(")");
          return result.toString();
        }
  
        /**
         * Adds this predictor term into the sum for the
         * current prediction.
         * 
         * @param preds the prediction computed so far. For regression, it is a
         * single element array; for classification it is a multi-element array
         * @param input the input instance's values
         */
        public void add(double[] preds, double[] input) {
          int indx = 0;
          if (m_targetCategory != -1) {
            indx = m_targetCategory;
          }
  
          double result = m_coefficient;
          for (int i = 0; i < m_indexes.length; i++) {
            result *= input[m_indexes[i]];
          }
          preds[indx] += result;
        }
      }
      
      /** Constant for regression model type */
      public static final int REGRESSION = 0;
      
      /** Constant for classification model type */
      public static final int CLASSIFICATION = 1;
  
      /** The type of function - regression or classification */
      protected int m_functionType = REGRESSION;
      
      /** The mining schema */
      protected MiningSchema m_miningSchema;
          
      /** The intercept */
      protected double m_intercept = 0.0;
      
      /** classification only */
      protected int m_targetCategory = -1;
  
      /** Numeric and categorical predictors */
      protected ArrayList<Predictor> m_predictors = 
        new ArrayList<Predictor>();
  
      /** Interaction terms */
      protected ArrayList<PredictorTerm> m_predictorTerms =
        new ArrayList<PredictorTerm>();
  
      /**
       * Return a textual description of this RegressionTable.
       */
      public String toString() {
        Instances miningSchema = m_miningSchema.getFieldsAsInstances();
        StringBuffer temp = new StringBuffer();
        temp.append("Regression table:\n");
        temp.append(miningSchema.classAttribute().name());
        if (m_functionType == CLASSIFICATION) {
          temp.append("=" + miningSchema.
                      classAttribute().value(m_targetCategory));
        }
  
        temp.append(" =\n\n");
        
        // do the predictors
        for (int i = 0; i < m_predictors.size(); i++) {
          temp.append(m_predictors.get(i).toString() + " +\n");
        }
        
        // do the predictor terms
        for (int i = 0; i < m_predictorTerms.size(); i++) {
          temp.append(m_predictorTerms.get(i).toString() + " +\n");
        }
  
        temp.append(Utils.doubleToString(m_intercept, 12, 4));
        temp.append("\n\n");
  
        return temp.toString();
      }
  
      /**
       * Construct a regression table from an <code>Element</code>
       *
       * @param table the table to encapsulate
       * @param functionType the type of function 
       * (regression or classification)
       * to use
       * @param mSchema the mining schema
       * @throws Exception if there is a problem while constructing
       * this regression table
       */
      protected RegressionTable(Element table, 
                             int functionType,
                             MiningSchema mSchema) throws Exception {
  
        m_miningSchema = mSchema;
        m_functionType = functionType;
  
        Instances miningSchema = m_miningSchema.getFieldsAsInstances();
  
        // get the intercept
        String intercept = table.getAttribute("intercept");
        if (intercept.length() > 0) {
          m_intercept = Double.parseDouble(intercept);
        }
  
        // get the target category (if classification)
        if (m_functionType == CLASSIFICATION) {
          // target category MUST be defined
          String targetCat = table.getAttribute("targetCategory");
          if (targetCat.length() > 0) {
            Attribute classA = miningSchema.classAttribute();
            for (int i = 0; i < classA.numValues(); i++) {
              if (classA.value(i).equals(targetCat)) {
                m_targetCategory = i;
              }
            }
          } 
          if (m_targetCategory == -1) {
            throw new Exception("[RegressionTable] No target categories defined for classification");
          }
        }
  
        // read all the numeric predictors
        NodeList numericPs = table.getElementsByTagName("NumericPredictor");
        for (int i = 0; i < numericPs.getLength(); i++) {
          Node nP = numericPs.item(i);
          if (nP.getNodeType() == Node.ELEMENT_NODE) {
            NumericPredictor numP = new NumericPredictor((Element)nP, miningSchema);
            m_predictors.add(numP);
          }
        }
  
        // read all the categorical predictors
        NodeList categoricalPs = table.getElementsByTagName("CategoricalPredictor");
        for (int i = 0; i < categoricalPs.getLength(); i++) {
          Node cP = categoricalPs.item(i);
          if (cP.getNodeType() == Node.ELEMENT_NODE) {
            CategoricalPredictor catP = new CategoricalPredictor((Element)cP, miningSchema);
            m_predictors.add(catP);
          }
        }
  
        // read all the PredictorTerms
        NodeList predictorTerms = table.getElementsByTagName("PredictorTerm");
        for (int i = 0; i < predictorTerms.getLength(); i++) {
          Node pT = predictorTerms.item(i);
          PredictorTerm predT = new PredictorTerm((Element)pT, miningSchema);
          m_predictorTerms.add(predT);
        }
      }
  
      public void predict(double[] preds, double[] input) {
        if (m_targetCategory == -1) {
          preds[0] = m_intercept;
        } else {
          preds[m_targetCategory] = m_intercept;
        }
        
        // add the predictors
        for (int i = 0; i < m_predictors.size(); i++) {
          Predictor p = m_predictors.get(i);
          p.add(preds, input);
        }
  
        // add the PredictorTerms
        for (int i = 0; i < m_predictorTerms.size(); i++) {
          PredictorTerm pt = m_predictorTerms.get(i);
          pt.add(preds, input);
        }
      }
    }
  
    /** Description of the algorithm */
    protected String m_algorithmName;
  
    /** The regression tables for this regression */
    protected RegressionTable[] m_regressionTables;
  
    /**
     * Enum for the normalization methods.
     */
    enum Normalization {
      NONE, SIMPLEMAX, SOFTMAX, LOGIT, PROBIT, CLOGLOG,
        EXP, LOGLOG, CAUCHIT}
  
    /** The normalization to use */
    protected Normalization m_normalizationMethod = Normalization.NONE;
  
    /**
     * Constructs a new PMML Regression.
     * 
     * @param model the <code>Element</code> containing the regression model
     * @param dataDictionary the data dictionary as an Instances object
     * @param miningSchema the mining schema
     * @throws Exception if there is a problem constructing this Regression
     */
    public Regression(Element model, Instances dataDictionary,
                      MiningSchema miningSchema) throws Exception {
      super(dataDictionary, miningSchema);
      
      int functionType = RegressionTable.REGRESSION;
  
      // determine function name first
      String fName = model.getAttribute("functionName");
      
      if (fName.equals("regression")) {
        functionType = RegressionTable.REGRESSION;
      } else if (fName.equals("classification")) {
        functionType = RegressionTable.CLASSIFICATION;
      } else {
        throw new Exception("[PMML Regression] Function name not defined in pmml!");
      }
  
      // do we have an algorithm name?
      String algName = model.getAttribute("algorithmName");
      if (algName != null && algName.length() > 0) {
        m_algorithmName = algName;
      }
  
      // determine normalization method (if any)
      m_normalizationMethod = determineNormalization(model);
  
      setUpRegressionTables(model, functionType);
  
      // convert any string attributes in the mining schema
      //miningSchema.convertStringAttsToNominal();
    }
  
    /**
     * Create all the RegressionTables for this model.
     * 
     * @param model the <code>Element</code> holding this regression model
     * @param functionType the type of function (regression or
     * classification)
     * @throws Exception if there is a problem setting up the regression
     * tables
     */
    private void setUpRegressionTables(Element model,
                                       int functionType) throws Exception {
      NodeList tableList = model.getElementsByTagName("RegressionTable");
      
      if (tableList.getLength() == 0) {
        throw new Exception("[Regression] no regression tables defined!");
      }
  
      m_regressionTables = new RegressionTable[tableList.getLength()];
      
      for (int i = 0; i < tableList.getLength(); i++) {
        Node table = tableList.item(i);
        if (table.getNodeType() == Node.ELEMENT_NODE) {
          RegressionTable tempRTable = 
            new RegressionTable((Element)table, 
                                functionType, 
                                m_miningSchema);
          m_regressionTables[i] = tempRTable;
        }
      }
    }
  
    /**
     * Return the type of normalization used for this regression
     * 
     * @param model the <code>Element</code> holding the model
     * @return the normalization used in this regression
     */
    private static Normalization determineNormalization(Element model) {
      
      Normalization normMethod = Normalization.NONE;
  
      String normName = model.getAttribute("normalizationMethod");
      if (normName.equals("simplemax")) {
        normMethod = Normalization.SIMPLEMAX;
      } else if (normName.equals("softmax")) {
        normMethod = Normalization.SOFTMAX;
      } else if (normName.equals("logit")) {
        normMethod = Normalization.LOGIT;
      } else if (normName.equals("probit")) {
        normMethod = Normalization.PROBIT;
      } else if (normName.equals("cloglog")) {
        normMethod = Normalization.CLOGLOG;
      } else if (normName.equals("exp")) {
        normMethod = Normalization.EXP;
      } else if (normName.equals("loglog")) {
        normMethod = Normalization.LOGLOG;
      } else if (normName.equals("cauchit")) {
        normMethod = Normalization.CAUCHIT;
      } 
      return normMethod;
    }
  
    /**
     * Return a textual description of this Regression model.
     */
    public String toString() {
      StringBuffer temp = new StringBuffer();
      temp.append("PMML version " + getPMMLVersion());
      if (!getCreatorApplication().equals("?")) {
        temp.append("\nApplication: " + getCreatorApplication());
      }
      if (m_algorithmName != null) {
        temp.append("\nPMML Model: " + m_algorithmName);
      }
      temp.append("\n\n");
      temp.append(m_miningSchema);
  
      for (RegressionTable table : m_regressionTables) {
        temp.append(table);
      }
      
      if (m_normalizationMethod != Normalization.NONE) {
        temp.append("Normalization: " + m_normalizationMethod);
      }
      temp.append("\n");
  
      return temp.toString();
    }
  
    /**                                                                                                             
     * Classifies the given test instance. The instance has to belong to a                                          
     * dataset when it's being classified.                                                          
     *                                                                                                              
     * @param inst the instance to be classified                                                                
     * @return the predicted most likely class for the instance or                                                  
     * Utils.missingValue() if no prediction is made                                                             
     * @exception Exception if an error occurred during the prediction                                              
     */
    public double[] distributionForInstance(Instance inst) throws Exception {
      if (!m_initialized) {
        mapToMiningSchema(inst.dataset());
      }
      double[] preds = null;
      if (m_miningSchema.getFieldsAsInstances().classAttribute().isNumeric()) {
        preds = new double[1];
      } else {
        preds = new double[m_miningSchema.getFieldsAsInstances().classAttribute().numValues()];
      }
  
      // create an array of doubles that holds values from the incoming
      // instance; in order of the fields in the mining schema. We will
      // also handle missing values and outliers here.
      //    System.err.println(inst);
      double[] incoming = m_fieldsMap.instanceToSchema(inst, m_miningSchema);
  
      // scan for missing values. If there are still missing values after instanceToSchema(),
      // then missing value handling has been deferred to the PMML scheme. The specification
      // (Regression PMML 3.2) seems to contradict itself with regards to classification and categorical
      // variables. In one place it states that if a categorical variable is missing then 
      // variable_name=value is 0 for any value. Further down in the document it states: "if
      // one or more of the y_j cannot be evaluated because the value in one of the referenced
      // fields is missing, then the following formulas (for computing p_j) do not apply. In
      // that case the predictions are defined by the priorProbability values in the Target
      // element".
  
      // In this implementation we will default to information in the Target element (default
      // value for numeric prediction and prior probabilities for classification). If there is
      // no Target element defined, then an Exception is thrown.
  
      boolean hasMissing = false;
      for (int i = 0; i < incoming.length; i++) {
        if (i != m_miningSchema.getFieldsAsInstances().classIndex() && 
            Utils.isMissingValue(incoming[i])) {
          hasMissing = true;
          break;
        }
      }
  
      if (hasMissing) {
        if (!m_miningSchema.hasTargetMetaData()) {
          String message = "[Regression] WARNING: Instance to predict has missing value(s) but "
            + "there is no missing value handling meta data and no "
            + "prior probabilities/default value to fall back to. No "
            + "prediction will be made (" 
            + ((m_miningSchema.getFieldsAsInstances().classAttribute().isNominal() ||
                m_miningSchema.getFieldsAsInstances().classAttribute().isString())
                ? "zero probabilities output)."
                : "NaN output).");
          if (m_log == null) {
            System.err.println(message);
          } else {
            m_log.logMessage(message);
          }
          if (m_miningSchema.getFieldsAsInstances().classAttribute().isNumeric()) {
            preds[0] = Utils.missingValue();
          }
          return preds;
        } else {
          // use prior probablilities/default value
          TargetMetaInfo targetData = m_miningSchema.getTargetMetaData();
          if (m_miningSchema.getFieldsAsInstances().classAttribute().isNumeric()) {
            preds[0] = targetData.getDefaultValue();
          } else {
            Instances miningSchemaI = m_miningSchema.getFieldsAsInstances();
            for (int i = 0; i < miningSchemaI.classAttribute().numValues(); i++) {
              preds[i] = targetData.getPriorProbability(miningSchemaI.classAttribute().value(i));
            }
          }
          return preds;
        }
      } else {
        // loop through the RegressionTables
        for (int i = 0; i < m_regressionTables.length; i++) {
          m_regressionTables[i].predict(preds, incoming);
        }
   
        // Now apply the normalization
        switch (m_normalizationMethod) {
        case NONE:
          // nothing to be done
          break;
        case SIMPLEMAX:
          Utils.normalize(preds);
          break;
        case SOFTMAX:
          for (int i = 0; i < preds.length; i++) {
            preds[i] = Math.exp(preds[i]);
          }
          if (preds.length == 1) {
            // hack for those models that do binary logistic regression as
            // a numeric prediction model
            preds[0] = preds[0] / (preds[0] + 1.0);
          } else {
            Utils.normalize(preds);
          }
          break;
        case LOGIT:
          for (int i = 0; i < preds.length; i++) {
            preds[i] = 1.0 / (1.0 + Math.exp(-preds[i]));
          }
          Utils.normalize(preds);
          break;
        case PROBIT:
          for (int i = 0; i < preds.length; i++) {
            preds[i] = weka.core.matrix.Maths.pnorm(preds[i]);
          }
          Utils.normalize(preds);
          break;
        case CLOGLOG:
          // note this is supposed to be illegal for regression
          for (int i = 0; i < preds.length; i++) {
            preds[i] = 1.0 - Math.exp(-Math.exp(-preds[i]));
          }
          Utils.normalize(preds);
          break;
        case EXP:
          for (int i = 0; i < preds.length; i++) {
            preds[i] = Math.exp(preds[i]);
          }
          Utils.normalize(preds);
          break;
        case LOGLOG:
          // note this is supposed to be illegal for regression
          for (int i = 0; i < preds.length; i++) {
            preds[i] = Math.exp(-Math.exp(-preds[i]));
          }
          Utils.normalize(preds);
          break;
        case CAUCHIT:
          for (int i = 0; i < preds.length; i++) {
            preds[i] = 0.5 + (1.0 / Math.PI) * Math.atan(preds[i]);
          }
          Utils.normalize(preds);
          break;
        default:
            throw new Exception("[Regression] unknown normalization method");
        }
  
        // If there is a Target defined, and this is a numeric prediction problem,
        // then apply any min, max, rescaling etc.
        if (m_miningSchema.getFieldsAsInstances().classAttribute().isNumeric()
            && m_miningSchema.hasTargetMetaData()) {
          TargetMetaInfo targetData = m_miningSchema.getTargetMetaData();
          preds[0] = targetData.applyMinMaxRescaleCast(preds[0]);
        }
      }
      
      return preds;
    }
  
    /* (non-Javadoc)
     * @see weka.core.RevisionHandler#getRevision()
     */
    public String getRevision() {
      return RevisionUtils.extract("$Revision: 6018 $");
    }
  }
  