moa.classifiers.rules.AMRules.java Source code

Java tutorial

Introduction

Here is the source code for moa.classifiers.rules.AMRules.java

Source

/*
 *    AMRules.java
 *    Copyright (C) 2013 University of Porto, Portugal
 *    @author E. Almeida, J. Gama
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *    
 *    
 */

package moa.classifiers.rules;

import moa.classifiers.AbstractClassifier;
import moa.classifiers.core.attributeclassobservers.*;
import moa.classifiers.core.attributeclassobservers.BinaryTreeNumericAttributeClassObserverRegression.Node;
import moa.core.AutoExpandVector;
import moa.core.DoubleVector;
import moa.core.Measurement;
import moa.core.StringUtils;
import moa.options.FlagOption;
import moa.options.FloatOption;
import moa.options.IntOption;
import moa.options.MultiChoiceOption;
import weka.core.Instance;
import weka.core.Utils;
//import samoa.instances.Instance;
//import moa.core.Utils;
import java.math.BigDecimal;
import java.util.*;
import moa.classifiers.Regressor;

/**
 * Adaptive Model Rules (AMRules), the streaming rule learning algorithm for regression problems.
 * This algorithm learn ordered and unordered rule set from data stream. Each rule in AMRules use a 
 * Page-Hinkley test to detect changes in the processing generating data and react to changes by pruning the rule set.
 * This algorithm also does the detection of anomalies.
 * 
 * <p>Learning Adaptive Model Rules from High-Speed Data Streams, ECML 2013, E. Almeida, C. Ferreira, and J. Gama; </p>
 * Project Knowledge Discovery from Data Streams, FCT LIAAD-INESC TEC, 
 *
 * Contact: jgama@fep.up.pt
 *  
 * <p>Parameters:</p>
 * <ul>
 * <li> -h: The threshold value to be used in the Page Hinckley change detection tests.<li>
 * <li> -a: The alpha value to use in the Page Hinckley change detection tests.</li>
 * <li> -q: The number of instances a leaf should observe before permitting Naive Bayes.</li>
 * <li> -p: Minimum value of p </li>
 * <li> -t: Tie Threshold </li>
 * <li> -c: Split Confidence </li>
 * <li> -g: GracePeriod, the number of instances a leaf should observe between split attempts </li>
 * <li> -z: Prediction function to use. Ex: TargetMean </li>
 * <li> -r: learningRatio </li>
 * <li> -w: Learn ordered or unordered rule </li>
 * <li> -x: randomSeed<li>
 * </ul>
 * 
 * @author E. Almeida, J. Gama
 * @version $Revision: 1 $
 */

public class AMRules extends AbstractClassifier implements Regressor {

    private static final long serialVersionUID = 1L;

    protected AutoExpandVector<AttributeClassObserver> attributeObservers;

    protected ArrayList<ArrayList<Double>> saveBestValGlobalSDR = new ArrayList<ArrayList<Double>>(); // For each attribute contains the best value of SDR and its cutPoint.

    protected ArrayList<Double> saveTheBest = new ArrayList<Double>(); // Contains the best attribute.

    protected ArrayList<Rule> ruleSet = new ArrayList<Rule>();

    protected ArrayList<Rule> ruleSetAnomalies = new ArrayList<Rule>();

    protected ArrayList<Integer> ruleAnomaliesIndex = new ArrayList<Integer>();

    protected ArrayList<ArrayList<Integer>> caseAnomaly = new ArrayList<ArrayList<Integer>>();

    protected ArrayList<ArrayList<ArrayList<Double>>> ruleAttribAnomalyStatistics = new ArrayList<ArrayList<ArrayList<Double>>>();

    protected ArrayList<Double> targetValue = new ArrayList<Double>(); // Target value for each rule.

    protected ArrayList<Double> numTargetValue = new ArrayList<Double>(); // Number of target value seen by each rule.

    protected ArrayList<Double> ruleTargetMean = new ArrayList<Double>(); // Target mean of each rule.

    protected ArrayList<Double> ruleTargetMeanAnomalies = new ArrayList<Double>();

    protected double[] weightAttributeDefault; //The Perceptron weights. 

    protected DoubleVector saveBestGlobalSDR = new DoubleVector(); // For each attribute contains the best value of SDR.

    protected DoubleVector attributeStatisticsDefault = new DoubleVector(); // Statistic used for error calculations.

    protected DoubleVector squaredAttributeStatisticsDefault = new DoubleVector(); // Statistics used for  anomaly detection in the test data.

    protected DoubleVector attributesProbabilityDefault = new DoubleVector(); // Probalility of each attribute of default rule.

    protected Instance instance;

    protected boolean resetDefault = true; // If the model should be reset or not.

    // Statistics used for normalize actualClass and predictedClass.
    protected double actualClassStatisticsDefault = 0.0;

    protected double squaredActualClassStatisticsDefault = 0.0;

    protected double sumTotalLeft; // The sum total of all target attibute values for instances falling to the left or on the split point.

    protected double sumTotalRight; // The sum total of all target attibute values for instances falling to the right of the split point.

    protected double sumSqTotalLeft; //The corresponding sums of squared target attribute values.

    protected double sumSqTotalRight;

    protected double rightTotal;

    protected double total;

    protected double maxSDR = 0.0; //Standard deviation reduction of the best split.

    protected double splitpoint = 0.0; //Value of the best split.

    protected double symbolTemp = 0.0;

    protected double symbol = 0.0;

    protected double sumTotalTemp = 0.0;

    protected double sumTotal = 0.0;

    protected double numSomaTotalTemp = 0.0;

    protected double numSomaTotal = 0.0;

    protected double learnRateDecay = 0.001;

    protected double initLearnRate = 0.1;

    protected int instancesSeenDefault = 0; // The number of instances seen by the DefaultRule.

    protected int instancesSeenDefaultTest = 0; // The number of test instances seen by the DefaultRule.

    protected int numInstTest = 0;

    protected int numInstance = 0;

    protected int maxInt = Integer.MAX_VALUE;

    Node root;

    Predicates pred;

    public static final double NORMAL_CONSTANT = Math.sqrt(2 * Math.PI);

    public FloatOption splitConfidenceOption = new FloatOption("splitConfidence", 'c',
            "The allowable error in split decision, values closer to 0 will take longer to decide.", 0.0000001, 0.0,
            1.0);

    public FloatOption tieThresholdOption = new FloatOption("tieThreshold", 't',
            "Threshold below which a split will be forced to break ties.", 0.05, 0.0, 1.0);

    public FloatOption pageHinckleyAlphaOption = new FloatOption("PageHinckleyAlpha", 'a',
            "The alpha value to use in the Page Hinckley change detection tests.", 0.005, 0.0, 1.0);

    public FloatOption anomalyProbabilityThresholdOption = new FloatOption("anomalyprobabilityThreshold", 'o',
            "The threshold value.", 0.99, 0.0, 1.0);

    public FloatOption probabilityThresholdOption = new FloatOption("probabilityThreshold", 'k',
            "The threshold value.", 0.10, 0.0, 1.0);

    public IntOption pageHinckleyThresholdOption = new IntOption("PageHinckleyThreshold", 'h',
            "The threshold value to be used in the Page Hinckley change detection tests.", 50, 0,
            Integer.MAX_VALUE);

    public IntOption anomalyNumInstThresholdOption = new IntOption("anomalyThreshold", 'i',
            "The threshold value to be used in the anomaly detection.", 15, 0, Integer.MAX_VALUE);

    public IntOption gracePeriodOption = new IntOption("gracePeriod", 'g',
            "The number of instances a leaf should observe between split attempts.", 200, 0, Integer.MAX_VALUE);

    public FloatOption learningRatioOption = new FloatOption("learningRatio", 'w',
            "Learning ratio to use for training the Perceptrons in the leaves.", 0.01);

    public FloatOption seedOption = new FloatOption("randomSeed", 'x',
            "The alpha value to use in the Page Hinckley change detection tests.", 100);

    public MultiChoiceOption predictionFunctionOption = new MultiChoiceOption("predictionFunctionOption", 'z',
            "The prediction function to use.", new String[] { "Adaptative", "Perceptron", "Target Mean" },
            new String[] { "Adaptative", "Perceptron", "Target Mean" }, 0);

    public FlagOption orderedRulesOption = new FlagOption("orderedRules", 'r', "orderedRules.");

    public FlagOption anomalyDetectionOption = new FlagOption("anomalyDetection", 'u', "anomaly Detection.");

    public FlagOption learningRatio_Decay_or_Const_Option = new FlagOption("learningRatio_Decay_or_Const", 'd',
            "learning Ratio Decay or const parameter.");

    @Override
    public boolean isRandomizable() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public double[] getVotesForInstance(Instance inst) {
        double[] votes = new double[1];
        this.numInstTest = this.numInstTest + 1;
        switch (this.predictionFunctionOption.getChosenIndex()) { // Adaptative strategy.
        case 0: {
            if (this.orderedRulesOption.isSet()) { //Ordered rule.
                if (this.numInstTest > 100) {
                    double perceptronPrediction = getVotesOrderedRulesPerceptron(inst);
                    double targetMeanPrediction = getVotesOrderedRulesTargetMean(inst);
                    double perceptronError = Math.abs(inst.classValue() - perceptronPrediction);
                    double targetMeanError = Math.abs(inst.classValue() - targetMeanPrediction);
                    if (perceptronError < targetMeanError) {
                        votes[0] = perceptronPrediction;
                    } else {
                        votes[0] = targetMeanPrediction;
                    }
                } else {
                    votes[0] = getVotesOrderedRulesTargetMean(inst);
                }
            } else { //Unordered rule.
                if (this.numInstTest > 100) {
                    double perceptronPrediction = getVotesUnorderedRulesPerceptron(inst);
                    double targetMeanPrediction = getVotesUnorderedRulesTargetMean(inst);
                    double perceptronError = Math.abs(inst.classValue() - perceptronPrediction);
                    double targetMeanError = Math.abs(inst.classValue() - targetMeanPrediction);
                    if (perceptronError < targetMeanError) {
                        votes[0] = perceptronPrediction;
                    } else {
                        votes[0] = targetMeanPrediction;
                    }
                } else {
                    votes[0] = getVotesUnorderedRulesTargetMean(inst);
                }
            }
            break;
        }
        case 1: // Perceptron strategy.
        {
            if (this.orderedRulesOption.isSet()) { //Ordered rule.
                if (this.numInstTest > 100) {
                    votes[0] = getVotesOrderedRulesPerceptron(inst);
                } else {
                    votes[0] = getVotesOrderedRulesTargetMean(inst);
                }
            } else { //Unordered rule.
                if (this.numInstTest > 100) {
                    votes[0] = getVotesUnorderedRulesPerceptron(inst);
                } else {
                    votes[0] = getVotesUnorderedRulesTargetMean(inst);
                }
            }
            break;
        }
        case 2: // Target mean strategy.
        {
            if (this.orderedRulesOption.isSet()) {
                votes[0] = getVotesOrderedRulesTargetMean(inst);
            } else {
                votes[0] = getVotesUnorderedRulesTargetMean(inst);
            }
            break;
        }
        }
        return votes;
    }

    @Override
    protected Measurement[] getModelMeasurementsImpl() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void resetLearningImpl() {
        // TODO Auto-generated method stub
        this.attributeObservers = new AutoExpandVector<AttributeClassObserver>();
    }

    @Override
    public void trainOnInstanceImpl(Instance inst) {
        this.numInstance = this.numInstance + 1;
        int countRuleFiredTrue = 0;
        boolean ruleFired = false;
        this.instance = inst;
        for (int j = 0; j < this.ruleSet.size(); j++) {
            if (this.ruleSet.get(j).ruleEvaluate(inst) == true) {
                countRuleFiredTrue = countRuleFiredTrue + 1;
                this.saveBestValGlobalSDR = new ArrayList<ArrayList<Double>>();
                this.saveBestGlobalSDR = new DoubleVector();
                this.saveTheBest = new ArrayList<Double>();
                double anomaly = computeAnomaly(this.ruleSet.get(j), j, inst); // compute anomaly
                if ((this.ruleSet.get(j).instancesSeen <= this.anomalyNumInstThresholdOption.getValue())
                        || (anomaly < this.anomalyProbabilityThresholdOption.getValue()
                                && this.anomalyDetectionOption.isSet())
                        || !this.anomalyDetectionOption.isSet()) {
                    for (int i = 0; i < inst.numAttributes() - 1; i++) {
                        int instAttIndex = modelAttIndexToInstanceAttIndex(i, inst);
                        AttributeClassObserver obs = this.ruleSet.get(j).observers.get(i);
                        if (obs == null) {
                            obs = inst.attribute(instAttIndex).isNominal() ? newNominalClassObserver()
                                    : newNumericClassObserverRegression();
                            this.ruleSet.get(j).observers.set(i, obs);
                        }
                        obs.observeAttributeTarget(inst.value(instAttIndex), inst.classValue());
                    }

                    double RuleError = computeRuleError(inst, this.ruleSet.get(j), j); // compute rule error
                    boolean ph = PageHinckleyTest(RuleError, this.pageHinckleyThresholdOption.getValue(),
                            this.ruleSet.get(j));
                    if (ph == true) { //Page Hinckley test.
                        //Pruning rule set.
                        //   System.out.print("Pruning rule set \n");
                        this.ruleSet.remove(j);
                        this.targetValue.remove(j);
                        this.numTargetValue.remove(j);
                        this.ruleTargetMean.remove(j);
                    } else {
                        this.expandeRule(this.ruleSet.get(j), j, inst); //Expand the rule.
                    }
                }
                if (this.orderedRulesOption.isSet()) { // Ordered rules
                    break;
                }
            }
        }
        if (countRuleFiredTrue > 0) {
            ruleFired = true;
        } else {
            ruleFired = false;
        }
        if (ruleFired == false) { //Default rule
            this.saveBestValGlobalSDR = new ArrayList<ArrayList<Double>>();
            this.saveBestGlobalSDR = new DoubleVector();
            this.saveTheBest = new ArrayList<Double>();
            double anomalies = computeAnomalyDefaultRules(inst);
            if ((instancesSeenDefault <= this.anomalyNumInstThresholdOption.getValue())
                    || (anomalies < this.anomalyProbabilityThresholdOption.getValue()
                            && this.anomalyDetectionOption.isSet())
                    || !this.anomalyDetectionOption.isSet()) {
                for (int i = 0; i < inst.numAttributes() - 1; i++) {
                    int instAttIndex = modelAttIndexToInstanceAttIndex(i, inst);
                    AttributeClassObserver obs = this.attributeObservers.get(i);
                    if (obs == null) {
                        obs = inst.attribute(instAttIndex).isNominal() ? newNominalClassObserver()
                                : newNumericClassObserverRegression();
                        this.attributeObservers.set(i, obs);
                    }
                    obs.observeAttributeTarget(inst.value(instAttIndex), inst.classValue());
                }
                initialyPerceptron(inst); // Initialize Perceptron if necessary.  
                this.updateAttWeight(inst, this.weightAttributeDefault, this.squaredActualClassStatisticsDefault,
                        this.actualClassStatisticsDefault, this.squaredAttributeStatisticsDefault,
                        this.attributeStatisticsDefault, this.instancesSeenDefault, resetDefault); // Update weights. Ensure actual class and the predicted class are normalised first.
                this.updatedefaultRuleStatistics(inst); //Update the default rule statistics.
                this.createRule(inst);//This function creates a rule
            }
        }
    }

    @Override
    public void getModelDescription(StringBuilder out, int indent) {
        if (this.anomalyDetectionOption.isSet()) {
            this.getModelDescriptionAnomalyDetection(out, indent); //Anomaly detection
        } else {
            this.getModelDescriptionNoAnomalyDetection(out, indent); //No anomaly detection
        }
    }

    public void getModelDescriptionAnomalyDetection(StringBuilder out, int indent) { //ModelDescription anomaly detection
        StringUtils.appendNewline(out);
        for (int k = 0; k < this.ruleSetAnomalies.size(); k++) {
            StringUtils.appendIndented(out, indent, "Case: " + this.caseAnomaly.get(k).get(0) + "   Anomaly Score: "
                    + this.caseAnomaly.get(k).get(1) + "%");
            StringUtils.appendNewline(out);
            if (this.ruleSetAnomalies.get(k).predicateSet.isEmpty()) {
                StringUtils.appendNewline(out);
                StringUtils.appendIndented(out, indent, "Default Rule { } -> ");
                StringUtils.appendIndented(out, indent,
                        "TargetAverage: " + round(this.ruleTargetMeanAnomalies.get(k)) + "  ");
                StringUtils.appendNewline(out);
            } else {
                StringUtils.appendIndented(out, indent, "Rule " + this.ruleAnomaliesIndex.get(k) + ": ");
                for (int i = 0; i < this.ruleSetAnomalies.get(k).predicateSet.size(); i++) {
                    if (this.ruleSetAnomalies.get(k).predicateSet.size() == 1) {
                        if (this.ruleSetAnomalies.get(k).predicateSet.get(i).getSymbol() == -1.0) {
                            String nam = this.instance.attribute(
                                    (int) this.ruleSetAnomalies.get(k).predicateSet.get(i).getAttributeValue())
                                    .name();
                            StringUtils.appendIndented(out, indent, nam + " <= "
                                    + round(this.ruleSetAnomalies.get(k).predicateSet.get(i).getValue()) + " --> ");
                            StringUtils.appendIndented(out, indent,
                                    "TargetAverage: " + round(this.ruleTargetMeanAnomalies.get(k)) + "  ");
                            StringUtils.appendNewline(out);
                        } else {
                            String nam = this.instance.attribute(
                                    (int) this.ruleSetAnomalies.get(k).predicateSet.get(i).getAttributeValue())
                                    .name();
                            StringUtils.appendIndented(out, indent, nam + " > "
                                    + round(this.ruleSetAnomalies.get(k).predicateSet.get(i).getValue()) + " --> ");
                            StringUtils.appendIndented(out, indent,
                                    "TargetAverage: " + round(this.ruleTargetMeanAnomalies.get(k)) + "  ");
                            StringUtils.appendNewline(out);
                        }
                    } else {
                        if (this.ruleSetAnomalies.get(k).predicateSet.get(i).getSymbol() == -1.0) {
                            String nam = this.instance.attribute(
                                    (int) this.ruleSetAnomalies.get(k).predicateSet.get(i).getAttributeValue())
                                    .name();
                            StringUtils.appendIndented(out, indent, nam + " <= "
                                    + round(ruleSetAnomalies.get(k).predicateSet.get(i).getValue()) + " ");
                        } else {
                            String nam = this.instance.attribute(
                                    (int) this.ruleSetAnomalies.get(k).predicateSet.get(i).getAttributeValue())
                                    .name();
                            StringUtils.appendIndented(out, indent, nam + " > "
                                    + round(this.ruleSetAnomalies.get(k).predicateSet.get(i).getValue()) + " ");
                        }
                        if (i < this.ruleSetAnomalies.get(k).predicateSet.size() - 1) {
                            StringUtils.appendIndented(out, indent, "and ");
                        } else {
                            StringUtils.appendIndented(out, indent, " --> ");
                            StringUtils.appendIndented(out, indent,
                                    "TargetAverage: " + round(this.ruleTargetMeanAnomalies.get(k)) + "  ");
                            StringUtils.appendNewline(out);
                        }
                    }
                }
            }
            for (int z = 0; z < this.ruleAttribAnomalyStatistics.get(k).size(); z++) {
                String s = String.format("%.3e", this.ruleAttribAnomalyStatistics.get(k).get(z).get(4));
                StringUtils.appendIndented(out, indent,
                        instance.attribute(this.ruleAttribAnomalyStatistics.get(k).get(z).get(0).intValue()).name()
                                + "=" + round(this.ruleAttribAnomalyStatistics.get(k).get(z).get(1)) + "   ("
                                + round(this.ruleAttribAnomalyStatistics.get(k).get(z).get(2)) + " +- "
                                + round(this.ruleAttribAnomalyStatistics.get(k).get(z).get(3)) + ")   P=" + s);
                StringUtils.appendNewline(out);
            }
            StringUtils.appendNewline(out);
            StringUtils.appendNewline(out);
        }

    }

    public void getModelDescriptionNoAnomalyDetection(StringBuilder out, int indent) { //ModelDescription no anomaly detection
        StringUtils.appendNewline(out);
        StringUtils.appendIndented(out, indent, "Default Rule { } -> ");
        StringUtils.appendIndented(out, indent,
                "TargetAverage: " + round(observersDistrib(this.instance, this.attributeObservers)) + "  ");
        StringUtils.appendNewline(out);
        StringUtils.appendIndented(out, indent, "Number of Rule: " + this.ruleSet.size());
        StringUtils.appendNewline(out);
        StringUtils.appendNewline(out);
        for (int k = 0; k < this.ruleSet.size(); k++) {
            StringUtils.appendIndented(out, indent, "Rule " + (k + 1) + ": ");
            for (int i = 0; i < this.ruleSet.get(k).predicateSet.size(); i++) {
                if (this.ruleSet.get(k).predicateSet.size() == 1) {
                    if (this.ruleSet.get(k).predicateSet.get(i).getSymbol() == -1.0) {
                        String nam = this.instance
                                .attribute((int) ruleSet.get(k).predicateSet.get(i).getAttributeValue()).name();
                        StringUtils.appendIndented(out, indent,
                                nam + " <= " + ruleSet.get(k).predicateSet.get(i).getValue() + " --> ");
                        StringUtils.appendIndented(out, indent,
                                "TargetAverage: " + round(this.ruleTargetMean.get(k)) + "  ");
                        StringUtils.appendNewline(out);
                        StringUtils.appendNewline(out);
                    } else {
                        String nam = this.instance
                                .attribute((int) this.ruleSet.get(k).predicateSet.get(i).getAttributeValue())
                                .name();
                        StringUtils.appendIndented(out, indent,
                                nam + " > " + this.ruleSet.get(k).predicateSet.get(i).getValue() + " --> ");
                        StringUtils.appendIndented(out, indent,
                                "TargetAverage: " + round(this.ruleTargetMean.get(k)) + "  ");
                        StringUtils.appendNewline(out);
                        StringUtils.appendNewline(out);
                    }
                } else {
                    if (this.ruleSet.get(k).predicateSet.get(i).getSymbol() == -1.0) {
                        String nam = this.instance
                                .attribute((int) this.ruleSet.get(k).predicateSet.get(i).getAttributeValue())
                                .name();
                        StringUtils.appendIndented(out, indent,
                                nam + " <= " + this.ruleSet.get(k).predicateSet.get(i).getValue() + " ");
                    } else {
                        String nam = this.instance
                                .attribute((int) this.ruleSet.get(k).predicateSet.get(i).getAttributeValue())
                                .name();
                        StringUtils.appendIndented(out, indent,
                                nam + " > " + this.ruleSet.get(k).predicateSet.get(i).getValue() + " ");
                    }
                    if (i < this.ruleSet.get(k).predicateSet.size() - 1) {
                        StringUtils.appendIndented(out, indent, "and ");
                    } else {
                        StringUtils.appendIndented(out, indent, " --> ");
                        StringUtils.appendIndented(out, indent,
                                "TargetAverage: " + round(this.ruleTargetMean.get(k)) + "  ");
                        StringUtils.appendNewline(out);
                        StringUtils.appendNewline(out);
                    }
                }
            }
            StringUtils.appendNewline(out);
        }
    }

    //Compute the Standard deviation reduction.
    protected double computeSDR() {
        double standardDR = 0.0; // Standard deviation reduction.
        double sdS = 0.0;
        double sdSL = 0.0;
        double sdSR = 0.0;
        double NL = this.total - this.rightTotal;
        double NR = this.rightTotal;
        double N = NL + NR;
        double sumTotal = this.sumTotalLeft + this.sumTotalRight;
        double sumSqTotal = this.sumSqTotalLeft + this.sumSqTotalRight;
        sdS = Math.sqrt((1 / N) * (sumSqTotal - (1 / N) * Math.pow(sumTotal, 2)));
        sdSL = Math.sqrt((1 / NL) * (this.sumSqTotalLeft - (1 / NL) * Math.pow(this.sumTotalLeft, 2)));
        sdSR = Math.sqrt((1 / NR) * (this.sumSqTotalRight - (1 / NR) * Math.pow(this.sumTotalRight, 2)));
        if (sdSL >= sdSR) {
            this.symbolTemp = -1.0;
            this.sumTotalTemp = this.sumTotalLeft;
            this.numSomaTotalTemp = NL;
        } else {
            this.symbolTemp = 1.0;
            this.sumTotalTemp = sumTotalRight;
            this.numSomaTotalTemp = NR;
        }
        standardDR = sdS - (NL / N) * sdSL - (NR / N) * sdSR;
        return standardDR;
    }

    //Get best and second best attributes
    protected double[] getBestSecondBestSDR(DoubleVector SDR) {
        double[] SDRValues = new double[2];
        double best = 0.0;
        double secondBest = 0.0;
        for (int i = 0; i < SDR.numValues(); i++) {
            if (SDR.getValue(i) > best) {
                secondBest = best;
                best = SDR.getValue(i);
            } else {
                if (SDR.getValue(i) > secondBest) {
                    secondBest = SDR.getValue(i);
                }
            }
        }
        SDRValues[0] = best;
        SDRValues[1] = secondBest;

        return SDRValues;
    }

    //Find the spit point with lower value SDR.
    protected void findBestSplit(Node root1) {
        if (root1.left != null) {
            findBestSplit(root1.left);
        }
        this.sumTotalLeft = this.sumTotalLeft + root1.lessThan[0];
        this.sumTotalRight = this.sumTotalRight - root1.lessThan[0];
        this.sumSqTotalLeft = this.sumSqTotalLeft + root1.lessThan[1];
        this.sumSqTotalRight = this.sumSqTotalRight - root1.lessThan[1];
        this.rightTotal = this.rightTotal - root1.lessThan[2];
        double standardDR = computeSDR();
        if (this.maxSDR < standardDR) {
            this.maxSDR = standardDR;
            this.splitpoint = root1.cut_point;
            this.symbol = this.symbolTemp;
            this.sumTotal = this.sumTotalTemp;
            this.numSomaTotal = this.numSomaTotalTemp;
        }
        if (root1.right != null) {
            findBestSplit(root1.right);
        }
        this.sumTotalLeft = this.sumTotalLeft - root1.lessThan[0];
        this.sumTotalRight = this.sumTotalRight + root1.lessThan[0];
        this.sumSqTotalLeft = this.sumSqTotalLeft - root1.lessThan[1];
        this.sumSqTotalRight = this.sumSqTotalRight + root1.lessThan[1];
        this.rightTotal = this.rightTotal + root1.lessThan[2];
    }

    // This function save all the informations about the best attribute.
    public void theBestAttributes(Instance instance, AutoExpandVector<AttributeClassObserver> observersParameter) {
        for (int z = 0; z < instance.numAttributes() - 1; z++) {
            int instAttIndex = modelAttIndexToInstanceAttIndex(z, instance);
            if (instance.attribute(instAttIndex).isNumeric()) {
                this.root = ((BinaryTreeNumericAttributeClassObserverRegression) observersParameter.get(z)).root1;
                this.sumTotalLeft = 0.0;
                this.sumTotalRight = this.root.lessThan[0] + this.root.greaterThan[0];
                this.sumSqTotalLeft = 0.0;
                this.sumSqTotalRight = this.root.lessThan[1] + this.root.greaterThan[1];
                this.rightTotal = this.total = this.root.lessThan[2] + this.root.greaterThan[2];
                this.maxSDR = 0.0;
                this.symbol = 0.0;
                this.sumTotal = 0.0;
                this.numSomaTotal = 0.0;
                findBestSplit(this.root); // The best value (SDR) of a numeric attribute.
                ArrayList<Double> saveTheBestAtt = new ArrayList<Double>(); // Contains the best attribute.
                saveTheBestAtt.add(this.splitpoint);
                saveTheBestAtt.add(this.maxSDR);
                saveTheBestAtt.add(this.symbol);
                saveTheBestAtt.add(this.sumTotal);
                saveTheBestAtt.add(this.numSomaTotal);
                this.saveBestValGlobalSDR.add(saveTheBestAtt);
                this.saveBestGlobalSDR.setValue(z, this.maxSDR);
            }
        }
    }

    //Check if the best attribute is really the best.
    public boolean checkBestAttrib(double n) {
        boolean isTheBest = false;
        double[] SDRValues = getBestSecondBestSDR(this.saveBestGlobalSDR);
        double bestSDR = SDRValues[0];
        double secondBestSDR = SDRValues[1];
        double range = Utils.log2(1);
        double hoeffdingBound = computeHoeffdingBound(range, this.splitConfidenceOption.getValue(), n);
        double r = secondBestSDR / bestSDR;
        double upperBound = r + hoeffdingBound;
        if ((upperBound < 1) || (hoeffdingBound < this.tieThresholdOption.getValue())) {
            for (int i = 0; i < this.saveBestValGlobalSDR.size(); i++) {
                if (bestSDR == (this.saveBestValGlobalSDR.get(i).get(1))) {
                    this.saveTheBest.add(this.saveBestValGlobalSDR.get(i).get(0));
                    this.saveTheBest.add(this.saveBestValGlobalSDR.get(i).get(1));
                    this.saveTheBest.add(this.saveBestValGlobalSDR.get(i).get(2));
                    this.saveTheBest.add((double) i);
                    this.saveTheBest.add(this.saveBestValGlobalSDR.get(i).get(3));
                    this.saveTheBest.add(this.saveBestValGlobalSDR.get(i).get(4));
                    break;
                }
            }
            isTheBest = true;
        } else {
            isTheBest = false;
        }
        return isTheBest;
    }

    //Hoeffding Bound 
    public double computeHoeffdingBound(double range, double confidence, double n) {
        return Math.sqrt(((range * range) * Math.log(1.0 / confidence)) / (2.0 * n));
    }

    //Mean
    public double computeMean(double sum, int size) {
        return sum / size;
    }

    //Standard Deviation
    public double computeSD(double squaredVal, double val, int size) {
        return Math.sqrt((squaredVal - ((val * val) / size)) / size);
    }

    //Attribute probability
    public double computeProbability(double mean, double sd, double value) {
        sd = sd + 0.00001;
        double probability = 0.0;
        double diff = value - mean;
        if (sd > 0.0) {
            double k = (Math.abs(value - mean) / sd);
            if (k > 1.0) {
                probability = 1.0 / (k * k);
            } else {
                probability = Math.exp(-(diff * diff / (2.0 * sd * sd)));
            }
        }
        return probability;
    }

    //Compute anomalies
    public double computeAnomaly(Rule rl, int ruleIndex, Instance inst) {
        ArrayList<Integer> caseAnomalyTemp = new ArrayList<Integer>();
        ArrayList<ArrayList<Double>> AttribAnomalyStatisticTemp2 = new ArrayList<ArrayList<Double>>();
        double D = 0.0;
        double N = 0.0;
        if (rl.instancesSeen > this.anomalyNumInstThresholdOption.getValue()
                && this.anomalyDetectionOption.isSet()) {
            for (int x = 0; x < inst.numAttributes() - 1; x++) {
                ArrayList<Double> AttribAnomalyStatisticTemp = new ArrayList<Double>();
                if (inst.attribute(x).isNumeric()) {
                    double mean = computeMean(rl.attributeStatistics.getValue(x), rl.instancesSeen);
                    double sd = computeSD(rl.squaredAttributeStatistics.getValue(x),
                            rl.attributeStatistics.getValue(x), rl.instancesSeen);
                    double probability = computeProbability(mean, sd, inst.value(x));
                    if (probability != 0.0) {
                        D = D + Math.log(probability);
                        if (probability < this.probabilityThresholdOption.getValue()) { //0.10
                            N = N + Math.log(probability);
                            AttribAnomalyStatisticTemp.add((double) x);
                            AttribAnomalyStatisticTemp.add(inst.value(x));
                            AttribAnomalyStatisticTemp.add(mean);
                            AttribAnomalyStatisticTemp.add(sd);
                            AttribAnomalyStatisticTemp.add(probability);
                            AttribAnomalyStatisticTemp2.add(AttribAnomalyStatisticTemp);
                        }
                    }
                }
            }
        }
        double anomaly = Math.abs(N / D);
        if (anomaly >= this.anomalyProbabilityThresholdOption.getValue()) {
            caseAnomalyTemp.add(this.numInstance);
            double val = anomaly * 100;
            caseAnomalyTemp.add((int) val);
            this.caseAnomaly.add(caseAnomalyTemp);
            this.ruleSetAnomalies.add(this.ruleSet.get(ruleIndex));
            this.ruleTargetMeanAnomalies.add(this.ruleTargetMean.get(ruleIndex));
            this.ruleAnomaliesIndex.add(ruleIndex + 1);
            this.ruleAttribAnomalyStatistics.add(AttribAnomalyStatisticTemp2);
        }
        return anomaly;
    }

    //Compute anomalies DefaultRule
    public double computeAnomalyDefaultRules(Instance inst) {
        double D = 0.0;
        double N = 0.0;
        ArrayList<Integer> caseAnomalyTemp = new ArrayList<Integer>();
        ArrayList<ArrayList<Double>> AttribAnomalyStatisticTemp2 = new ArrayList<ArrayList<Double>>();
        if (this.instancesSeenDefault > this.anomalyNumInstThresholdOption.getValue()
                && this.anomalyDetectionOption.isSet()) {
            for (int x = 0; x < inst.numAttributes() - 1; x++) {
                ArrayList<Double> AttribAnomalyStatisticTemp = new ArrayList<Double>();
                if (inst.attribute(x).isNumeric()) {
                    double mean = computeMean(this.attributeStatisticsDefault.getValue(x),
                            this.instancesSeenDefault);
                    double sd = computeSD(this.squaredAttributeStatisticsDefault.getValue(x),
                            this.attributeStatisticsDefault.getValue(x), this.instancesSeenDefault);
                    double probability = computeProbability(mean, sd, inst.value(x));
                    if (probability != 0.0) {
                        D = D + Math.log(probability);
                        if (probability < this.probabilityThresholdOption.getValue()) { //0.10
                            N = N + Math.log(probability);
                            AttribAnomalyStatisticTemp.add((double) x);
                            AttribAnomalyStatisticTemp.add(inst.value(x));
                            AttribAnomalyStatisticTemp.add(mean);
                            AttribAnomalyStatisticTemp.add(sd);
                            AttribAnomalyStatisticTemp.add(probability);
                            AttribAnomalyStatisticTemp2.add(AttribAnomalyStatisticTemp);
                        }
                    }
                }
            }
        }
        double anomalies = Math.abs(N / D);
        if (anomalies >= this.anomalyProbabilityThresholdOption.getValue()) {
            caseAnomalyTemp.add(this.numInstance);
            double val = anomalies * 100;
            caseAnomalyTemp.add((int) val);
            this.caseAnomaly.add(caseAnomalyTemp);
            Rule rule = new Rule();
            this.ruleSetAnomalies.add(rule);
            this.ruleTargetMeanAnomalies.add(observersDistrib(this.instance, this.attributeObservers));
            this.ruleAnomaliesIndex.add(-1);
            this.ruleAttribAnomalyStatistics.add(AttribAnomalyStatisticTemp2);
        }
        return anomalies;
    }

    //Get the number of instances of an attribute.
    protected int observersNumberInstance(Instance inst, AutoExpandVector<AttributeClassObserver> observerss) {
        int numberInstance = 0;
        for (int z = 0; z < inst.numAttributes() - 1; z++) {
            numberInstance = 0;
            int instAttIndex = modelAttIndexToInstanceAttIndex(z, inst);
            if (inst.attribute(instAttIndex).isNumeric()) {
                Node rootNode = ((BinaryTreeNumericAttributeClassObserverRegression) observerss.get(z)).root1;
                if (rootNode != null) {
                    numberInstance = (int) (rootNode.lessThan[2] + rootNode.greaterThan[2]);
                    break;
                }
            }
        }
        return numberInstance;
    }

    //Reanicialized rule statistics
    protected void reanicializeRuleStatistic(Rule rl) {
        rl.reset = false;
        rl.instancesSeen = 0;
        rl.actualClassStatistics = 0.0;
        rl.squaredActualClassStatistics = 0.0;
        rl.attributeStatistics = new DoubleVector();
        rl.squaredAttributeStatistics = new DoubleVector();
        rl.attributesProbability = new DoubleVector();
        rl.PHmT = 0;
        rl.PHMT = Double.MAX_VALUE;
        rl.XiSum = 0; //Absolute error
    }

    // Output the prediction made by perceptron on the given instance and an set of rules
    public double prediction(Instance inst, double[] weightAtt, double squaredActualClassStatistics,
            double actualClassStatistics, int instancesSeen, boolean reset) {
        double prediction = 0;
        if (reset == false) {
            for (int j = 0; j < inst.numAttributes() - 1; j++) {
                if (inst.attribute(j).isNumeric()) {
                    prediction += weightAtt[j] * inst.value(j);
                }
            }
            prediction += weightAtt[inst.numAttributes() - 1];
        }
        double sdPredictedClass = computeSD(squaredActualClassStatistics, actualClassStatistics, instancesSeen);
        double outputDesnorm = 0;
        if (sdPredictedClass > 0.0000001) {
            outputDesnorm = 3 * prediction * sdPredictedClass + (actualClassStatistics / instancesSeen);
        }
        return outputDesnorm;
    }

    // Update weights. Ensure actual class and the predicted class are normalised first.
    public double updateAttWeight(Instance inst, double[] weightAtt, double squaredActualClassStatistics,
            double actualClassStatistics, DoubleVector squaredAttributeStatistics, DoubleVector attributeStatistics,
            int instancesSeen, boolean reset) {
        double learningRatio = 0.0;
        if (this.learningRatio_Decay_or_Const_Option.isSet()) { //Decaying learning rate option
            learningRatio = this.learningRatioOption.getValue();
        } else {
            learningRatio = initLearnRate / (1 + instancesSeen * this.learnRateDecay);
        }

        double predict = 0.0;
        if (instancesSeen > 30) {
            predict = this.prediction(inst, weightAtt, squaredActualClassStatistics, actualClassStatistics,
                    instancesSeen, reset);
            double sdClass = computeSD(squaredActualClassStatistics, actualClassStatistics, instancesSeen);
            double actualClass = 0.0;
            double predictedClass = 0.0;
            if (sdClass > 0.0000001) {
                actualClass = (inst.classValue() - (actualClassStatistics / instancesSeen)) / (3 * sdClass);
                predictedClass = (predict - (actualClassStatistics / instancesSeen)) / (3 * sdClass);
            }
            double delta = actualClass - predictedClass;
            for (int x = 0; x < inst.numAttributes() - 1; x++) {
                if (inst.attribute(x).isNumeric()) {
                    // Update weights. Ensure attribute values are normalised first.
                    double sd = Math.sqrt((squaredAttributeStatistics.getValue(x)
                            - ((attributeStatistics.getValue(x) * attributeStatistics.getValue(x)) / instancesSeen))
                            / instancesSeen);
                    double instanceValue = 0;
                    instanceValue = (inst.value(x) - (attributeStatistics.getValue(x) / instancesSeen));
                    if (sd > 0.0000001) {
                        instanceValue = instanceValue / (3 * sd);
                    }
                    if (sd == 0.0) {
                        weightAtt[x] = 0.0;
                    } else {
                        weightAtt[x] += learningRatio * delta * instanceValue;
                    }
                }
            }
            weightAtt[inst.numAttributes() - 1] += learningRatio * delta;
        }
        return predict;
    }

    // Get the predict value and the actual class value normalised
    public double[] getPredictionActualValueNormalized(Instance inst, Rule rl, int ruleIndex, double predict) {
        double[] values = new double[2];
        double predictVal = 0.0;
        double classActual = 0.0;
        double sd = computeSD(rl.squaredActualClassStatistics, rl.actualClassStatistics, rl.instancesSeen);
        if (this.predictionFunctionOption.getChosenIndex() == 2) { //Target mean strategy

            predictVal = (this.ruleTargetMean.get(ruleIndex) - (rl.actualClassStatistics / rl.instancesSeen))
                    / (3 * sd);
            classActual = (inst.classValue() - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd);
        } else if (this.predictionFunctionOption.getChosenIndex() == 1) { //Perceptron strategy
            if (rl.instancesSeen <= 30) {
                if (sd > 0.0000001) {
                    predictVal = (ruleTargetMean.get(ruleIndex) - (rl.actualClassStatistics / rl.instancesSeen))
                            / (3 * sd); //Predicted value normalized.
                    classActual = (inst.classValue() - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd); //Class value normalized.
                }
            } else {
                if (sd > 0.0000001) {
                    predictVal = (predict - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd); //Predicted value normalized.
                    classActual = (inst.classValue() - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd); //Class value normalized.
                }
            }
        } else { //Adaptative strategy.
            double predictValTargetMean = 0;
            double predictValPerceptron = 0;
            if (sd > 0.0000001) {
                predictValTargetMean = (this.ruleTargetMean.get(ruleIndex)
                        - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd);
                predictValPerceptron = (predict - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd);
                classActual = (inst.classValue() - (rl.actualClassStatistics / rl.instancesSeen)) / (3 * sd);
            }
            double absolutErrorTargetMean = Math.abs(classActual - predictValTargetMean); //Target mean strategy absolute error.
            double absolutErrorPerceptron = Math.abs(classActual - predictValPerceptron); //Perceptron strategy absolute error.
            if (absolutErrorTargetMean < absolutErrorPerceptron) {
                predictVal = predictValTargetMean;
            } else {
                predictVal = predictValPerceptron;
            }
        }
        values[0] = predictVal;
        values[1] = classActual;

        return values;
    }

    // Update rule statistics
    public void updateRuleStatistics(Instance inst, Rule rl, int ruleIndex) {
        rl.instancesSeen++;
        double targetValueSize = this.numTargetValue.get(ruleIndex) + 1.0;
        double targetVal = this.targetValue.get(ruleIndex) + inst.classValue();
        this.targetValue.set(ruleIndex, targetVal);
        this.numTargetValue.set(ruleIndex, targetValueSize);
        setRuleTarget(this.targetValue.get(ruleIndex), this.numTargetValue.get(ruleIndex), ruleIndex);
        rl.ValorTargetRule = this.ruleTargetMean.get(ruleIndex);
        for (int s = 0; s < inst.numAttributes() - 1; s++) {
            rl.attributeStatistics.addToValue(s, inst.value(s));
            rl.squaredAttributeStatistics.addToValue(s, inst.value(s) * inst.value(s));
        }
        rl.actualClassStatistics += inst.classValue();
        rl.squaredActualClassStatistics += inst.classValue() * inst.classValue();
    }

    //Get rule error
    public double computeRuleError(Instance inst, Rule rl, int ruleIndex) {//
        double predict = this.updateAttWeight(inst, rl.weightAttribute, rl.squaredActualClassStatistics,
                rl.actualClassStatistics, rl.squaredAttributeStatistics, rl.attributeStatistics, rl.instancesSeen,
                rl.reset);
        double[] values = getPredictionActualValueNormalized(inst, rl, ruleIndex, predict);
        double predictVal = values[0];
        double classActual = values[1];

        this.updateRuleStatistics(inst, rl, ruleIndex); // Update rule statistics
        double xi = Math.abs(classActual - predictVal); // Absolute error.
        rl.XiSum += xi;
        double RuleError = xi - (rl.XiSum / rl.instancesSeen) - this.pageHinckleyAlphaOption.getValue();

        return RuleError;
    }

    //This function adds a predicate to a rule and updates the statistics of this rule
    public void AddPredUpdateRuleStatistics(Rule rl, int RuleIndex, double targetValorTotal,
            double contaTargetValorTotal) {
        rl.predicateSet.add(this.pred);
        this.targetValue.set(RuleIndex, targetValorTotal);
        this.numTargetValue.set(RuleIndex, contaTargetValorTotal);
        setRuleTarget(this.targetValue.get(RuleIndex), this.numTargetValue.get(RuleIndex), RuleIndex);
        rl.ValorTargetRule = this.ruleTargetMean.get(RuleIndex);
        reanicializeRuleStatistic(rl);
        rl.observers = new AutoExpandVector<AttributeClassObserver>();
    }

    public void expandeRule(Rule rl, int ruleIndex, Instance inst) {
        ruleTargetData(inst, ruleIndex); // For each rule gets the respective target sum.
        int remainder = (int) Double.MAX_VALUE;
        int numInstanciaObservers = observersNumberInstance(inst, rl.observers); // Number of instances for this rule observers.
        if (numInstanciaObservers != 0 && this.gracePeriodOption.getValue() != 0) {
            remainder = (numInstanciaObservers) % (this.gracePeriodOption.getValue());
        }
        if (remainder == 0) {
            theBestAttributes(inst, rl.observers); // The best value of SDR for each attribute.
            boolean bestAttribute = checkBestAttrib(numInstanciaObservers); // Check if the best attribute value is really the best.
            if (bestAttribute == true) {
                double attributeValue = this.saveTheBest.get(3);
                double symbol = this.saveTheBest.get(2); // <=, > (-1.0, 1.0).
                double value = this.saveTheBest.get(0); // Value of the attribute.
                double targetValorTotal = this.saveTheBest.get(4);
                double contaTargetValorTotal = this.saveTheBest.get(5);
                this.pred = new Predicates(attributeValue, symbol, value);
                int countPred = 0;
                for (int i = 0; i < rl.predicateSet.size(); i++) { // Checks if the new predicate is not yet in the predicateSet. 
                    if (rl.predicateSet.get(i).getAttributeValue() != this.pred.getAttributeValue()
                            || rl.predicateSet.get(i).getSymbol() != this.pred.getSymbol()
                            || rl.predicateSet.get(i).getValue() != this.pred.getValue()) {
                        countPred = countPred + 1;
                    }
                }
                if (countPred == rl.predicateSet.size()) {
                    int countDifPred = 0;
                    ArrayList<Predicates> predicSetTemp = new ArrayList<Predicates>();
                    for (int x = 0; x < rl.predicateSet.size(); x++) {
                        predicSetTemp.add(rl.predicateSet.get(x));
                    }
                    predicSetTemp.add(this.pred);
                    for (int f = 0; f < this.ruleSet.size(); f++) {
                        int countDifPredTemp = 0;
                        if (this.ruleSet.get(f).predicateSet.size() == predicSetTemp.size()) {
                            for (int x = 0; x < this.ruleSet.get(f).predicateSet.size(); x++) {
                                if (this.ruleSet.get(f).predicateSet.get(x).getAttributeValue() == predicSetTemp
                                        .get(x).getAttributeValue()
                                        && this.ruleSet.get(f).predicateSet.get(x).getSymbol() == predicSetTemp
                                                .get(x).getSymbol()
                                        && this.ruleSet.get(f).predicateSet.get(x).getValue() == predicSetTemp
                                                .get(x).getValue()) {
                                    countDifPredTemp = countDifPredTemp + 1;
                                }
                            }
                            if (countDifPredTemp == predicSetTemp.size()) {
                                break;
                            } else {
                                countDifPred = countDifPred + 1;
                            }
                        } else {
                            countDifPred = countDifPred + 1;
                        }
                    }
                    if (countDifPred == this.ruleSet.size()) {
                        if (this.pred.getSymbol() == 1.0) {
                            int countIqualPred = 0;
                            for (int f = 0; f < rl.predicateSet.size(); f++) {
                                if (this.pred.getAttributeValue() == rl.predicateSet.get(f).getAttributeValue()
                                        && this.pred.getSymbol() == rl.predicateSet.get(f).getSymbol()) {
                                    countIqualPred = countIqualPred + 1;
                                    if (this.pred.getValue() > rl.predicateSet.get(f).getValue()) {
                                        rl.predicateSet.remove(f);
                                        AddPredUpdateRuleStatistics(rl, ruleIndex, targetValorTotal,
                                                contaTargetValorTotal);
                                    }
                                }
                            }
                            if (countIqualPred == 0) {
                                AddPredUpdateRuleStatistics(rl, ruleIndex, targetValorTotal, contaTargetValorTotal);
                            }
                        } else {
                            int countIqualPred = 0;
                            for (int f = 0; f < rl.predicateSet.size(); f++) {
                                if (this.pred.getAttributeValue() == rl.predicateSet.get(f).getAttributeValue()
                                        && pred.getSymbol() == rl.predicateSet.get(f).getSymbol()) {
                                    countIqualPred = countIqualPred + 1;
                                    if (this.pred.getValue() < rl.predicateSet.get(f).getValue()) {
                                        rl.predicateSet.remove(f);
                                        AddPredUpdateRuleStatistics(rl, ruleIndex, targetValorTotal,
                                                contaTargetValorTotal);
                                    }
                                }
                            }
                            if (countIqualPred == 0) {
                                AddPredUpdateRuleStatistics(rl, ruleIndex, targetValorTotal, contaTargetValorTotal);
                            }
                        }
                    }
                }
            }
        }
    }

    //Check to see if the set of rules needs updating.
    public boolean PageHinckleyTest(double error, double threshold, Rule rl) {
        rl.PHmT += error; // Update the cumulative mT sum
        if (rl.PHmT < rl.PHMT) { // Update the minimum mT value if the new mT is smaller than the current minimum
            rl.PHMT = rl.PHmT;
        }
        return rl.PHmT - rl.PHMT > threshold; // Return true if the cumulative value - the current minimum is greater than the current threshold
    }

    //This function This expands the rule
    public void expandeRule(Rule rl, Instance inst) {

    }

    //Update the rule target value
    protected void ruleTargetData(Instance inst, int ruleIndex) {
        double target = this.targetValue.get(ruleIndex) + inst.classValue();
        double targetCount = this.numTargetValue.get(ruleIndex) + 1;
        this.targetValue.set(ruleIndex, target);
        this.numTargetValue.set(ruleIndex, targetCount);
    }

    protected void getRuleTarget(double sum, double count, int index) {
        double value = sum / count;
        this.ruleTargetMean.add(value);
    }

    protected void setRuleTarget(double sum, double count, int index) {
        double value = sum / count;
        this.ruleTargetMean.set(index, value);
    }

    //Initialize perceptron if is necessary
    public void initialyPerceptron(Instance inst) {
        if (this.resetDefault == true) {
            this.resetDefault = false;
            this.weightAttributeDefault = new double[inst.numAttributes()];
            this.instancesSeenDefault = 0;
            this.actualClassStatisticsDefault = 0.0;
            this.squaredActualClassStatisticsDefault = 0.0;
            this.attributeStatisticsDefault = new DoubleVector();
            this.squaredAttributeStatisticsDefault = new DoubleVector();
            this.attributesProbabilityDefault = new DoubleVector();
            Random r = new Random();
            long value = (long) seedOption.getValue();
            r.setSeed(value);
            for (int j = 0; j < inst.numAttributes(); j++) {
                this.weightAttributeDefault[j] = 2 * r.nextDouble() - 1;
            }
        }
    }

    //Update the default rule statistics.
    public void updatedefaultRuleStatistics(Instance inst) {
        this.instancesSeenDefault++;
        for (int j = 0; j < inst.numAttributes() - 1; j++) {
            this.attributeStatisticsDefault.addToValue(j, inst.value(j));
            this.squaredAttributeStatisticsDefault.addToValue(j, inst.value(j) * inst.value(j));
        }
        this.actualClassStatisticsDefault += inst.classValue();
        this.squaredActualClassStatisticsDefault += inst.classValue() * inst.classValue();
    }

    //This function creates a rule
    public void createRule(Instance inst) {
        int remainder = (int) Double.MAX_VALUE;
        int numInstanciaObservers = observersNumberInstance(inst, this.attributeObservers);
        if (numInstanciaObservers != 0 && this.gracePeriodOption.getValue() != 0) {
            remainder = (numInstanciaObservers) % (this.gracePeriodOption.getValue());
        }
        if (remainder == 0) {
            theBestAttributes(inst, this.attributeObservers);
            boolean bestAttribute = checkBestAttrib(numInstanciaObservers); // Check if the best attribute value is really the best.
            if (bestAttribute == true) {
                double attributeValue = this.saveTheBest.get(3);
                double symbol = this.saveTheBest.get(2); // <=, > : (0.0, -1.0, 1.0).
                double value = this.saveTheBest.get(0); // Value of the attribute
                double targetValorTotal = this.saveTheBest.get(4);
                double contaTargetValorTotal = this.saveTheBest.get(5);
                this.pred = new Predicates(attributeValue, symbol, value);
                Rule Rl = new Rule(); // Create new rule.
                Rl.predicateSet.add(pred);
                Rl.weightAttribute = new double[inst.numAttributes()];
                System.arraycopy(this.weightAttributeDefault, 0, Rl.weightAttribute, 0,
                        this.weightAttributeDefault.length); //Initialize the rule array of weights.
                reanicializeRuleStatistic(Rl); //Initialize the others statistics of the rule.
                this.ruleSet.add(Rl);
                this.targetValue.add(targetValorTotal);
                this.numTargetValue.add(contaTargetValorTotal);
                getRuleTarget(this.targetValue.get(ruleSet.size() - 1), this.numTargetValue.get(ruleSet.size() - 1),
                        this.ruleSet.size() - 1);
                Rl.ValorTargetRule = this.ruleTargetMean.get(this.ruleSet.size() - 1);
                this.attributeObservers = new AutoExpandVector<AttributeClassObserver>();
            }
        }
    }

    // Get the mean of observer distributions
    protected double observersDistrib(Instance inst, AutoExpandVector<AttributeClassObserver> observerss) {
        double votes = 0.0;
        for (int z = 0; z < inst.numAttributes() - 1; z++) {
            int instAttIndex = modelAttIndexToInstanceAttIndex(z, inst);
            if (inst.attribute(instAttIndex).isNumeric()) {
                if (observerss.get(z) != null) {
                    Node rootNode = ((BinaryTreeNumericAttributeClassObserverRegression) observerss.get(z)).root1;
                    if (rootNode != null) {
                        double sum = rootNode.greaterThan[0] + rootNode.lessThan[0];
                        double numTarget = rootNode.greaterThan[2] + rootNode.lessThan[2];
                        votes = sum / numTarget;
                        break;
                    }
                }
            }
        }
        return votes;
    }

    // The following functions are used for the prediction 
    protected double getVotesUnorderedRulesTargetMean(Instance inst) { //Unordered Rules Target Mean prediction
        double votes = 0.0;
        double sum = 0.0;
        boolean fired = false;
        int countFired = 0;
        int count = 0;
        for (int j = 0; j < this.ruleSet.size(); j++) {
            if (this.ruleSet.get(j).ruleEvaluate(inst) == true) {
                countFired = countFired + 1;
                double value = this.ruleSet.get(j).ValorTargetRule;
                sum = sum + value;
                count = count + 1;
            }
        }
        if (countFired > 0) {
            fired = true;
            votes = sum / count;
        } else {
            fired = false;
        }
        if (fired == false) {
            votes = observersDistrib(inst, this.attributeObservers);
        }
        return votes;
    }

    protected double getVotesOrderedRulesTargetMean(Instance inst) { //Ordered Rules Target Mean prediction
        double votes = 0.0;
        boolean fired = false;
        int countFired = 0;
        for (int j = 0; j < this.ruleSet.size(); j++) {
            if (this.ruleSet.get(j).ruleEvaluate(inst) == true) {
                countFired = countFired + 1;
                double value = this.ruleSet.get(j).ValorTargetRule;
                votes = value;
                break;
            }
        }
        if (countFired > 0) {
            fired = true;
        } else {
            fired = false;
        }
        if (fired == false) {
            votes = observersDistrib(inst, this.attributeObservers);
        }
        return votes;
    }

    protected double getVotesUnorderedRulesPerceptron(Instance inst) { //Unordered Rules Perceptron prediction
        double votes = 0.0;
        double sum = 0.0;
        boolean fired = false;
        int countFired = 0;
        int count = 0;
        for (int j = 0; j < this.ruleSet.size(); j++) {
            if (this.ruleSet.get(j).ruleEvaluate(inst) == true) {
                countFired = countFired + 1;
                double value = this.prediction(inst, this.ruleSet.get(j).weightAttribute,
                        this.ruleSet.get(j).squaredActualClassStatistics, this.ruleSet.get(j).actualClassStatistics,
                        this.ruleSet.get(j).instancesSeen, this.ruleSet.get(j).reset);
                sum = sum + value;
                count = count + 1;
            }
        }
        if (countFired > 0) {
            fired = true;
            votes = sum / count;
        } else {
            fired = false;
        }
        if (fired == false) {
            votes = this.prediction(inst, this.weightAttributeDefault, this.squaredActualClassStatisticsDefault,
                    this.actualClassStatisticsDefault, this.instancesSeenDefault, this.resetDefault);
        }
        return votes;
    }

    protected double getVotesOrderedRulesPerceptron(Instance inst) { //Ordered Rules Perceptron prediction
        double votes = 0.0;
        boolean fired = false;
        int countFired = 0;
        for (int j = 0; j < this.ruleSet.size(); j++) {
            if (this.ruleSet.get(j).ruleEvaluate(inst) == true) {
                countFired = countFired + 1;
                double value = this.prediction(inst, this.ruleSet.get(j).weightAttribute,
                        this.ruleSet.get(j).squaredActualClassStatistics, this.ruleSet.get(j).actualClassStatistics,
                        this.ruleSet.get(j).instancesSeen, this.ruleSet.get(j).reset);
                votes = value;
                break;
            }
        }
        if (countFired > 0) {
            fired = true;
        } else {
            fired = false;
        }
        if (fired == false) {
            votes = this.prediction(inst, this.weightAttributeDefault, this.squaredActualClassStatisticsDefault,
                    this.actualClassStatisticsDefault, this.instancesSeenDefault, this.resetDefault);
        }
        return votes;
    }

    protected AttributeClassObserver newNominalClassObserver() {
        return new NominalAttributeClassObserver();
    }

    protected AttributeClassObserver newNumericClassObserver() {
        return new BinaryTreeNumericAttributeClassObserver();
    }

    protected AttributeClassObserver newNumericClassObserverRegression() {
        return new BinaryTreeNumericAttributeClassObserverRegression();
    }

    //Round an number
    protected BigDecimal round(double val) {
        BigDecimal value = new BigDecimal(val);
        if (val != 0.0) {
            value = value.setScale(3, BigDecimal.ROUND_DOWN);
        }
        return value;
    }

    //Round an number
    protected BigDecimal roundValue(double val) {
        BigDecimal value = new BigDecimal(val);
        if (val != 0.0) {
            value = value.setScale(0, BigDecimal.ROUND_DOWN);
        }
        return value;
    }

}