Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.4/Creators/BalancedTreeCreator.cs @ 18064

Last change on this file since 18064 was 18064, checked in by mkommend, 3 years ago

#2997: Simplified tree creator interface and removed cloning of grammars,

File size: 9.7 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.Linq;
25using HEAL.Attic;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Parameters;
30using HeuristicLab.PluginInfrastructure;
31using HeuristicLab.Random;
32
33namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding {
34  [NonDiscoverableType]
35  [StorableType("AA3649C4-18CF-480B-AA41-F5D6F148B494")]
36  [Item("BalancedTreeCreator", "An operator that produces trees with a specified distribution")]
37  public class BalancedTreeCreator : SymbolicExpressionTreeCreator {
38    private const string IrregularityBiasParameterName = "IrregularityBias";
39
40    public IFixedValueParameter<PercentValue> IrregularityBiasParameter {
41      get { return (IFixedValueParameter<PercentValue>)Parameters[IrregularityBiasParameterName]; }
42    }
43
44    public double IrregularityBias {
45      get { return IrregularityBiasParameter.Value.Value; }
46      set { IrregularityBiasParameter.Value.Value = value; }
47    }
48
49    [StorableConstructor]
50    protected BalancedTreeCreator(StorableConstructorFlag _) : base(_) { }
51
52    [StorableHook(HookType.AfterDeserialization)]
53    private void AfterDeserialization() {
54      if (!Parameters.ContainsKey(IrregularityBiasParameterName)) {
55        Parameters.Add(new FixedValueParameter<PercentValue>(IrregularityBiasParameterName, "Allows to bias tree initialization towards less balanced/regular shapes. Set to 0% for most balanced and 100% for least balanced trees. (default = 0%)", new PercentValue(0.0)));
56      }
57    }
58
59    protected BalancedTreeCreator(BalancedTreeCreator original, Cloner cloner) : base(original, cloner) { }
60
61    public BalancedTreeCreator() {
62      Parameters.Add(new FixedValueParameter<PercentValue>(IrregularityBiasParameterName, new PercentValue(0.0)));
63    }
64
65    public override IDeepCloneable Clone(Cloner cloner) {
66      return new BalancedTreeCreator(this, cloner);
67    }
68
69    public override ISymbolicExpressionTree CreateTree(IRandom random, ISymbolicExpressionGrammar grammar, int maxLength, int maxDepth) {
70      return Create(random, grammar, maxLength, maxDepth, IrregularityBias);
71    }
72
73    public static ISymbolicExpressionTree Create(IRandom random, ISymbolicExpressionGrammar grammar, int maxLength, int maxDepth, double irregularityBias = 0) {
74      int targetLength = random.Next(3, maxLength); // because we have 2 extra nodes for the root and start symbols, and the end is exclusive
75      return CreateExpressionTree(random, grammar, targetLength, maxDepth, irregularityBias);
76    }
77
78    public static ISymbolicExpressionTree CreateExpressionTree(IRandom random, ISymbolicExpressionGrammar grammar, int targetLength, int maxDepth, double irregularityBias = 1) {
79      // even lengths cannot be achieved without symbols of odd arity
80      // therefore we randomly pick a neighbouring odd length value
81      var tree = MakeStump(random, grammar); // create a stump consisting of just a ProgramRootSymbol and a StartSymbol
82      CreateExpression(random, tree.Root.GetSubtree(0), targetLength - tree.Length, maxDepth - 2, irregularityBias); // -2 because the stump has length 2 and depth 2
83      return tree;
84    }
85
86    private static ISymbolicExpressionTreeNode SampleNode(IRandom random, ISymbolicExpressionTreeGrammar grammar, IEnumerable<ISymbol> allowedSymbols, int minChildArity, int maxChildArity) {
87      var candidates = new List<ISymbol>();
88      var weights = new List<double>();
89
90      foreach (var s in allowedSymbols) {
91        var minSubtreeCount = grammar.GetMinimumSubtreeCount(s);
92        var maxSubtreeCount = grammar.GetMaximumSubtreeCount(s);
93
94        if (maxChildArity < minSubtreeCount || minChildArity > maxSubtreeCount) { continue; }
95
96        candidates.Add(s);
97        weights.Add(s.InitialFrequency);
98      }
99      var symbol = candidates.SampleProportional(random, 1, weights).First();
100      var node = symbol.CreateTreeNode();
101      if (node.HasLocalParameters) {
102        node.ResetLocalParameters(random);
103      }
104      return node;
105    }
106
107    public static void CreateExpression(IRandom random, ISymbolicExpressionTreeNode root, int targetLength, int maxDepth, double irregularityBias = 1) {
108      var grammar = root.Grammar;
109      var minSubtreeCount = grammar.GetMinimumSubtreeCount(root.Symbol);
110      var maxSubtreeCount = grammar.GetMaximumSubtreeCount(root.Symbol);
111      var arity = random.Next(minSubtreeCount, maxSubtreeCount + 1);
112      int openSlots = arity;
113
114      var allowedSymbols = grammar.AllowedSymbols.Where(x => !(x is ProgramRootSymbol || x is GroupSymbol || x is Defun || x is StartSymbol)).ToList();
115      bool hasUnarySymbols = allowedSymbols.Any(x => grammar.GetMinimumSubtreeCount(x) <= 1 && grammar.GetMaximumSubtreeCount(x) >= 1);
116
117      if (!hasUnarySymbols && targetLength % 2 == 0) {
118        // without functions of arity 1 some target lengths cannot be reached
119        targetLength = random.NextDouble() < 0.5 ? targetLength - 1 : targetLength + 1;
120      }
121
122      var tuples = new List<NodeInfo>(targetLength) { new NodeInfo { Node = root, Depth = 0, Arity = arity } };
123
124      // we use tuples.Count instead of targetLength in the if condition
125      // because depth limits may prevent reaching the target length
126      for (int i = 0; i < tuples.Count; ++i) {
127        var t = tuples[i];
128        var node = t.Node;
129
130        for (int childIndex = 0; childIndex < t.Arity; ++childIndex) {
131          // min and max arity here refer to the required arity limits for the child node
132          int minChildArity = 0;
133          int maxChildArity = 0;
134
135          var allowedChildSymbols = allowedSymbols.Where(x => grammar.IsAllowedChildSymbol(node.Symbol, x, childIndex)).ToList();
136
137          // if we are reaching max depth we have to fill the slot with a leaf node (max arity will be zero)
138          // otherwise, find the maximum value from the grammar which does not exceed the length limit
139          if (t.Depth < maxDepth - 1 && openSlots < targetLength) {
140
141            // we don't want to allow sampling a leaf symbol if it prevents us from reaching the target length
142            // this should be allowed only when we have enough open expansion points (more than one)
143            // the random check against the irregularity bias helps to increase shape variability when the conditions are met
144            int minAllowedArity = allowedChildSymbols.Min(x => grammar.GetMaximumSubtreeCount(x));
145            if (minAllowedArity == 0 && (openSlots - tuples.Count <= 1 || random.NextDouble() > irregularityBias)) {
146              minAllowedArity = 1;
147            }
148
149            // finally adjust min and max arity according to the expansion limits
150            int maxAllowedArity = allowedChildSymbols.Max(x => grammar.GetMaximumSubtreeCount(x));
151            maxChildArity = Math.Min(maxAllowedArity, targetLength - openSlots);
152            minChildArity = Math.Min(minAllowedArity, maxChildArity);
153          }
154         
155          // sample a random child with the arity limits
156          var child = SampleNode(random, grammar, allowedChildSymbols, minChildArity, maxChildArity);
157
158          // get actual child arity limits
159          minChildArity = Math.Max(minChildArity, grammar.GetMinimumSubtreeCount(child.Symbol));
160          maxChildArity = Math.Min(maxChildArity, grammar.GetMaximumSubtreeCount(child.Symbol));
161          minChildArity = Math.Min(minChildArity, maxChildArity);
162
163          // pick a random arity for the new child node
164          var childArity = random.Next(minChildArity, maxChildArity + 1);
165          var childDepth = t.Depth + 1;
166          node.AddSubtree(child);
167          tuples.Add(new NodeInfo { Node = child, Depth = childDepth, Arity = childArity });
168          openSlots += childArity;
169        }
170      }
171    }
172
173    #region helpers
174    private class NodeInfo {
175      public ISymbolicExpressionTreeNode Node;
176      public int Depth;
177      public int Arity;
178    }
179
180    private static ISymbolicExpressionTree MakeStump(IRandom random, ISymbolicExpressionGrammar grammar) {
181      SymbolicExpressionTree tree = new SymbolicExpressionTree();
182      var rootNode = (SymbolicExpressionTreeTopLevelNode)grammar.ProgramRootSymbol.CreateTreeNode();
183      if (rootNode.HasLocalParameters) rootNode.ResetLocalParameters(random);
184      rootNode.SetGrammar(grammar.CreateExpressionTreeGrammar());
185
186      var startNode = (SymbolicExpressionTreeTopLevelNode)grammar.StartSymbol.CreateTreeNode();
187      if (startNode.HasLocalParameters) startNode.ResetLocalParameters(random);
188      startNode.SetGrammar(grammar.CreateExpressionTreeGrammar());
189
190      rootNode.AddSubtree(startNode);
191      tree.Root = rootNode;
192      return tree;
193    }
194
195    public void CreateExpression(IRandom random, ISymbolicExpressionTreeNode seedNode, int maxTreeLength, int maxTreeDepth) {
196      CreateExpression(random, seedNode, maxTreeLength, maxTreeDepth, IrregularityBias);
197    }
198    #endregion
199  }
200}
Note: See TracBrowser for help on using the repository browser.