#region License Information
/* HeuristicLab
* Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab 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 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab 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 HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Linq;
using HeuristicLab.Analysis;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.ConditionActionEncoding;
using HeuristicLab.Operators;
using HeuristicLab.Optimization;
using HeuristicLab.Optimization.Operators;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Random;
namespace HeuristicLab.Algorithms.LearningClassifierSystems {
///
/// A learning classifier system.
///
[Item("XCS", "A learning classifier system")]
[Creatable("Algorithms")]
[StorableClass]
public sealed class LearningClassifierSystem : HeuristicOptimizationEngineAlgorithm, IStorableContent {
public string Filename { get; set; }
#region Problem Properties
public override Type ProblemType {
get { return typeof(IConditionActionProblem); }
}
public new IConditionActionProblem Problem {
get { return (IConditionActionProblem)base.Problem; }
set { base.Problem = value; }
}
#endregion
#region Parameter Properties
private ValueParameter SeedParameter {
get { return (ValueParameter)Parameters["Seed"]; }
}
private ValueParameter SetSeedRandomlyParameter {
get { return (ValueParameter)Parameters["SetSeedRandomly"]; }
}
private ValueParameter CreateInitialPopulationParameter {
get { return (ValueParameter)Parameters["CreateInitialPopulation"]; }
}
private ValueParameter PopulationSizeParameter {
get { return (ValueParameter)Parameters["N"]; }
}
private ValueParameter BetaParameter {
get { return (ValueParameter)Parameters["Beta"]; }
}
private ValueParameter AlphaParameter {
get { return (ValueParameter)Parameters["Alpha"]; }
}
private ValueParameter ErrorZeroParameter {
get { return (ValueParameter)Parameters["ErrorZero"]; }
}
private ValueParameter PowerParameter {
get { return (ValueParameter)Parameters["v"]; }
}
private ValueParameter GammaParameter {
get { return (ValueParameter)Parameters["Gamma"]; }
}
private ValueParameter CrossoverProbabilityParameter {
get { return (ValueParameter)Parameters["CrossoverProbability"]; }
}
private ValueParameter MutationProbabilityParameter {
get { return (ValueParameter)Parameters["MutationProbability"]; }
}
private ValueParameter ThetaGAParameter {
get { return (ValueParameter)Parameters["ThetaGA"]; }
}
private ValueParameter ThetaDeletionParameter {
get { return (ValueParameter)Parameters["ThetaDeletion"]; }
}
private ValueParameter ThetaSubsumptionParameter {
get { return (ValueParameter)Parameters["ThetaSubsumption"]; }
}
private ValueParameter DeltaParameter {
get { return (ValueParameter)Parameters["Delta"]; }
}
private ValueParameter ExplorationProbabilityParameter {
get { return (ValueParameter)Parameters["ExplorationProbability"]; }
}
private ValueParameter DoGASubsumptionParameter {
get { return (ValueParameter)Parameters["DoGASubsumption"]; }
}
private ValueParameter DoActionSetSubsumptionParameter {
get { return (ValueParameter)Parameters["DoActionSetSubsumption"]; }
}
private ValueParameter AnalyzerParameter {
get { return (ValueParameter)Parameters["Analyzer"]; }
}
private ValueParameter FinalAnalyzerParameter {
get { return (ValueParameter)Parameters["FinalAnalyzer"]; }
}
private ValueParameter MaxIterationsParameter {
get { return (ValueParameter)Parameters["MaxIterations"]; }
}
public IConstrainedValueParameter SelectorParameter {
get { return (IConstrainedValueParameter)Parameters["Selector"]; }
}
public IConstrainedValueParameter CrossoverParameter {
get { return (IConstrainedValueParameter)Parameters["Crossover"]; }
}
public IConstrainedValueParameter MutatorParameter {
get { return (IConstrainedValueParameter)Parameters["Mutator"]; }
}
public ValueParameter AnalyzeInIterationParameter {
get { return (ValueParameter)Parameters["AnalyzeInIteration"]; }
}
#endregion
#region Properties
public IntValue Seed {
get { return SeedParameter.Value; }
set { SeedParameter.Value = value; }
}
public BoolValue SetSeedRandomly {
get { return SetSeedRandomlyParameter.Value; }
set { SetSeedRandomlyParameter.Value = value; }
}
public BoolValue CreateInitialPopulation {
get { return CreateInitialPopulationParameter.Value; }
set { CreateInitialPopulationParameter.Value = value; }
}
public IntValue PopulationSize {
get { return PopulationSizeParameter.Value; }
set { PopulationSizeParameter.Value = value; }
}
public PercentValue Beta {
get { return BetaParameter.Value; }
set { BetaParameter.Value = value; }
}
public PercentValue Alpha {
get { return AlphaParameter.Value; }
set { AlphaParameter.Value = value; }
}
public DoubleValue ErrorZero {
get { return ErrorZeroParameter.Value; }
set { ErrorZeroParameter.Value = value; }
}
public DoubleValue Power {
get { return PowerParameter.Value; }
set { PowerParameter.Value = value; }
}
public PercentValue Gamma {
get { return GammaParameter.Value; }
set { GammaParameter.Value = value; }
}
public PercentValue CrossoverProbability {
get { return CrossoverProbabilityParameter.Value; }
set { CrossoverProbabilityParameter.Value = value; }
}
public PercentValue MutationProbability {
get { return MutationProbabilityParameter.Value; }
set { MutationProbabilityParameter.Value = value; }
}
public IntValue ThetaGA {
get { return ThetaGAParameter.Value; }
set { ThetaGAParameter.Value = value; }
}
public IntValue ThetaDeletion {
get { return ThetaDeletionParameter.Value; }
set { ThetaDeletionParameter.Value = value; }
}
public IntValue ThetaSubsumption {
get { return ThetaSubsumptionParameter.Value; }
set { ThetaSubsumptionParameter.Value = value; }
}
public PercentValue Delta {
get { return DeltaParameter.Value; }
set { DeltaParameter.Value = value; }
}
public PercentValue ExplorationProbability {
get { return ExplorationProbabilityParameter.Value; }
set { ExplorationProbabilityParameter.Value = value; }
}
public BoolValue DoGASubsumption {
get { return DoGASubsumptionParameter.Value; }
set { DoGASubsumptionParameter.Value = value; }
}
public BoolValue DoActionSetSubsumption {
get { return DoActionSetSubsumptionParameter.Value; }
set { DoActionSetSubsumptionParameter.Value = value; }
}
public IntValue MaxIterations {
get { return MaxIterationsParameter.Value; }
set { MaxIterationsParameter.Value = value; }
}
public MultiAnalyzer Analyzer {
get { return AnalyzerParameter.Value; }
set { AnalyzerParameter.Value = value; }
}
public MultiAnalyzer FinalAnalyzer {
get { return FinalAnalyzerParameter.Value; }
set { FinalAnalyzerParameter.Value = value; }
}
public ISelector Selector {
get { return SelectorParameter.Value; }
set { SelectorParameter.Value = value; }
}
public ICrossover Crossover {
get { return CrossoverParameter.Value; }
set { CrossoverParameter.Value = value; }
}
public IManipulator Mutator {
get { return MutatorParameter.Value; }
set { MutatorParameter.Value = value; }
}
private RandomCreator RandomCreator {
get { return (RandomCreator)OperatorGraph.InitialOperator; }
}
public LearningClassifierSystemMainLoop MainLoop {
get { return FindMainLoop(RandomCreator.Successor); }
}
#endregion
public LearningClassifierSystem()
: base() {
#region Create parameters
Parameters.Add(new ValueParameter("Seed", "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
Parameters.Add(new ValueParameter("SetSeedRandomly", "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
Parameters.Add(new ValueParameter("CreateInitialPopulation", "Specifies if a population should be created at the beginning of the algorithm.", new BoolValue(false)));
Parameters.Add(new ValueParameter("N", "Max size of the population of solutions.", new IntValue(100)));
Parameters.Add(new ValueParameter("Beta", "Learning rate", new PercentValue(0.1)));
Parameters.Add(new ValueParameter("Alpha", "", new PercentValue(0.1)));
Parameters.Add(new ValueParameter("ErrorZero", "The error below which classifiers are considered to have equal accuracy", new DoubleValue(10)));
Parameters.Add(new ValueParameter("v", "Power parameter", new DoubleValue(5)));
Parameters.Add(new ValueParameter("Gamma", "Discount factor", new PercentValue(0.71)));
Parameters.Add(new ValueParameter("CrossoverProbability", "Probability of crossover", new PercentValue(0.9)));
Parameters.Add(new ValueParameter("MutationProbability", "Probability of mutation", new PercentValue(0.05)));
Parameters.Add(new ValueParameter("ThetaGA", "GA threshold. GA is applied in a set when the average time since the last GA is greater than ThetaGA.", new IntValue(25)));
Parameters.Add(new ValueParameter("ThetaDeletion", "Deletion threshold. If the experience of a classifier is greater than ThetaDeletion, its fitness may be considered in its probability of deletion.", new IntValue(20)));
Parameters.Add(new ValueParameter("ThetaSubsumption", "Subsumption threshold. The experience of a classifier must be greater than TheatSubsumption to be able to subsume another classifier.", new IntValue(20)));
Parameters.Add(new ValueParameter("Delta", "Delta specifies the fraction of mean fitness in [P] below which the fitness of a classifier may be considered in its probability of deletion", new PercentValue(0.1)));
Parameters.Add(new ValueParameter("ExplorationProbability", "Probability of selecting the action uniform randomly", new PercentValue(0.5)));
Parameters.Add(new ValueParameter("DoGASubsumption", "Specifies if offsprings are tested for possible logical subsumption by parents.", new BoolValue(true)));
Parameters.Add(new ValueParameter("DoActionSetSubsumption", "Specifies if action set is tested for subsuming classifiers.", new BoolValue(true)));
Parameters.Add(new ValueParameter("Analyzer", "The operator used to analyze each generation.", new MultiAnalyzer()));
Parameters.Add(new ValueParameter("FinalAnalyzer", "The operator used to analyze the last generation.", new MultiAnalyzer()));
Parameters.Add(new ValueParameter("MaxIterations", "The maximum number of iterations.", new IntValue(1000)));
Parameters.Add(new ConstrainedValueParameter("Selector", "The operator used to select solutions."));
Parameters.Add(new ConstrainedValueParameter("Crossover", "The operator used to cross solutions."));
Parameters.Add(new ConstrainedValueParameter("Mutator", "The operator used to mutate solutions."));
Parameters.Add(new ValueParameter("AnalyzeInIteration", "", new IntValue(50)));
#endregion
#region Create operators
RandomCreator randomCreator = new RandomCreator();
ResultsCollector resultsCollector = new ResultsCollector();
LearningClassifierSystemMainLoop mainLoop = new LearningClassifierSystemMainLoop();
randomCreator.RandomParameter.ActualName = "Random";
randomCreator.SeedParameter.ActualName = SeedParameter.Name;
randomCreator.SeedParameter.Value = null;
randomCreator.SetSeedRandomlyParameter.ActualName = SetSeedRandomlyParameter.Name;
randomCreator.SetSeedRandomlyParameter.Value = null;
resultsCollector.ResultsParameter.ActualName = "Results";
mainLoop.AnalyzerParameter.ActualName = AnalyzerParameter.Name;
mainLoop.FinalAnalyzerParameter.ActualName = FinalAnalyzerParameter.Name;
mainLoop.MaxIterationsParameter.ActualName = MaxIterationsParameter.Name;
mainLoop.SelectorParameter.ActualName = SelectorParameter.Name;
mainLoop.CrossoverParameter.ActualName = CrossoverParameter.Name;
mainLoop.MutatorParameter.ActualName = MutatorParameter.Name;
mainLoop.CrossoverProbabilityParameter.ActualName = CrossoverProbabilityParameter.Name;
mainLoop.AnalyzeInIterationParameter.ActualName = AnalyzeInIterationParameter.Name;
#endregion
#region Create operator graph
OperatorGraph.InitialOperator = randomCreator;
randomCreator.Successor = resultsCollector;
resultsCollector.Successor = mainLoop;
#endregion
UpdateAnalyzers();
}
private LearningClassifierSystem(LearningClassifierSystem original, Cloner cloner)
: base(original, cloner) {
}
public override IDeepCloneable Clone(Cloner cloner) {
return new LearningClassifierSystem(this, cloner);
}
[StorableConstructor]
private LearningClassifierSystem(bool deserializing) : base(deserializing) { }
protected override void OnProblemChanged() {
if (Problem != null) {
ParameterizeEvaluator(Problem.Evaluator);
MainLoop.SetCurrentProblem(Problem);
UpdateSelectors();
UpdateCrossovers();
UpdateMutators();
UpdateAnalyzers();
ParameterizeSelectors();
ParameterizeManipulator();
}
base.OnProblemChanged();
}
protected override void Problem_EvaluatorChanged(object sender, EventArgs e) {
ParameterizeEvaluator(Problem.Evaluator);
MainLoop.SetCurrentProblem(Problem);
base.Problem_EvaluatorChanged(sender, e);
}
protected override void Problem_SolutionCreatorChanged(object sender, EventArgs e) {
MainLoop.SetCurrentProblem(Problem);
base.Problem_SolutionCreatorChanged(sender, e);
}
protected override void Problem_OperatorsChanged(object sender, EventArgs e) {
UpdateSelectors();
UpdateCrossovers();
UpdateMutators();
UpdateAnalyzers();
ParameterizeSelectors();
ParameterizeManipulator();
base.Problem_OperatorsChanged(sender, e);
}
private void ParameterizeSelectors() {
foreach (ISelector selector in SelectorParameter.ValidValues) {
selector.CopySelected = new BoolValue(true);
selector.NumberOfSelectedSubScopesParameter.Value = new IntValue(4);
selector.NumberOfSelectedSubScopesParameter.Hidden = true;
ParameterizeStochasticOperator(selector);
}
if (Problem != null) {
foreach (IXCSSelector selector in SelectorParameter.ValidValues.OfType()) {
selector.NumerosityParameter.ActualName = Problem.Evaluator.NumerosityParameter.ActualName;
selector.NumerosityParameter.Hidden = true;
}
foreach (ISingleObjectiveSelector selector in SelectorParameter.ValidValues.OfType()) {
selector.MaximizationParameter.ActualName = Problem.MaximizationParameter.Name;
selector.MaximizationParameter.Hidden = true;
selector.QualityParameter.ActualName = Problem.Evaluator.FitnessParameter.ActualName;
selector.QualityParameter.Hidden = true;
}
}
}
private void ParameterizeManipulator() {
foreach (var op in Problem.Operators.OfType()) {
op.ProbabilityParameter.ActualName = MutationProbabilityParameter.Name;
}
}
private void ParameterizeEvaluator(IXCSEvaluator evaluator) {
evaluator.ActualTimeParameter.ActualName = "Iteration";
evaluator.BetaParameter.ActualName = BetaParameter.Name;
evaluator.AlphaParameter.ActualName = AlphaParameter.Name;
evaluator.PowerParameter.ActualName = PowerParameter.Name;
evaluator.ErrorZeroParameter.ActualName = ErrorZeroParameter.Name;
}
private void ParameterizeStochasticOperator(IOperator op) {
IStochasticOperator stochasticOp = op as IStochasticOperator;
if (stochasticOp != null) {
stochasticOp.RandomParameter.ActualName = RandomCreator.RandomParameter.ActualName;
stochasticOp.RandomParameter.Hidden = true;
}
}
private void UpdateSelectors() {
ISelector oldSelector = SelectorParameter.Value;
SelectorParameter.ValidValues.Clear();
ISelector defaultSelector = Problem.Operators.OfType().FirstOrDefault();
if (defaultSelector == null) {
defaultSelector = Problem.Operators.OfType().FirstOrDefault();
}
foreach (ISelector selector in Problem.Operators.OfType().OrderBy(x => x.Name))
SelectorParameter.ValidValues.Add(selector);
if (oldSelector != null) {
ISelector selector = SelectorParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldSelector.GetType());
if (selector != null) SelectorParameter.Value = selector;
else oldSelector = null;
}
if (oldSelector == null && defaultSelector != null)
SelectorParameter.Value = defaultSelector;
}
private void UpdateCrossovers() {
ICrossover oldCrossover = CrossoverParameter.Value;
CrossoverParameter.ValidValues.Clear();
ICrossover defaultCrossover = Problem.Operators.OfType().FirstOrDefault();
foreach (ICrossover crossover in Problem.Operators.OfType().OrderBy(x => x.Name))
CrossoverParameter.ValidValues.Add(crossover);
if (oldCrossover != null) {
ICrossover crossover = CrossoverParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldCrossover.GetType());
if (crossover != null) CrossoverParameter.Value = crossover;
else oldCrossover = null;
}
if (oldCrossover == null && defaultCrossover != null)
CrossoverParameter.Value = defaultCrossover;
}
private void UpdateMutators() {
IManipulator oldMutator = MutatorParameter.Value;
MutatorParameter.ValidValues.Clear();
IManipulator defaultMutator = Problem.Operators.OfType().FirstOrDefault();
foreach (IManipulator mutator in Problem.Operators.OfType().OrderBy(x => x.Name))
MutatorParameter.ValidValues.Add(mutator);
if (oldMutator != null) {
IManipulator mutator = MutatorParameter.ValidValues.FirstOrDefault(x => x.GetType() == oldMutator.GetType());
if (mutator != null) MutatorParameter.Value = mutator;
else oldMutator = null;
}
if (oldMutator == null && defaultMutator != null)
MutatorParameter.Value = defaultMutator;
}
private void UpdateAnalyzers() {
Analyzer.Operators.Clear();
FinalAnalyzer.Operators.Clear();
if (Problem != null) {
foreach (IAnalyzer analyzer in Problem.Operators.OfType()) {
Analyzer.Operators.Add(analyzer, analyzer.EnabledByDefault);
FinalAnalyzer.Operators.Add(analyzer, analyzer.EnabledByDefault);
}
}
}
private LearningClassifierSystemMainLoop FindMainLoop(IOperator start) {
IOperator mainLoop = start;
while (mainLoop != null && !(mainLoop is LearningClassifierSystemMainLoop))
mainLoop = ((SingleSuccessorOperator)mainLoop).Successor;
if (mainLoop == null) return null;
else return (LearningClassifierSystemMainLoop)mainLoop;
}
}
}