/* * RapidMiner * * Copyright (C) 2001-2008 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.operator.learner.igss.hypothesis; import java.util.LinkedList; import com.rapidminer.example.Attribute; import com.rapidminer.example.Example; /** Objects of this class represent a conjunctive rule. * All abstract methods of the superclass Hypothesis are implemented. * * @author Dirk Dach * @version $Id: Rule.java,v 1.5 2008/05/09 19:23:25 ingomierswa Exp $ */ public class Rule extends Hypothesis { private static final long serialVersionUID = -1121263970366784202L; /** All literals possible with the given attributeset. */ private Literal[][] allLiterals; /** The premise of the rule.*/ private Literal[] literals; /** The index of the label class this rule predicts aka Y+ or Y-. */ private int prediction; /** Creates a new rule,initializes the regularAttributes and the literals attribute. */ public Rule(Attribute[] regularAttributes, Attribute label, boolean rejectionSampling, boolean createAll) { super(regularAttributes,label, rejectionSampling,createAll); allLiterals = new Literal[regularAttributes.length][]; for (int attributeIndex=0;attributeIndex<regularAttributes.length;attributeIndex++) { allLiterals[attributeIndex]=new Literal[regularAttributes[attributeIndex].getMapping().size()]; for (int valueIndex=0;valueIndex<regularAttributes[attributeIndex].getMapping().size();valueIndex++) { allLiterals[attributeIndex][valueIndex]=new Literal(regularAttributes[attributeIndex],valueIndex,attributeIndex); } } } /** Clones the rule with covered and positive weight.*/ public Hypothesis clone () { Rule clone=new Rule(this.literals, this.prediction); clone.setCoveredWeight(this.getCoveredWeight()); clone.setPositiveWeight(this.getPositiveWeight()); return clone; } /** Construct a new rule with one literal. */ public Rule(Literal literal, int prediction) { super(); this.literals=new Literal[1]; this.literals[0]=literal; this.prediction=prediction; } /** Construct a new rule with the given literals. */ public Rule(Literal[] literals, int prediction) { super(); this.literals=new Literal[literals.length]; for (int i=0;i<literals.length;i++) { this.literals[i]=literals[i]; } this.prediction=prediction; } /** Applies the rule to the given examples. */ public void apply(Example e) { if (this.applicable(e)) { if (rejectionSampling) { coveredWeight++; if ((int)e.getLabel()==this.prediction) { positiveWeight++; } } else { coveredWeight+=e.getWeight(); if ((int)e.getLabel()==this.prediction) { positiveWeight+=e.getWeight(); } } } } /** Test if the rule is applicable to the given examples without updating the corresponding value. */ public boolean applicable(Example e) { boolean success=true; for (int i=0;i<literals.length;i++) { int exampleValue=(int)e.getValue(literals[i].getAttribute()); int ruleValue=literals[i].getValue(); if (exampleValue!=ruleValue) { success=false; break; } } return success; } /** Creates all rules with length<=minComplexity. */ public LinkedList<Hypothesis> init(int minComplexity) { LinkedList<Hypothesis> border=new LinkedList<Hypothesis>(); LinkedList<Hypothesis> result=new LinkedList<Hypothesis>(); //Add all hypothesis of lenght 1 to border. for (int attributeIndex=0;attributeIndex<allLiterals.length;attributeIndex++) { for (int valueIndex=0;valueIndex<allLiterals[attributeIndex].length;valueIndex++) { border.addLast(new Rule(allLiterals[attributeIndex][valueIndex],POSITIVE_CLASS)); // Create h->Y+ first. } } while(!border.isEmpty()) { Rule rule=(Rule)border.removeFirst(); result.addLast(rule); // Add h->Y+ to result. if (createAllHypothesis) { result.addLast(new Rule(rule.getLiterals(),NEGATIVE_CLASS)); // Add h->Y- to result. } //No need to refine anymore if rule length already is equal to minComplexity. if(rule.getComplexity()<minComplexity) { border.addAll(rule.refine()); // Add h->Y+ only } } return result; } /** Creates all successors of the rule that have one more literal. */ public LinkedList<Hypothesis> refine() { LinkedList<Hypothesis> result=new LinkedList<Hypothesis>(); Literal[] lits=new Literal[literals.length+1]; // New rule contains all literals of the old rule for (int i=0;i<literals.length;i++) { lits[i]=literals[i]; } // Create new Rules with the remainig literals with higher indices. // The literals testing the same attribute as the last literal of this rule are excluded. int lastLiteralIndex=literals[literals.length-1].getIndex(); for (int literalIndex=lastLiteralIndex+1;literalIndex<allLiterals.length;literalIndex++) { for (int valueIndex=0;valueIndex<allLiterals[literalIndex].length;valueIndex++) { lits[lits.length-1]=allLiterals[literalIndex][valueIndex]; result.addLast(new Rule(lits,this.prediction)); } } return result; } /** Returns true only if this hypothesis can still be refined.*/ public boolean canBeRefined() { //No literals can be appended if the last literal tests the last attribute. if ( (literals[literals.length-1].getIndex()==allLiterals.length-1) ) { return false; } else { return true; } } /** Returns the index of prediction of this rule*/ public int getPrediction() { return this.prediction; } /** Returns the lenght of the premise of the rule. */ public int getComplexity () { return this.literals.length; } /** Returns true if the two rules have the same premise and make the same perdiction. */ public boolean equals(Object o) { if (!(o instanceof Rule)) return false; Rule otherRule=(Rule) o; if (otherRule.literals.length!=this.literals.length) { return false; } if (otherRule.prediction!=this.prediction) { return false; } boolean result=true; for (int i=0;i<this.literals.length;i++) { if ( !(this.literals[i].equals(otherRule.literals[i])) ) { result=false; break; } } return result; } public int hashCode() { return this.literals.hashCode() ^ Integer.valueOf(this.prediction).hashCode(); } /** Returns a String representation of the rule. */ public String toString() { StringBuffer result = new StringBuffer("IF "); for (int i=0;i<literals.length-1;i++) { result.append(literals[i].toString() + " AND "); } result.append(literals[literals.length-1].toString()); result.append(" THEN ("+getLabel().getName()+"="+getLabel().getMapping().mapIndex(this.getPrediction())+")"); return result.toString(); } /** Returns the literals in the premise of this rule.*/ public Literal[] getLiterals() { return literals; } }