Free cookie consent management tool by TermsFeed Policy Generator

Changes between Initial Version and Version 1 of Documentation/Howto/Implement Genetic Programming Problems


Ignore:
Timestamp:
04/06/12 18:18:53 (13 years ago)
Author:
gkronber
Comment:

first import of how-to for implementation of GP problems

Legend:

Unmodified
Added
Removed
Modified
  • Documentation/Howto/Implement Genetic Programming Problems

    v1 v1  
     1[[PageOutline]]
     2= How-to Implement Custom Genetic Programming Problems =
     3
     4This how-to describes how you can implement your own plug-in to solve custom problems with genetic programming. GP is a very general evolutionary problem solving method and can be used to find solutions for a large number of different problems. The default installation of !HeuristicLab includes plug-ins for solving symbolic regression (and classification) problems and the artificial ant problems with genetic programming. However, if you want to use GP to solve other kinds of problems it is necessary to write a little bit of code for your own problem plug-in. This can be a little intriguing at first because the HeuristicLab source code is already very extensive and especially the implementation of the symbolic data analysis features includes so many extensions that it cannot be really used as a template for custom implementations. The source code for the artificial ant problem is much easier to understand and includes everything that is necessary for custom implementations of problems for genetic programming.
     5
     6
     7== The lawn mower problem ==
     8This how-to describes the implementation of a very simple GP problem, namely the lawn mower problem [Koza 1994]. The goal is to find a program for a lawn mower that directs the mower over the whole lawn.
     9
     10The mower can execute one of the following instructions:
     11 * Forward: Move one step forward and mow.
     12 * Left: Make a 90° left turn.
     13 * Frog (Jump): Drive to a given point on the lawn without mowing.
     14
     15The lawn is a rectangular of size (n x m), which is 'connected' at the ends so that the mower appears on the left side if it drops off of the side (toroidal shape). This is  The mower should mow each cell of the lawn. The mower can mow a cell twice but this has no effect.
     16
     17[image(lawnmower.jpg)]
     18
     19== Design ==
     20The first step for the implementation of a GP problem is the definition of the instruction set including function- and terminal symbols. In this example we will use the same symbols as used by Koza.
     21* Terminals:
     22 1. Left: Turn the mower left
     23 2. Forward: Move one step forward while mowing
     24 3. Constant: Random constant that contains a vector of two integers
     25* Functions:
     26 1. Sum: The vector sum of two integer vectors
     27 2. Frog: Jump to a new position on the lawn, where the relative distance is specified by the single integer vector argument
     28 3. Prog: Executes two branches sequentially and returns the result of the last branch.
     29
     30Random constants and the vector sum function is necessary to provide input for the frog function. Without the frog function this example would be much easier as all instructions would only have side effects and no return values. Because of the frog function the closure property becomes relevant. This means that each terminal and function symbol must produce a integer vector output so that each combinations of functions and arguments represents a valid program. To satisfy the closure property The left, mow, and frog symbols return the integer vector {{{[0,0]}}}.
     31
     32
     33== Implementation ==
     34Next we implement the problem in a !HeuristicLab plug-in. There is a separate how-to on implementing custom plug-ins which explains this step in detail.
     35
     36=== Project setup ===
     37First create a new C# class library project called {{{HeuristicLab.Problems.LawnMower}}}.
     38
     39In the properties of the project change the assembly name to {{{HeuristicLab.Problems.LawnMower-1.0}}}. This is not strictly necessary but in case we later want to add new features and release a new version of the problem we could potentially install both versions of the plug-in side by side if the version is included in the assembly name.
     40
     41Next add references to the following assemblies in the !HeuristicLab installation folder:
     42* `HeuristicLab.PluginInfrastructure-3.3.dll`
     43* `HeuristicLab.Persistence-3.3.dll`
     44* `HeuristicLab.Collections-3.3.dll`
     45* `HeuristicLab.Common-3.3.dll`
     46* `HeuristicLab.Core-3.3.dll`
     47* `HeuristicLab.Data-3.3.dll`
     48* `HeuristicLab.Encodings.SymbolicExpressionTreeEncoding-3.4.dll`
     49* `HeuristicLab.Operators-3.3.dll`
     50* `HeuristicLab.Optimization-3.3.dll`
     51* `HeuristicLab.Optimization.Operators-3.3.dll`
     52* `HeuristicLab.Parameters-3.3.dll`
     53
     54These assemblies are necessary for genetic programming problem implementations.
     55
     56=== Plugin Class ===
     57
     58Every !HeuristicLab plug-in must contain a class which derives from {{{PluginBase}}} and contains properties declaring information about the plug-in and it's dependencies. We simple call this class {{{Plugin}}}
     59
     60{{{
     61#!csharp
     62using HeuristicLab.PluginInfrastructure;
     63
     64namespace HeuristicLab.Problems.LawnMower {
     65  [Plugin("HeuristicLab.Problems.LawnMower", "Lawn mower demo problem for genetic programming", "1.0.0")]
     66  [PluginFile("HeuristicLab.Problems.LawnMower-1.0.dll", PluginFileType.Assembly)]
     67  [PluginDependency("HeuristicLab.Data", "3.3")]
     68  [PluginDependency("HeuristicLab.Core", "3.3")]
     69  [PluginDependency("HeuristicLab.Common", "3.3")]
     70  [PluginDependency("HeuristicLab.Encodings.SymbolicExpressionTreeEncoding", "3.4")]
     71  [PluginDependency("HeuristicLab.Parameters", "3.3")]
     72  [PluginDependency("HeuristicLab.Persistence", "3.3")]
     73  public class Plugin : PluginBase {
     74  }
     75}
     76}}}
     77
     78With the {{{Plugin}} attribute we can add a name an description for the plug-in that is shown in the !HeuristicLab plug-in manager. Next we declare which files belong to the plug-in. In our case the plug-in consists only of the assembly {{{HeuristicLab.Problems.LawnMower-1.0.dll}}}. The {{{PluginDependency}}} attributes declare dependencies for this plug-in which is necessary for plug-in management. Each !HeuristicLab assembly that is referenced in our project must also be included as a plug-in dependencies.
     79
     80 
     81=== Symbol Classes ===
     82The next step for implementing GP problems is the implementation of the symbol classes. Symbols include both function and terminal symbols that can be used in the evolved GP solutions.
     83
     84Left.cs:
     85{{{
     86#!csharp
     87using HeuristicLab.Common;
     88using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     89
     90namespace HeuristicLab.Problems.LawnMower {
     91  // first version of left symbol (unfinished)
     92  public class Left : Symbol {
     93    public override int MinimumArity { get { return 0; } }
     94    public override int MaximumArity { get { return 0; } }
     95
     96    public Left()
     97      : base("Left", "Turns the lawn mower to the left.") {
     98    }
     99  }
     100}
     101}}}
     102
     103New symbol implementations must derive from the class {{{Symbol}}} defined in the {{{SymbolicExpressionTreeEncoding}}}. In each symbol the properties {{{MinimumArity}}} and {{{MaximumArity}}} must be overridden to declare how the symbol can be used in evolved programs. Here, the {{{Left}}} symbol is a terminal symbol and thus must not have any arguments. Therefore the arity of the symbol is set to zero (no arguments allowed).
     104
     105This first implementation of the {{{Left}}} symbol must be extended to support two essential functionalities, namely cloning and persistence.
     106
     107Since instances of symbols must be cloned it is necessary to implement the cloning functionality. In !HeuristicLab a common pattern is used for cloning in all clonable classes. The pattern involves overriding the {{{Clone()}}} method and implementing a cloning constructor.
     108
     109Cloning pattern for symbol {{{Left}}}
     110{{{
     111#!csharp
     112    // cloning constructor
     113    private Left(Left original, Cloner cloner)
     114      : base(original, cloner) {
     115    }
     116
     117    // override for clone method
     118    public override IDeepCloneable Clone(Cloner cloner) {
     119      return new Left(this, cloner);
     120    }
     121}}}
     122
     123To make it possible to store trees containing symbols to a file it is necessary to make two additional changes to the implementation of the {{{Left}}} symbol. In !HeuristicLab the classes of instances which are storable must be marked with the {{{[StorableClass]}}} attribute. Additionally, a storable constructor must be implemented which is marked with the {{{[StorableConstructor]}}} attribute. Any field or property that should be stored must be marked with a {{{[Storable]}}} attribute. However, in the left symbol there are no data that need to be stored.
     124
     125{{{Left.cs}}} including cloning pattern and storable pattern.
     126{{{
     127#!csharp
     128using HeuristicLab.Common;
     129using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     130using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     131
     132namespace HeuristicLab.Problems.LawnMower {
     133  // final implementation of the left symbol
     134  // with persistence and cloning patterns
     135  [StorableClass]
     136  public class Left : Symbol {
     137
     138    public override int MinimumArity { get { return 0; } }
     139    public override int MaximumArity { get { return 0; } }
     140
     141    // storable constructor for persistence
     142    [StorableConstructor]
     143    private Left(bool deserializing) : base(deserializing) { }
     144   
     145    // cloning constructor
     146    private Left(Left original, Cloner cloner)
     147      : base(original, cloner) {
     148      // nothing needs to be cloned
     149    }
     150
     151    public Left()
     152      : base("Left", "Turns the lawn mower to the left.") {
     153    }
     154
     155    // clone method override
     156    public override IDeepCloneable Clone(Cloner cloner) {
     157      return new Left(this, cloner);
     158    }
     159  }
     160}
     161}}}
     162
     163 
     164In the same fashion we can implement the remaining symbols, where {{{MinimumArity}}} and {{{MaximumArity}}} properties must be adapted for each symbol.
     165
     166
     167{{{Prog.cs}}}
     168{{{
     169#!csharp
     170using HeuristicLab.Common;
     171using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     172using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     173
     174namespace HeuristicLab.Problems.LawnMower {
     175  [StorableClass]
     176  public class Prog : Symbol {
     177    public override int MinimumArity { get { return 2; } }
     178    public override int MaximumArity { get { return 2; } }
     179
     180    [StorableConstructor]
     181    private Prog(bool deserializing) : base(deserializing) { }
     182    private Prog(Prog original, Cloner cloner)
     183      : base(original, cloner) {
     184    }
     185
     186    public Prog()
     187      : base("Prog", "Prog executes two branches sequentially and returns the result of the last branch.") {
     188    }
     189
     190    public override IDeepCloneable Clone(Cloner cloner) {
     191      return new Prog(this, cloner);
     192    }
     193  }
     194}
     195
     196}}}
     197
     198{{{Sum.cs}}}
     199{{{
     200#!csharp
     201using HeuristicLab.Common;
     202using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     203using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     204
     205namespace HeuristicLab.Problems.LawnMower {
     206  [StorableClass]
     207  public class Sum : Symbol {
     208    public override int MinimumArity { get { return 2; } }
     209    public override int MaximumArity { get { return 2; } }
     210
     211    [StorableConstructor]
     212    private Sum(bool deserializing) : base(deserializing) { }
     213    private Sum(Sum original, Cloner cloner)
     214      : base(original, cloner) {
     215    }
     216
     217    public Sum()
     218      : base("Sum", "Sum of two integer vectors.") {
     219    }
     220
     221    public override IDeepCloneable Clone(Cloner cloner) {
     222      return new Sum(this, cloner);
     223    }
     224  }
     225}
     226}}}
     227
     228{{{Frog.cs}}}
     229{{{
     230#!csharp
     231using HeuristicLab.Common;
     232using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     233using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     234
     235namespace HeuristicLab.Problems.LawnMower {
     236  [StorableClass]
     237  public class Frog : Symbol {
     238    public override int MinimumArity { get { return 1; } }
     239    public override int MaximumArity { get { return 1; } }
     240
     241    [StorableConstructor]
     242    private Frog(bool deserializing) : base(deserializing) { }
     243    private Frog(Frog original, Cloner cloner)
     244      : base(original, cloner) {
     245    }
     246
     247    public Frog()
     248      : base("Frog", "Jumps to a relative position (determined by the argument).") {
     249    }
     250
     251    public override IDeepCloneable Clone(Cloner cloner) {
     252      return new Frog(this, cloner);
     253    }
     254  }
     255}
     256}}}
     257
     258{{{Forward.cs}}}
     259{{{
     260#!csharp
     261using HeuristicLab.Common;
     262using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     263using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     264
     265namespace HeuristicLab.Problems.LawnMower {
     266  [StorableClass]
     267  public class Forward : Symbol {
     268    public override int MinimumArity { get { return 0; } }
     269    public override int MaximumArity { get { return 0; } }
     270
     271    [StorableConstructor]
     272    private Forward(bool deserializing) : base(deserializing) { }
     273    private Forward(Forward original, Cloner cloner)
     274      : base(original, cloner) {
     275    }
     276
     277    public Forward()
     278      : base("Forward", "Moves the lawn mower one square forward.") {
     279    }
     280
     281    public override IDeepCloneable Clone(Cloner cloner) {
     282      return new Forward(this, cloner);
     283    }
     284  }
     285}
     286}}}
     287
     288{{{Constant.cs}}}
     289{{{
     290#!csharp
     291using HeuristicLab.Common;
     292using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     293using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     294
     295namespace HeuristicLab.Problems.LawnMower {
     296  [StorableClass]
     297  public class Constant : Symbol {
     298    public override int MinimumArity { get { return 0; } }
     299    public override int MaximumArity { get { return 0; } }
     300
     301    [StorableConstructor]
     302    private Constant(bool deserializing) : base(deserializing) { }
     303    private Constant(Constant original, Cloner cloner)
     304      : base(original, cloner) {
     305    }
     306
     307    public Constant()
     308      : base("Constant", "Vector of two random integers.") {
     309    }
     310
     311    // override to create a specific tree node for constants
     312    public override ISymbolicExpressionTreeNode CreateTreeNode() {
     313      return new ConstantTreeNode();
     314    }
     315    public override IDeepCloneable Clone(Cloner cloner) {
     316      return new Constant(this, cloner);
     317    }
     318  }
     319}
     320}}}
     321
     322As you can see in the implementation of the class {{{Constant}}}, there is another override for the method {{{CreateTreeNode()}}}. This override is necessary because we want to create a specific kind of tree nodes ({{{ConstantTreeNode}}}), which will hold the random integer vector. This is the first class that holds data which must be marked with the {{{[Storable]}}} attribute and cloned in the cloning constructor.
     323
     324{{{ConstantTreeNode.cs}}}
     325{{{
     326#!csharp
     327using System;
     328using HeuristicLab.Common;
     329using HeuristicLab.Core;
     330using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     331using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     332
     333namespace HeuristicLab.Problems.LawnMower {
     334  [StorableClass]
     335  public class ConstantTreeNode : SymbolicExpressionTreeTerminalNode {
     336    // the random integer vector is stored as a tuple of ints.
     337    private Tuple<int, int> value;
     338    [Storable]
     339    public Tuple<int, int> Value {
     340      get { return value; }
     341      private set { this.value = value; }
     342    }
     343
     344    [StorableConstructor]
     345    private ConstantTreeNode(bool deserializing) : base(deserializing) { }
     346    private ConstantTreeNode(ConstantTreeNode original, Cloner cloner)
     347      : base(original, cloner) {
     348      // important here we need to implement the cloning
     349      // of the local data (integer vector)
     350      this.value =
     351        new Tuple<int, int>(original.value.Item1, original.value.Item2);
     352    }
     353
     354    // default constructor, the symbol of a ConstantTreeNode is always
     355    // a Constant.
     356    public ConstantTreeNode()
     357      : base(new Constant()) {
     358    }
     359
     360    public override IDeepCloneable Clone(Cloner cloner) {
     361      return new ConstantTreeNode(this, cloner);
     362    }
     363
     364    // This tree node holds data (integer vector).
     365    // Thus, this property must be overridden to return true
     366    // to indicate to the algorithm that the data in this node
     367    // must be reset initially.
     368    public override bool HasLocalParameters {
     369      get { return true; }
     370    }
     371    // ResetLocalParameters is called by the algorithm to reset
     372    // the data of the tree node when initializing the population.
     373    // Here we want to create a random integer vector where both
     374    // components are distributed uniformly.
     375    public override void ResetLocalParameters(IRandom random) {
     376      value = new Tuple<int, int>(random.Next(-20, 20), random.Next(-20, 20));
     377    }
     378  }
     379}
     380}}}
     381
     382
     383=== Grammar Class ===
     384The {{{Grammar}}} class defines the set of valid trees. In this example the {{{Grammar}}} is very simple as it is allowed to use any function or terminal symbol as argument of any function symbol. In real-world application it is frequently necessary to restrict this relation which is possible by implementing these rules in the {{{Grammar}}}.
     385
     386{{{Grammar.cs}}}
     387{{{
     388#!csharp
     389using System.Collections.Generic;
     390using HeuristicLab.Common;
     391using HeuristicLab.Core;
     392using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     393using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     394
     395namespace HeuristicLab.Problems.LawnMower {
     396  [StorableClass]
     397  [Item("Lawn Mower Grammar", "The grammar for the lawn mower demo GP problem.")]
     398  public class Grammar : SymbolicExpressionGrammar {
     399    [StorableConstructor]
     400    private Grammar(bool deserializing) : base(deserializing) { }
     401    private Grammar(Grammar original, Cloner cloner)
     402      : base(original, cloner) {
     403    }
     404
     405    public Grammar()
     406      : base("Grammar", "The grammar for the lawn mower demo GP problem.") {
     407      Initialize();
     408    }
     409
     410    public override IDeepCloneable Clone(Cloner cloner) {
     411      return new Grammar(this, cloner);
     412    }
     413
     414    // initialize set of allowed symbols and define
     415    // the allowed combinations of symbols
     416    private void Initialize() {
     417      // create all symbols
     418      var sum = new Sum();
     419      var prog = new Prog();
     420      var frog = new Frog();
     421      var left = new Left();
     422      var forward = new Forward();
     423      var constant = new Constant();
     424
     425      var allSymbols = new List<ISymbol>() {
     426          sum, prog, frog, left, forward, constant };
     427
     428      // add all symbols to the grammar
     429      foreach (var s in allSymbols)
     430        AddSymbol(s);
     431
     432      // define grammar rules
     433      // all symbols are allowed ...
     434      foreach (var s in allSymbols) {
     435        // ... as children of the sum function
     436        AddAllowedChildSymbol(sum, s, 0);
     437        AddAllowedChildSymbol(sum, s, 1);
     438        // ... as children of the prog function
     439        AddAllowedChildSymbol(prog, s, 0);
     440        AddAllowedChildSymbol(prog, s, 1);
     441        // ... as children of the frog function
     442        AddAllowedChildSymbol(frog, s, 0);
     443        // ... as root symbol
     444        AddAllowedChildSymbol(StartSymbol, s, 0);     
     445      }
     446    }
     447  }
     448}
     449}}}
     450
     451
     452=== Interpreter and Evaluator Classes ===
     453Next we implement the necessary classes for evaluation of GP trees representing solution candidates for the lawn mower problem. We implement two classes, the {{{Interpreter}}} which simply executes a given tree, and the {{{Evaluator}}} which will be used as the fitness evaluation operator for the lawn mower problem and uses the {{{Interpreter}}} to execute trees.
     454
     455While it would be possible to implement the evaluation of symbols directly in the symbols themselves, we found it more convenient to extract the symbol semantics into a separate interpreter class. Due to this design it is for instance easily possible to implement different interpreters for lawn mower problems.
     456
     457Additionally, the interpreter and evaluator are closely related classes, which could be combined into one class. However, we found that this design is more convenient because we can later also call the interpreter for other purposes than evaluation (e.g. for visualization).
     458
     459First let's look at the implementation of the {{{Interpreter}}}. The {{{Interpreter}}} is at the core of the lawn mower problem as it exactly describes how solution candidates are executed and which results they produce. The {{{Interpreter}}} should be implemented in an efficient way because it will be called repeatedly for solution evaluation and thus is at the critical point for performance optimization. The {{{Interpreter}}} is a simple class and not derived from any framework classes. It contains only static methods for the interpretation of trees. Thus, it is not necessary to mark it as a {{{[StorableClass]}}} and it is not necessary to implement the cloning pattern or the persistence pattern.
     460
     461{{{Interpreter.cs}}}
     462{{{
     463#!csharp
     464using System;
     465using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     466
     467namespace HeuristicLab.Problems.LawnMower {
     468  public class Interpreter {
     469    // enumerable type for the possible headings of the mower
     470    private enum Heading { South, East, North, West };
     471
     472    // internal class used to encapsulate the state of the mower
     473    // at any time the mower has a certain heading
     474    // has a position in the lawn
     475    // and additionally has an energy to limit the number of steps
     476    // a lawn mower can make
     477    private class MowerState {
     478      public Heading Heading { get; set; }
     479      public Tuple<uint, uint> Position { get; set; }
     480      public int Energy { get; set; }
     481      public MowerState() {
     482        Heading = Heading.South;
     483        Position = new Tuple<uint, uint>(0, 0);
     484        Energy = 0;
     485      }
     486    }
     487
     488
     489    public static bool[,] EvaluateLawnMowerProgram(int length, int width,
     490      ISymbolicExpressionTree tree) {
     491      // array to mark the fields that have been mowed
     492      bool[,] lawn = new bool[length, width];
     493      // initialize the mower state and position on the lawn
     494      var mowerState = new MowerState();
     495      mowerState.Heading = Heading.South;
     496      mowerState.Energy = length * width * 2;
     497      lawn[mowerState.Position.Item1, mowerState.Position.Item2] = true;
     498     
     499      // start program execution at the root node
     500      EvaluateLawnMowerProgram(tree.Root, ref mowerState, lawn);
     501
     502      return lawn;
     503    }
     504
     505    // evaluate a whole tree branch, each branch returns an integer vector
     506    private static Tuple<int, int> EvaluateLawnMowerProgram(
     507        ISymbolicExpressionTreeNode node,
     508        ref MowerState mowerState, bool[,] lawn) {
     509      // if no energy is left then return immediately
     510      if (mowerState.Energy <= 0) return new Tuple<int, int>(0, 0);
     511     
     512      // The program-root and start symbols are predefined symbols
     513      // in each problem using the symbolic expression tree encoding.
     514      // These symbols must be handled by the interpreter. Here we simply
     515      // evaluate the whole sub-tree
     516      if (node.Symbol is ProgramRootSymbol) {
     517        return EvaluateLawnMowerProgram(node.GetSubtree(0),
     518                                        ref mowerState, lawn);
     519      } else if (node.Symbol is StartSymbol) {
     520        return EvaluateLawnMowerProgram(node.GetSubtree(0),
     521                                        ref mowerState, lawn);
     522      } else if (node.Symbol is Left) {
     523        // turn left
     524        switch (mowerState.Heading) {
     525          case Heading.East: mowerState.Heading = Heading.North;
     526            break;
     527          case Heading.North: mowerState.Heading = Heading.West;
     528            break;
     529          case Heading.West: mowerState.Heading = Heading.South;
     530            break;
     531          case Heading.South:
     532            mowerState.Heading = Heading.East;
     533            break;
     534        }
     535        return new Tuple<int, int>(0, 0);
     536      } else if (node.Symbol is Forward) {
     537        int dRow = 0;
     538        int dCol = 0;
     539        // move forward
     540        switch (mowerState.Heading) {
     541          case Heading.East: dCol++; break;
     542          case Heading.North: dRow--; break;
     543          case Heading.West: dCol--; break;
     544          case Heading.South: dRow++; break;
     545        }
     546        // make sure the mower does not move outside the lawn
     547        int rows = lawn.GetLength(0);
     548        int columns = lawn.GetLength(1);
     549        uint newRow =
     550          (uint)((mowerState.Position.Item1 + rows + dRow) % rows);
     551        uint newColumn =
     552          (uint)((mowerState.Position.Item2 + columns + dCol) % columns);
     553        // set the new position
     554        mowerState.Position = new Tuple<uint, uint>(newRow, newColumn);
     555        // reduce the energy in each mowing step
     556        mowerState.Energy = mowerState.Energy - 1;
     557        lawn[newRow, newColumn] = true;
     558        return new Tuple<int, int>(0, 0);
     559      } else if (node.Symbol is Constant) {
     560        // return the random constant value
     561        var constNode = node as ConstantTreeNode;
     562        return constNode.Value;
     563      } else if (node.Symbol is Sum) {
     564        // add the return values of the two argument branches
     565        var p = EvaluateLawnMowerProgram(node.GetSubtree(0),
     566                                         ref mowerState, lawn);
     567        var q = EvaluateLawnMowerProgram(node.GetSubtree(1),
     568                                         ref mowerState, lawn);
     569        return new Tuple<int, int>(p.Item1 + q.Item1,
     570          p.Item2 + q.Item2);
     571      } else if (node.Symbol is Prog) {
     572        // execute both argument branches sequentially
     573        // and return the return value of the last one
     574        EvaluateLawnMowerProgram(node.GetSubtree(0), ref mowerState, lawn);
     575        return EvaluateLawnMowerProgram(node.GetSubtree(1),
     576                                        ref mowerState, lawn);
     577      } else if (node.Symbol is Frog) {
     578        // jump to a new position
     579        // the relative position is the return value of the argument branch
     580        var p = EvaluateLawnMowerProgram(node.GetSubtree(0),
     581                                         ref mowerState, lawn);
     582
     583        // make sure the mower does not move outside the lawn
     584        int rows = lawn.GetLength(0);
     585        int cols= lawn.GetLength(1);
     586        uint newRow =
     587          (uint)((mowerState.Position.Item1 + rows + p.Item1 % rows) % rows);
     588        uint newCol =
     589          (uint)((mowerState.Position.Item2 + cols + p.Item2 % cols) % cols);
     590        // set new position
     591        mowerState.Position = new Tuple<uint, uint>(newRow, newCol);
     592        // set the cell at the new position to mowed
     593        // and reduce the mower energy
     594        lawn[newRow, newCol] = true;
     595        mowerState.Energy = mowerState.Energy - 1;
     596        return new Tuple<int, int>(0, 0);
     597      } else {
     598        throw
     599          new ArgumentException("Invalid symbol in the lawn mower program.");
     600      }
     601    }
     602  }
     603}
     604}}}
     605
     606
     607{{{Evaluator.cs}}}
     608{{{
     609#!csharp
     610using HeuristicLab.Common;
     611using HeuristicLab.Core;
     612using HeuristicLab.Data;
     613using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     614using HeuristicLab.Operators;
     615using HeuristicLab.Optimization;
     616using HeuristicLab.Parameters;
     617using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     618
     619namespace HeuristicLab.Problems.LawnMower {
     620  [StorableClass]
     621  [Item("Lawn Mower Evaluator",
     622        "Evaluator for the lawn mower demo GP problem.")]
     623  public class Evaluator : SingleSuccessorOperator,
     624    ISingleObjectiveEvaluator {
     625    #region parameter names
     626    private const string QualityParameterName = "Quality";
     627    private const string LawnMowerProgramParameterName = "LawnMowerProgram";
     628    private const string LawnWidthParameterName = "LawnWidth";
     629    private const string LawnLengthParameterName = "LawnLength";
     630    #endregion
     631
     632
     633    #region parameters
     634    public ILookupParameter<DoubleValue> QualityParameter {
     635      get { return (ILookupParameter<DoubleValue>)
     636                     Parameters[QualityParameterName]; }
     637    }
     638    public ILookupParameter<ISymbolicExpressionTree> LawnMowerProgramParameter {
     639      get { return (ILookupParameter<ISymbolicExpressionTree>)
     640                     Parameters[LawnMowerProgramParameterName]; }
     641    }
     642    public ILookupParameter<IntValue> LawnWidthParameter {
     643      get { return (ILookupParameter<IntValue>)
     644                     Parameters[LawnWidthParameterName]; }
     645    }
     646    public ILookupParameter<IntValue> LawnLengthParameter {
     647      get { return (ILookupParameter<IntValue>)
     648                     Parameters[LawnLengthParameterName]; }
     649    }
     650    #endregion
     651
     652    [StorableConstructor]
     653    protected Evaluator(bool deserializing) : base(deserializing) { }
     654    protected Evaluator(Evaluator original, Cloner cloner)
     655      : base(original, cloner) {
     656    }
     657
     658    // default constructor for the evaluator
     659    public Evaluator() {
     660      Parameters.Add(
     661        new LookupParameter<DoubleValue>(
     662          QualityParameterName,
     663          "The solution quality of the lawn mower program."));
     664      Parameters.Add(
     665        new LookupParameter<ISymbolicExpressionTree>(
     666          LawnMowerProgramParameterName,
     667          "The lawn mower program to evaluate represented as " +
     668          "symbolic expression tree."));
     669      Parameters.Add(
     670        new LookupParameter<IntValue>(LawnWidthParameterName,
     671                                      "The width of the lawn to mow."));
     672      Parameters.Add(
     673        new LookupParameter<IntValue>(LawnLengthParameterName,
     674                                      "The length of the lawn to mow."));
     675    }
     676
     677    // override the apply method to change the behaviour of the operator
     678    // here we take trees as inputs and calculate qualities
     679    // (fitness evaluation)
     680    public override IOperation Apply() {
     681      int length = LawnLengthParameter.ActualValue.Value;
     682      int width = LawnWidthParameter.ActualValue.Value;
     683      ISymbolicExpressionTree tree = LawnMowerProgramParameter.ActualValue;
     684
     685      // call the interpreter to execute the program represented as tree
     686      bool[,] lawn = Interpreter.EvaluateLawnMowerProgram(length, width, tree);
     687
     688      // count number of cells that have been mowed
     689      int numberOfMowedCells = 0;
     690      for (int i = 0; i < length; i++)
     691        for (int j = 0; j < width; j++)
     692          if (lawn[i, j]) {
     693            numberOfMowedCells++;
     694          }
     695
     696      // the solution quality is the number of mowed cells
     697      QualityParameter.ActualValue = new DoubleValue(numberOfMowedCells);
     698     
     699      // important: return base.Apply() to make sure that the
     700      // next operator is queued for execution
     701      return base.Apply();
     702    }
     703
     704    public override IDeepCloneable Clone(Cloner cloner) {
     705      return new Evaluator(this, cloner);
     706    }
     707  }
     708}
     709}}}
     710
     711=== Problem Class ===
     712Finally, we implement the {Problem} class.
     713
     714{{{Problem.cs}}}
     715{{{
     716#!csharp
     717using System;
     718using System.Linq;
     719using HeuristicLab.Common;
     720using HeuristicLab.Core;
     721using HeuristicLab.Data;
     722using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     723using HeuristicLab.Optimization;
     724using HeuristicLab.Parameters;
     725using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     726using HeuristicLab.PluginInfrastructure;
     727
     728namespace HeuristicLab.Problems.LawnMower {
     729  [StorableClass]
     730  [Creatable("Problems")]
     731  [Item("Lawn Mower Problem",
     732        "The lawn mower demo problem for genetic programming.")]
     733  public class Problem : SingleObjectiveHeuristicOptimizationProblem<Evaluator,
     734    ISymbolicExpressionTreeCreator> {
     735    #region parameter names
     736    private const string LawnWidthParameterName = "LawnWidth";
     737    private const string LawnLengthParameterName = "LawnLength";
     738    private const string LawnMowerProgramParameterName = "Program";
     739    private const string MaxLawnMowerProgramLengthParameterName =
     740      "MaxProgramLength";
     741    private const string MaxLawnMowerProgramDepthParameterName =
     742      "MaxProgramDepth";
     743    private const string LawnMowerGrammarParameterName = "Grammar";
     744    #endregion
     745
     746    #region parameters
     747    public IFixedValueParameter<IntValue> LawnWidthParameter {
     748      get { return (IFixedValueParameter<IntValue>)
     749              Parameters[LawnWidthParameterName]; }
     750    }
     751    public IFixedValueParameter<IntValue> LawnLengthParameter {
     752      get { return (IFixedValueParameter<IntValue>)
     753              Parameters[LawnLengthParameterName]; }
     754    }
     755    public IFixedValueParameter<IntValue> MaxLawnMowerProgramLengthParameter {
     756      get { return (IFixedValueParameter<IntValue>)
     757              Parameters[MaxLawnMowerProgramLengthParameterName]; }
     758    }
     759    public IFixedValueParameter<IntValue> MaxLawnMowerProgramDepthParameter {
     760      get { return (IFixedValueParameter<IntValue>)
     761              Parameters[MaxLawnMowerProgramDepthParameterName]; }
     762    }
     763    public IValueParameter<Grammar> GrammarParameter {
     764      get { return (IValueParameter<Grammar>)
     765              Parameters[LawnMowerGrammarParameterName]; }
     766    }
     767    #endregion
     768
     769    [StorableConstructor]
     770    protected Problem(bool deserializing)
     771      : base(deserializing) {
     772    }
     773    protected Problem(Problem original, Cloner cloner)
     774      : base(original, cloner) {
     775    }
     776
     777    // default constructor for the problem
     778    // also creates the fitness evaluation operator (Evaluator),
     779    // and the tree creation operator (RampedHalfAndHalfTreeCreator)
     780    public Problem()
     781      : base(new Evaluator(), new RampedHalfAndHalfTreeCreator()) {
     782      Parameters.Add(
     783        new FixedValueParameter<IntValue>(
     784          LawnWidthParameterName,
     785          "Width of the lawn.",
     786          new IntValue(8)));
     787      Parameters.Add(
     788        new FixedValueParameter<IntValue>(
     789          LawnLengthParameterName,
     790          "Length of the lawn.",
     791          new IntValue(8)));
     792      Parameters.Add(
     793        new FixedValueParameter<IntValue>(
     794          MaxLawnMowerProgramDepthParameterName,
     795          "Maximal depth of the lawn mower program.",
     796          new IntValue(13)));
     797      Parameters.Add(
     798        new FixedValueParameter<IntValue>(
     799          MaxLawnMowerProgramLengthParameterName,
     800          "Maximal length of the lawn mower program.",
     801          new IntValue(1000)));
     802      Parameters.Add(
     803        new ValueParameter<Grammar>(
     804          LawnMowerGrammarParameterName,
     805          "Grammar for the lawn mower program.",
     806          new Grammar()));
     807      Maximization.Value = true;
     808      InitializeOperators();
     809
     810    }
     811
     812    public override IDeepCloneable Clone(Cloner cloner) {
     813      return new Problem(this, cloner);
     814    }
     815
     816    private void InitializeOperators() {
     817      Operators.AddRange(
     818        ApplicationManager.Manager.GetInstances<ISymbolicExpressionTreeOperator>());
     819      Operators.Add(new MinAverageMaxSymbolicExpressionTreeLengthAnalyzer());
     820      Operators.Add(new SymbolicExpressionSymbolFrequencyAnalyzer());
     821      Operators.Add(new BestSolutionAnalyzer());
     822      ParameterizeOperators();
     823      ParameterizeAnalyzers();
     824    }
     825
     826    protected override void OnEvaluatorChanged() {
     827      base.OnEvaluatorChanged();
     828      Evaluator.LawnMowerProgramParameter.ActualName =
     829        LawnMowerProgramParameterName;
     830      Evaluator.LawnLengthParameter.ActualName = LawnLengthParameterName;
     831      Evaluator.LawnWidthParameter.ActualName = LawnWidthParameterName;
     832      ParameterizeAnalyzers();
     833      ParameterizeOperators();
     834    }
     835
     836    protected override void OnSolutionCreatorChanged() {
     837      base.OnSolutionCreatorChanged();
     838      SolutionCreator.SymbolicExpressionTreeParameter.ActualName =
     839        LawnMowerProgramParameterName;
     840      ParameterizeAnalyzers();
     841      ParameterizeOperators();
     842    }
     843
     844    private void ParameterizeAnalyzers() {
     845      var analyzers = Operators.OfType<IAnalyzer>();
     846      foreach (var o in analyzers.OfType<ISymbolicExpressionTreeAnalyzer>()) {
     847        o.SymbolicExpressionTreeParameter.ActualName =
     848          SolutionCreator.SymbolicExpressionTreeParameter.ActualName;
     849      }
     850      foreach (var o in analyzers.OfType<BestSolutionAnalyzer>()) {
     851        o.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
     852      }
     853    }
     854
     855    private void ParameterizeOperators() {
     856      var operators = Parameters
     857        .OfType<IValueParameter>()
     858        .Select(p => p.Value)
     859        .OfType<IOperator>()
     860        .Union(Operators);
     861      foreach (var o in operators.OfType<ISymbolicExpressionTreeGrammarBasedOperator>()) {
     862        o.SymbolicExpressionTreeGrammarParameter.ActualName =
     863          LawnMowerGrammarParameterName;
     864      }
     865      foreach (var o in operators.OfType<ISymbolicExpressionTreeSizeConstraintOperator>()) {
     866        o.MaximumSymbolicExpressionTreeDepthParameter.ActualName =
     867          MaxLawnMowerProgramDepthParameterName;
     868        o.MaximumSymbolicExpressionTreeLengthParameter.ActualName =
     869          MaxLawnMowerProgramLengthParameterName;
     870      }
     871      foreach (var op in operators.OfType<Evaluator>()) {
     872        op.LawnMowerProgramParameter.ActualName =
     873          SolutionCreator.SymbolicExpressionTreeParameter.ActualName;
     874        op.LawnLengthParameter.ActualName = LawnLengthParameterName;
     875        op.LawnWidthParameter.ActualName = LawnWidthParameterName;
     876      }
     877      foreach (var op in operators.OfType<ISymbolicExpressionTreeCrossover>()) {
     878        op.ParentsParameter.ActualName =
     879          SolutionCreator.SymbolicExpressionTreeParameter.ActualName;
     880        op.ChildParameter.ActualName =
     881          SolutionCreator.SymbolicExpressionTreeParameter.ActualName;
     882      }
     883      foreach (var op in operators.OfType<ISymbolicExpressionTreeManipulator>()) {
     884        op.SymbolicExpressionTreeParameter.ActualName =
     885          SolutionCreator.SymbolicExpressionTreeParameter.ActualName;
     886      }
     887      foreach (var op in operators.OfType<ISymbolicExpressionTreeCreator>()) {
     888        op.SymbolicExpressionTreeParameter.ActualName =
     889          SolutionCreator.SymbolicExpressionTreeParameter.ActualName;
     890      }
     891    }
     892  }
     893}
     894}}}
     895
     896== Extension of the Implementation ==
     897
     898=== Solution Class ===
     899{{{Solution.cs}}}
     900{{{
     901#!csharp
     902using HeuristicLab.Common;
     903using HeuristicLab.Core;
     904using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     905using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     906
     907namespace HeuristicLab.Problems.LawnMower {
     908  [StorableClass]
     909  public sealed class Solution : NamedItem {
     910    private int width;
     911    [Storable]
     912    public int Width {
     913      get { return width; }
     914      private set { this.width = value; }
     915    }
     916    private int length;
     917    [Storable]
     918    public int Length {
     919      get { return length; }
     920      private set { this.length = value; }
     921    }
     922    private ISymbolicExpressionTree tree;
     923    [Storable]
     924    public ISymbolicExpressionTree Tree {
     925      get { return tree; }
     926      private set { this.tree = value; }
     927    }
     928    [StorableConstructor]
     929    private Solution(bool deserializing) : base(deserializing) { }
     930    private Solution(Solution original, Cloner cloner)
     931      : base(original, cloner) {
     932      this.length = original.length;
     933      this.width = original.width;
     934      this.tree = cloner.Clone(tree);
     935    }
     936
     937    public Solution(ISymbolicExpressionTree tree, int length, int width)
     938      : base("Solution", "A lawn mower solution.") {
     939      this.tree = tree;
     940      this.length = length;
     941      this.width = width;
     942    }
     943    [StorableHook(HookType.AfterDeserialization)]
     944    private void AfterDeserialization() {
     945    }
     946    public override IDeepCloneable Clone(Cloner cloner) {
     947      return new Solution(this, cloner);
     948    }
     949  }
     950}
     951
     952}}}
     953
     954=== Analyzer Class  ===
     955{{{BestSolutionAnalyzer.cs}}}
     956{{{
     957#!csharp
     958using HeuristicLab.Common;
     959using HeuristicLab.Core;
     960using HeuristicLab.Data;
     961using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     962using HeuristicLab.Operators;
     963using HeuristicLab.Optimization;
     964using HeuristicLab.Parameters;
     965using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     966
     967namespace HeuristicLab.Problems.LawnMower {
     968  [StorableClass]
     969  [Item("Best Lawn Mower Solution Analyzer",
     970        "Analyzer that stores the best lawn mower solution.")]
     971  public class BestSolutionAnalyzer : SingleSuccessorOperator,
     972    ISymbolicExpressionTreeAnalyzer {
     973    #region parameter names
     974    private const string QualityParameterName = "Quality";
     975    private const string SymbolicExpressionTreeParameterName =
     976      "LawnMowerProgram";
     977    private const string LawnWidthParameterName = "LawnWidth";
     978    private const string LawnLengthParameterName = "LawnLength";
     979    private const string BestSolutionParameterName = "Best solution";
     980    private const string ResultsParameterName = "Results";
     981    #endregion
     982
     983
     984    #region parameters
     985    public IScopeTreeLookupParameter<DoubleValue> QualityParameter {
     986      get { return (IScopeTreeLookupParameter<DoubleValue>)
     987                      Parameters[QualityParameterName]; }
     988    }
     989    public IScopeTreeLookupParameter<ISymbolicExpressionTree> SymbolicExpressionTreeParameter {
     990      get { return (IScopeTreeLookupParameter<ISymbolicExpressionTree>)
     991                      Parameters[SymbolicExpressionTreeParameterName]; }
     992    }
     993    public ILookupParameter<IntValue> LawnWidthParameter {
     994      get { return (ILookupParameter<IntValue>)
     995                      Parameters[LawnWidthParameterName]; }
     996    }
     997    public ILookupParameter<IntValue> LawnLengthParameter {
     998      get { return (ILookupParameter<IntValue>)
     999                      Parameters[LawnLengthParameterName]; }
     1000    }
     1001    public ILookupParameter<Solution> BestSolutionParameter {
     1002      get { return (ILookupParameter<Solution>)
     1003                      Parameters[BestSolutionParameterName]; }
     1004    }
     1005    public ILookupParameter<ResultCollection> ResultParameter {
     1006      get { return (ILookupParameter<ResultCollection>)
     1007                      Parameters[ResultsParameterName]; }
     1008    }
     1009    #endregion
     1010
     1011    [StorableConstructor]
     1012    protected BestSolutionAnalyzer(bool deserializing) : base(deserializing) { }
     1013    protected BestSolutionAnalyzer(BestSolutionAnalyzer original,
     1014                                   Cloner cloner) : base(original, cloner) {
     1015    }
     1016
     1017    public BestSolutionAnalyzer() {
     1018      Parameters.Add(
     1019        new ScopeTreeLookupParameter<DoubleValue>(
     1020          QualityParameterName,
     1021          "The solution quality of the lawn mower program."));
     1022      Parameters.Add(
     1023        new ScopeTreeLookupParameter<ISymbolicExpressionTree>(
     1024          SymbolicExpressionTreeParameterName,
     1025          "The lawn mower program to evaluate represented " +
     1026          "as symbolic expression tree."));
     1027      Parameters.Add(
     1028        new LookupParameter<Solution>(
     1029          BestSolutionParameterName, "The best lawn mower solution."));
     1030      Parameters.Add(
     1031        new LookupParameter<IntValue>(
     1032          LawnWidthParameterName, "The width of the lawn to mow."));
     1033      Parameters.Add(
     1034        new LookupParameter<IntValue>(
     1035          LawnLengthParameterName, "The length of the lawn to mow."));
     1036      Parameters.Add(
     1037        new LookupParameter<ResultCollection>(
     1038          ResultsParameterName, "The result collection of the algorithm."));
     1039    }
     1040
     1041    public override IOperation Apply() {
     1042      // get an array of all trees
     1043      // and an array of all qualities
     1044      var trees = SymbolicExpressionTreeParameter.ActualValue;
     1045      var qualities = QualityParameter.ActualValue;
     1046
     1047      // find the tree with the best quality
     1048      double maxQuality = double.NegativeInfinity;
     1049      ISymbolicExpressionTree bestTree = null;
     1050      for (int i = 0; i < qualities.Length; i++) {
     1051        if (qualities[i].Value > maxQuality) {
     1052          maxQuality = qualities[i].Value;
     1053          bestTree = trees[i];
     1054        }
     1055      }
     1056      // create a solution instance
     1057      int length = LawnLengthParameter.ActualValue.Value;
     1058      int width = LawnWidthParameter.ActualValue.Value;
     1059      var bestSolution = new Solution(bestTree, length, width);
     1060      // store the new solution in the best solution parameter
     1061      BestSolutionParameter.ActualValue = bestSolution;
     1062
     1063      // also add the best solution as a result to the result collection
     1064      // or alternatively update the existing result
     1065      var resultCollection = ResultParameter.ActualValue;
     1066      if (!resultCollection.ContainsKey(BestSolutionParameterName)) {
     1067        resultCollection.Add(
     1068          new Result(BestSolutionParameterName,
     1069                     "The best lawn mower solution", bestSolution));
     1070      } else {
     1071        resultCollection[BestSolutionParameterName].Value = bestSolution;
     1072      }
     1073
     1074      // important return base.Apply() to make sure the
     1075      // next operator is queued for execution
     1076      return base.Apply();
     1077    }
     1078
     1079    public override IDeepCloneable Clone(Cloner cloner) {
     1080      return new BestSolutionAnalyzer(this, cloner);
     1081    }
     1082
     1083    // override this property to indicate that this analyzer
     1084    // should be enabled by default in the algorithm
     1085    public bool EnabledByDefault {
     1086      get { return true; }
     1087    }
     1088  }
     1089}
     1090}}}
     1091
     1092=== Solution View Classes ===
     1093{{{SolutionProgramView.cs}}}
     1094{{{
     1095#!csharp
     1096using System.Windows.Forms;
     1097using HeuristicLab.Core.Views;
     1098using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
     1099using HeuristicLab.MainForm;
     1100using HeuristicLab.MainForm.WindowsForms;
     1101
     1102namespace HeuristicLab.Problems.LawnMower {
     1103  // This is a view called 'Lawn Mower Problem View',
     1104  // which can show instances of the class Solution,
     1105  // but it is not used as the default view for these instances.
     1106  [View("Lawn Mower Program View")]
     1107  [Content(typeof(Solution), IsDefaultView = false)]
     1108  public class SolutionProgramView : NamedItemView {
     1109    private Panel panel;
     1110    private GraphicalSymbolicExpressionTreeView graphTreeView;
     1111
     1112    public new Solution Content {
     1113      get { return (Solution)base.Content; }
     1114      set { base.Content = value; }
     1115    }
     1116
     1117    public SolutionProgramView() {
     1118      InitializeComponent();
     1119      // use an existing view from the symbolic expression tree encoding
     1120      // which visualizes symbolic expression trees
     1121      this.graphTreeView = new GraphicalSymbolicExpressionTreeView();
     1122      graphTreeView.Dock = DockStyle.Fill;
     1123      panel.Controls.Add(graphTreeView);
     1124    }
     1125
     1126    protected override void OnContentChanged() {
     1127      base.OnContentChanged();
     1128      if (Content == null) {
     1129        graphTreeView.Content = null;
     1130      } else {
     1131        graphTreeView.Content = Content.Tree;
     1132      }
     1133    }
     1134
     1135    private void InitializeComponent() {
     1136      #region designer code
     1137      [...]
     1138      #endregion
     1139    }
     1140  }
     1141}
     1142}}}
     1143
     1144{{{SolutionLawnView.cs}}}
     1145{{{
     1146#!csharp
     1147using System;
     1148using System.Drawing;
     1149using System.Windows.Forms;
     1150using HeuristicLab.Core.Views;
     1151using HeuristicLab.MainForm;
     1152using HeuristicLab.MainForm.WindowsForms;
     1153
     1154namespace HeuristicLab.Problems.LawnMower {
     1155  // This is a view called 'Lawn Mower Solution View',
     1156  // which can show instances of the class Solution,
     1157  // and it is used as the default view.
     1158  [View("Lawn Mower Solution View")]
     1159  [Content(typeof(Solution), IsDefaultView = true)]
     1160  public class SolutionLawnView : NamedItemView {
     1161    private PictureBox pictureBox;
     1162    private System.Windows.Forms.GroupBox groupBox;
     1163
     1164    public new Solution Content {
     1165      get { return (Solution)base.Content; }
     1166      set { base.Content = value; }
     1167    }
     1168
     1169    public SolutionLawnView() {
     1170      InitializeComponent();
     1171      pictureBox.Image = new Bitmap(200, 200);
     1172    }
     1173
     1174    protected override void OnContentChanged() {
     1175      base.OnContentChanged();
     1176      if (Content == null) {
     1177        using (var g = Graphics.FromImage(pictureBox.Image))
     1178          g.Clear(DefaultBackColor);
     1179      } else {
     1180        bool[,] lawn =
     1181          Interpreter.EvaluateLawnMowerProgram(Content.Length,
     1182                                               Content.Width,
     1183                                               Content.Tree);
     1184        PaintTiles(pictureBox.Image, lawn);
     1185      }
     1186    }
     1187
     1188    private void PaintTiles(Image lawnImage, bool[,] mowed) {
     1189      int w = lawnImage.Width;
     1190      int h = lawnImage.Height;
     1191
     1192      float tileHeight = h / (float)mowed.GetLength(0);
     1193      float tileWidth = w / (float)mowed.GetLength(1);
     1194
     1195      tileWidth = Math.Min(tileWidth, tileHeight);
     1196      tileHeight = tileWidth; // draw square tiles
     1197
     1198      using (var g = Graphics.FromImage(lawnImage)) {
     1199        g.Clear(DefaultBackColor);
     1200        for (int i = 0; i < Content.Length; i++)
     1201          for (int j = 0; j < Content.Width; j++)
     1202            if (mowed[i, j]) {
     1203              g.FillRectangle(Brushes.Chartreuse,
     1204                              tileWidth * j, tileHeight * i,
     1205                              tileWidth, tileHeight);
     1206            } else {
     1207              g.FillRectangle(Brushes.DarkGreen,
     1208                              tileWidth * j, tileHeight * i,
     1209                              tileWidth, tileHeight);
     1210            }
     1211      }
     1212    }
     1213
     1214    private void InitializeComponent() {
     1215      #region designer code
     1216      [...]
     1217      #endregion
     1218    }
     1219  }
     1220}
     1221}}}
     1222
     1223[Koza 1994] J. Koza. Genetic Programming II: Automatic Discovery of Reusable Programs. Cambridge, MA, The MIT Press. 1994