Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2521_ProblemRefactoring/HeuristicLab.Problems.GeneticProgramming/3.3/Boolean/MultiplexerProblem.cs @ 17382

Last change on this file since 17382 was 17382, checked in by mkommend, 5 years ago

#2521: Refactored single-objective problems to use EvaluationResult instead of double as return type from Evaluate.

File size: 8.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Diagnostics.Contracts;
25using System.Linq;
26using System.Threading;
27using HEAL.Attic;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
32using HeuristicLab.Optimization;
33using HeuristicLab.Parameters;
34
35
36namespace HeuristicLab.Problems.GeneticProgramming.Boolean {
37  [Item("Multiplexer Problem (MUX)",
38    "The Boolean multiplexer genetic programming problem. See Koza 1992, page 171, section 7.4.1 11-multiplexer.")]
39  [Creatable(CreatableAttribute.Categories.GeneticProgrammingProblems, Priority = 900)]
40  [StorableType("6DFE64E4-3968-446F-AE3D-FAF13C18930C")]
41  public sealed class MultiplexerProblem : SymbolicExpressionTreeProblem {
42
43    #region parameter names
44    private const string NumberOfBitsParameterName = "NumberOfBits";
45    #endregion
46
47    #region Parameter Properties
48    public IFixedValueParameter<IntValue> NumberOfBitsParameter {
49      get { return (IFixedValueParameter<IntValue>)Parameters[NumberOfBitsParameterName]; }
50    }
51    #endregion
52
53    #region Properties
54    public int NumberOfBits {
55      get { return NumberOfBitsParameter.Value.Value; }
56      set { NumberOfBitsParameter.Value.Value = value; }
57    }
58    #endregion
59
60    #region item cloning and persistence
61    // persistence
62    [StorableConstructor]
63    private MultiplexerProblem(StorableConstructorFlag _) : base(_) { }
64    [StorableHook(HookType.AfterDeserialization)]
65    private void AfterDeserialization() {
66      RegisterEventHandlers();
67    }
68
69    // cloning
70    private MultiplexerProblem(MultiplexerProblem original, Cloner cloner)
71      : base(original, cloner) {
72      RegisterEventHandlers();
73    }
74    public override IDeepCloneable Clone(Cloner cloner) {
75      return new MultiplexerProblem(this, cloner);
76    }
77    #endregion
78
79
80    public MultiplexerProblem()
81      : base(new SymbolicExpressionTreeEncoding()) {
82      Maximization = true;
83      Parameters.Add(new FixedValueParameter<IntValue>(NumberOfBitsParameterName,
84        "The number of bits for the input parameter for the multiplexer function. This is the sum of the number of address bits and the number of input lines. E.g. the 11-MUX has 3 address bits and 8 input lines",
85        new IntValue(11)));
86
87      Encoding.TreeLength = 100;
88      Encoding.TreeDepth = 17;
89
90      UpdateGrammar();
91      RegisterEventHandlers();
92    }
93
94    private void UpdateGrammar() {
95      var g = new SimpleSymbolicExpressionGrammar();
96      g.AddSymbols(new[] { "AND", "OR" }, 2, 2); // See Koza 1992, page 171, section 7.4.1 11-multiplexer
97      g.AddSymbols(new[] { "NOT" }, 1, 1);
98      g.AddSymbols(new[] { "IF" }, 3, 3);
99
100      // find the number of address lines and input lines
101      // e.g. 11-MUX: 3 addrBits + 8 input bits
102
103      var addrBits = (int)Math.Log(NumberOfBits, 2); // largest power of two that fits into the number of bits
104      var inputBits = NumberOfBits - addrBits;
105
106      for (int i = 0; i < addrBits; i++)
107        g.AddTerminalSymbol(string.Format("a{0}", i));
108      for (int i = 0; i < inputBits; i++)
109        g.AddTerminalSymbol(string.Format("d{0}", i));
110
111      Encoding.GrammarParameter.ReadOnly = false;
112      Encoding.Grammar = g;
113      Encoding.GrammarParameter.ReadOnly = true;
114
115      BestKnownQualityParameter.ReadOnly = false;
116      BestKnownQuality = Math.Pow(2, NumberOfBits); // this is a benchmark problem (the best achievable quality is known for a given number of bits)
117      BestKnownQualityParameter.ReadOnly = true;
118    }
119
120
121    public override ISingleObjectiveEvaluationResult Evaluate(ISymbolicExpressionTree tree, IRandom random, CancellationToken cancellationToken) {
122      if (NumberOfBits <= 0) throw new NotSupportedException("Number of bits must be larger than zero.");
123      if (NumberOfBits > 37) throw new NotSupportedException("Multiplexer does not support problems with number of bits > 37.");
124      var bs = Enumerable.Range(0, (int)Math.Pow(2, NumberOfBits));
125      var addrBits = (int)Math.Log(NumberOfBits, 2); // largest power of two that fits into the number of bits
126      var inputBits = NumberOfBits - addrBits;
127      var targets = bs.Select(b => CalcTarget(b, addrBits, inputBits));
128      var pred = Interpret(tree, bs, (byte)addrBits);
129      var quality = targets.Zip(pred, (t, p) => t == p ? 1 : 0).Sum(); // count number of correct predictions
130
131      return new SingleObjectiveEvaluationResult(quality);
132    }
133
134    private bool CalcTarget(int b, int addrBits, int inputBits) {
135      Contract.Assert(addrBits > 0);
136      // calc addr
137      int addr = 0;
138      for (int i = addrBits - 1; i >= 0; i--) {
139        addr = addr << 1;
140        if (GetBits(b, (byte)i)) addr += 1;
141      }
142      if (addr <= inputBits)
143        return GetBits(b, (byte)(addrBits + addr));
144      else return false; // in case the number of bits is smaller then necessary we assume that the remaining lines are false
145    }
146
147    private static IEnumerable<bool> Interpret(ISymbolicExpressionTree tree, IEnumerable<int> bs, byte addrBits) {
148      // skip programRoot and startSymbol
149      return InterpretRec(tree.Root.GetSubtree(0).GetSubtree(0), bs, addrBits);
150    }
151
152
153    private static IEnumerable<bool> InterpretRec(ISymbolicExpressionTreeNode node, IEnumerable<int> bs, byte addrBits) {
154      Func<ISymbolicExpressionTreeNode, Func<bool, bool>, IEnumerable<bool>> unaryEval =
155        (child, f) => InterpretRec(child, bs, addrBits).Select(f);
156      Func<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode, Func<bool, bool, bool>, IEnumerable<bool>> binaryEval =
157        (left, right, f) => InterpretRec(left, bs, addrBits).Zip(InterpretRec(right, bs, addrBits), f);
158
159      switch (node.Symbol.Name) {
160        case "AND": return binaryEval(node.GetSubtree(0), node.GetSubtree(1), (x, y) => x & y);
161        case "OR": return binaryEval(node.GetSubtree(0), node.GetSubtree(1), (x, y) => x | y);
162        case "NOT": return unaryEval(node.GetSubtree(0), (x) => !x);
163        case "IF": return EvalIf(node.GetSubtree(0), node.GetSubtree(1), node.GetSubtree(2), bs, addrBits);
164        default: {
165            if (node.Symbol.Name[0] == 'a') {
166              byte bitPos;
167              if (byte.TryParse(node.Symbol.Name.Substring(1), out bitPos)) {
168                return bs.Select(b => GetBits(b, bitPos));
169              }
170            } else if (node.Symbol.Name[0] == 'd') {
171              byte bitPos;
172              if (byte.TryParse(node.Symbol.Name.Substring(1), out bitPos)) {
173                return bs.Select(b => GetBits(b, (byte)(bitPos + addrBits))); // offset of input line bits
174              }
175            }
176
177            throw new NotSupportedException(string.Format("Found unexpected symbol {0}", node.Symbol.Name));
178          }
179      }
180    }
181
182    private static IEnumerable<bool> EvalIf(ISymbolicExpressionTreeNode pred, ISymbolicExpressionTreeNode trueBranch, ISymbolicExpressionTreeNode falseBranch, IEnumerable<int> bs, byte addrBits) {
183      var preds = InterpretRec(pred, bs, addrBits).GetEnumerator();
184      var tB = InterpretRec(trueBranch, bs, addrBits).GetEnumerator();
185      var fB = InterpretRec(falseBranch, bs, addrBits).GetEnumerator();
186      // start enumerators
187
188      while (preds.MoveNext() & tB.MoveNext() & fB.MoveNext()) {
189        if (preds.Current) yield return tB.Current;
190        else yield return fB.Current;
191      }
192    }
193
194    private static bool GetBits(int b, byte bitPos) {
195      return (b & (1 << bitPos)) != 0;
196    }
197
198    #region events
199    private void RegisterEventHandlers() {
200      NumberOfBitsParameter.Value.ValueChanged += (sender, args) => UpdateGrammar();
201    }
202    #endregion
203  }
204}
Note: See TracBrowser for help on using the repository browser.