/*
Copyright 2006 by Sean Luke and George Mason University
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package ec.rule;
import ec.*;
import ec.util.*;
import java.io.*;
/*
* RuleSet.java
*
* Created: Tue Feb 20 13:19:00 2001
* By: Liviu Panait and Sean Luke
*/
/**
* RuleSet is a set of Rules, implemented straightforwardly as an arbitrary-length array of Rules.
* A RuleIndividual is simply a list of RuleSets. Most typically, a RuleIndividual contains a
* single RuleSet, containing a variety of Rules.
* RuleSets contain many useful subsetting and modification functions which you can use
* in breeding operators which modify RuleSets and Rules.
*
*
Besides the Rules themselves, the only thing else a RuleSet contains is a pointer to a
* corresponding RuleSetConstraints object, which holds all of its modification parameters.
* See RuleSetConstraints for a description of these parameters.
*
In addition to serialization for checkpointing, RuleSets may read and write themselves to streams in three ways.
*
*
* - writeRuleSet(...,DataOutput)/readRuleSet(...,DataInput) This method
* transmits or receives a RuleSet in binary. It is the most efficient approach to sending
* RuleSets over networks, etc. The default versions of writeRuleSet/readRuleSet reads/writes out the number
* of rules, then calls read/writeRule(...) on each Rule. Override this if you need more functionality.
*
*
- printRuleSet(...,PrintWriter)/readRuleSet(...,LineNumberReader) This
* approach transmits or receives a RuleSet in text encoded such that the RuleSet is largely readable
* by humans but can be read back in 100% by ECJ as well. To do this, these methods will typically encode numbers
* using the ec.util.Code class. These methods are mostly used to write out populations to
* files for inspection, slight modification, then reading back in later on. readRuleSet
* reads in the number of rules, then calls readRule(...) on each new Rule. printRuleSet writes
* out the number of rules, then calls printrule(...) on each new Rule. Again, override this if you need more
* functionality.
*
*
- printRuleSetForHumans(...,PrintWriter) This
* approach prints a RuleSet in a fashion intended for human consumption only.
* printRuleSetForHumans prints out the number of rules, then calles printRuleForHumans
* on each Rule in turn. You may wish to override this to provide more information instead.
* You should handle one of these methods properly
* to ensure RuleSets can be printed by ECJ.
*
Parameters
base.constraints
string |
(name of the rule set constraints) |
Default Base
rule.ruleset
* @author Liviu Panait and Sean Luke
* @version 1.0
*/
public class RuleSet implements Prototype
{
/**
The message to appear when printing the rule set
*/
public final static String N_RULES = "Num: ";
public final static String P_RULESET = "ruleset";
/**
The constraint for the rule set
*/
public static final String P_CONSTRAINTS = "constraints";
/**
An index to a RuleSetConstraints
*/
public byte constraints;
/* Returns the RuleSet's constraints. A good JIT compiler should inline this. */
public final RuleSetConstraints constraints(RuleInitializer initializer)
{
return initializer.ruleSetConstraints[constraints];
}
/**
The rules in the rule set
*/
public Rule[] rules = new Rule[0];
/**
How many rules are there used in the rules array
*/
public int numRules = 0;
public Object clone()
{
try
{
RuleSet newRuleSet = (RuleSet)(super.clone());
// copy the rules over
if( rules != null )
{
newRuleSet.rules = (Rule[])(rules.clone());
}
else
{
newRuleSet.rules = null;
}
for(int x=0;x constraints(initializer).minSize )
{
removeRandomRule( state, thread );
}
while( state.random[thread].nextBoolean( constraints(initializer).p_add ) && numRules < constraints(initializer).maxSize )
{
addRandomRule( state, thread );
}
if( state.random[thread].nextBoolean( constraints(initializer).p_randorder ) )
{
randomizeRulesOrder( state, thread );
}
}
/**
Should be called by pipelines to "fix up" the rulesets before they have been
mutated or crossed over. Override this method to do so.
*/
public void preprocessRules(final EvolutionState state, final int thread)
{
}
/**
Should be called by pipelines to "fix up" the rulesets after they have been
mutated or crossed over. Override this method to do so.
*/
public void postprocessRules(final EvolutionState state, final int thread)
{
}
/**
Randomizes the order of the rules in the rule set. It is helpful when the
order of rule is important for the conflict resolution.
*/
public void randomizeRulesOrder(final EvolutionState state, final int thread)
{
Rule temp;
for( int i = numRules-1 ; i > 0 ; i-- )
{
int j = state.random[thread].nextInt( i+1 );
temp = rules[i];
rules[i] = rules[j];
rules[j] = temp;
}
}
/**
Add a random rule to the rule set
*/
public void addRandomRule(final EvolutionState state, final int thread)
{
Rule newRule = (Rule)(constraints(((RuleInitializer)state.initializer)).rulePrototype.clone());
newRule.reset(state,thread);
addRule(newRule);
}
/**
Add a rule directly to the rule set. Does not copy the rule.
*/
public void addRule( Rule rule )
{
if( ( rules == null && numRules == 0 ) || ( numRules == rules.length ) )
{
Rule[] tempRules;
if( rules == null )
{
tempRules = new Rule[2];
}
else
{
tempRules = new Rule[ (rules.length + 1 ) * 2 ];
}
if( rules != null )
System.arraycopy( rules, 0, tempRules, 0, rules.length );
rules = tempRules;
}
// add the rule and increase the counter
rules[ numRules++ ] = rule;
}
/**
Removes a rule from the rule set and returns it. If index is out of bounds, then
this method returns null. The rules are shifted down --- thus this is O(n).
*/
public Rule removeRule( int index )
{
if (index >= numRules || index < 0 ) return null;
Rule myrule = rules[index];
System.arraycopy(rules, index + 1, rules, index, numRules - index + 1);
/*
// swap to the top
Rule myrule = rules[index];
rules[index] = rules[numRules-1];
*/
numRules--;
return myrule;
}
/**
Removes a randomly-chosen rule from the rule set and returns it. If there are no rules to remove,
this method returns null.
*/
public Rule removeRandomRule( final EvolutionState state, final int thread )
{
if (numRules <= 0) return null;
else return removeRule(state.random[thread].nextInt(numRules));
}
/**
Makes a copy of the rules in another RuleSet and adds the rule copies.
*/
public void join( final RuleSet other )
{
// if there's not enough place to store the new rules, increase space
if( rules.length <= numRules + other.numRules )
{
Rule[] tempRules = new Rule[ rules.length + other.rules.length ];
System.arraycopy( rules, 0, tempRules, 0, numRules );
rules = tempRules;
}
// copy in the new rules
System.arraycopy( other.rules, 0, rules, numRules, other.numRules );
// clone the rules
for(int x=numRules;xsets.
*/
public RuleSet[] split( int[] points, RuleSet[] sets )
{
// Do the first chunk or the whole thing
for(int i=0; i < (points.length > 0 ? points[0] : rules.length); i++)
sets[0].addRule((Rule)(rules[i].clone()) );
if (points.length > 0)
{
// do the in-between chunks
for(int p = 1; p < points.length; p++)
for(int i= points[p-1]; i < points[p]; i++)
sets[p].addRule((Rule)(rules[i].clone()) );
// do the final chunk
for(int i=points[points.length - 1]; i < rules.length; i++)
sets[points.length].addRule((Rule)(rules[i].clone()) );
}
return sets;
}
/**
Splits the rule set into a number of disjoint rule sets, copying the rules and adding
them to the sets as appropriate. Each rule independently
throws a die to determine which ruleset it will go into. Sets must be already allocated.
Comment: This function appends the split rulesets to the existing rulesets already in sets.
*/
public RuleSet[] split( final EvolutionState state, final int thread, RuleSet[] sets )
{
for( int i = 0 ; i < numRules ; i++ )
sets[ state.random[ thread ].nextInt( sets.length ) ].addRule(
(Rule)(rules[i].clone()) );
return sets;
}
/**
Splits the rule set into a two disjoint rule sets, copying the rules and adding
them to the sets as appropriate. The value prob is the probability that an element will
land in the first set. Sets must be already allocated.
Comment: This function appends the split rulesets to the existing rulesets already in sets.
*/
public RuleSet[] splitIntoTwo( final EvolutionState state, final int thread, RuleSet[] sets, float prob )
{
for( int i = 0 ; i < numRules ; i++ )
if (state.random[thread].nextBoolean(prob))
sets[0].addRule((Rule)(rules[i].clone()) );
else
sets[1].addRule((Rule)(rules[i].clone()) );
return sets;
}
/**
Prints out the rule set in a readable fashion.
*/
public void printRuleSetForHumans(final EvolutionState state, final int log)
{
printRuleSetForHumans(state, log, Output.V_VERBOSE);
}
/**
Prints out the rule set in a readable fashion.
@deprecated Verbosity no longer has an effect
*/
public void printRuleSetForHumans(final EvolutionState state, final int log,
final int verbosity)
{
state.output.println( "Ruleset contains " + numRules + " rules",
log );
for( int i = 0 ; i < numRules ; i ++ )
{
state.output.println( "Rule " + i + ":", log );
rules[i].printRuleForHumans( state, log );
}
}
/**
Prints the rule set such that the computer can read it later
*/
public void printRuleSet(final EvolutionState state, final int log)
{
printRuleSet(state, log, Output.V_VERBOSE);
}
/**
Prints the rule set such that the computer can read it later
@deprecated Verbosity no longer has an effect
*/
public void printRuleSet(final EvolutionState state,
final int log, final int verbosity)
{
state.output.println(N_RULES + Code.encode(numRules), log);
for( int i = 0 ; i < numRules ; i ++ )
rules[i].printRule(state,log);
}
/**
Prints the rule set such that the computer can read it later
*/
public void printRuleSet(final EvolutionState state,
final PrintWriter writer)
{
writer.println( N_RULES + Code.encode(numRules) );
for( int i = 0 ; i < numRules ; i ++ )
rules[i].printRule(state,writer);
}
/**
Reads the rule set
*/
public void readRuleSet(final EvolutionState state,
final LineNumberReader reader)
throws IOException
{
numRules = Code.readIntegerWithPreamble(N_RULES, state, reader);
rules = new Rule[ numRules ];
for(int x=0;x