1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Diagnostics.Contracts;
|
---|
4 | using System.Linq;
|
---|
5 | using System.Net.NetworkInformation;
|
---|
6 | using System.Text;
|
---|
7 | using System.Threading.Tasks;
|
---|
8 | using HeuristicLab.Common;
|
---|
9 | using HeuristicLab.Core;
|
---|
10 | using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
|
---|
11 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
12 |
|
---|
13 | namespace HeuristicLab.Algorithms.IteratedSymbolicExpressionConstruction {
|
---|
14 | // this class is a base class for symbolic expression construction policies
|
---|
15 | // it filters allowed actions based on tree length constraint
|
---|
16 | // and keeps all visited derivation paths in memory to prevent sampling of duplicates
|
---|
17 | [StorableClass]
|
---|
18 | public abstract class SymbolicExpressionConstructionPolicyBase : ParameterizedNamedItem, ISymbolicExpressionConstructionPolicy {
|
---|
19 | [Storable]
|
---|
20 | public SymbolicExpressionTreeProblem Problem { get; private set; }
|
---|
21 | [Storable]
|
---|
22 | public IRandom Random { get; private set; }
|
---|
23 | private SearchTree<ISymbol> searchTree; // tree of replacement symbols
|
---|
24 |
|
---|
25 | private class Slot {
|
---|
26 | public ISymbolicExpressionTreeNode parent;
|
---|
27 | public int childIdx;
|
---|
28 | public int minSize;
|
---|
29 | }
|
---|
30 |
|
---|
31 | [StorableHook(HookType.AfterDeserialization)]
|
---|
32 | private void AfterDeserialization() {
|
---|
33 | searchTree = new SearchTree<ISymbol>();
|
---|
34 | }
|
---|
35 | protected SymbolicExpressionConstructionPolicyBase(SymbolicExpressionConstructionPolicyBase original, Cloner cloner)
|
---|
36 | : base(original, cloner) {
|
---|
37 | this.Problem = cloner.Clone(original.Problem);
|
---|
38 | this.Random = cloner.Clone(original.Random);
|
---|
39 |
|
---|
40 | // search tree is not cloned or stored
|
---|
41 | searchTree = new SearchTree<ISymbol>();
|
---|
42 | }
|
---|
43 |
|
---|
44 | [StorableConstructor]
|
---|
45 | protected SymbolicExpressionConstructionPolicyBase(bool deserializing) : base(deserializing) { }
|
---|
46 |
|
---|
47 | protected SymbolicExpressionConstructionPolicyBase() {
|
---|
48 |
|
---|
49 | }
|
---|
50 |
|
---|
51 | public virtual void Initialize(SymbolicExpressionTreeProblem problem, IRandom random) {
|
---|
52 | this.Problem = problem;
|
---|
53 | this.Random = random;
|
---|
54 | this.searchTree = new SearchTree<ISymbol>(); // represents all realized actionSequences as a prefix tree
|
---|
55 | }
|
---|
56 |
|
---|
57 | public ISymbolicExpressionTree Sample(out IEnumerable<object> stateSequence) {
|
---|
58 | var actions = new List<ISymbol>();
|
---|
59 | var states = new List<object>();
|
---|
60 |
|
---|
61 | var openSlots = new Stack<Slot>();
|
---|
62 | var g = Problem.Encoding.Grammar;
|
---|
63 | var root = g.ProgramRootSymbol.CreateTreeNode();
|
---|
64 | int maxDepth = Problem.Encoding.TreeDepthParameter.Value.Value; // ignored
|
---|
65 | int maxLen = Problem.Encoding.TreeLengthParameter.Value.Value;
|
---|
66 |
|
---|
67 |
|
---|
68 | Contract.Assert(Problem.Encoding.FunctionDefinitions == 0);
|
---|
69 | openSlots.Push(new Slot() { parent = root, childIdx = 0, minSize = g.GetMinimumExpressionLength(root.Symbol) - 1 }); // at least two nodes are necessary below root
|
---|
70 |
|
---|
71 | // tree size lower bound is the current tree size + the sum of the minimal size for all open slots
|
---|
72 | int treeSize = 1;
|
---|
73 |
|
---|
74 | searchTree.ResetPosition();
|
---|
75 |
|
---|
76 | while (openSlots.Any()) {
|
---|
77 | var next = openSlots.First();
|
---|
78 | openSlots.Pop();
|
---|
79 | var parent = next.parent;
|
---|
80 | var childIdx = next.childIdx;
|
---|
81 |
|
---|
82 | if (searchTree.IsLeafNode()) {
|
---|
83 | var allowedChildSymbols = g.GetAllowedChildSymbols(parent.Symbol, childIdx)
|
---|
84 | .Where(a => a.Enabled)
|
---|
85 | .Where(a => treeSize + g.GetMinimumExpressionLength(a) + openSlots.Select(e => e.minSize).Sum() <= maxLen)
|
---|
86 | .OrderBy(a => a.MaximumArity); // terminals should be tried first
|
---|
87 |
|
---|
88 | searchTree.ExpandCurrentNode(allowedChildSymbols);
|
---|
89 | }
|
---|
90 |
|
---|
91 | if (!searchTree.ChildValues.Any()) {
|
---|
92 | throw new InvalidProgramException(string.Format("Couldn't construct a valid tree of maximum length {0} or all possible trees have been visited", maxLen));
|
---|
93 | }
|
---|
94 |
|
---|
95 | var alternatives = searchTree.ChildValues.ToArray(); // TODO perf
|
---|
96 |
|
---|
97 | // generate follow states
|
---|
98 | var followStates = new object[alternatives.Length];
|
---|
99 | for (int i = 0; i < followStates.Length; i++) {
|
---|
100 | // temporarily make the replacement and create the followState object
|
---|
101 | var childNode = alternatives[i].CreateTreeNode();
|
---|
102 | if (childNode.HasLocalParameters) {
|
---|
103 | throw new NotSupportedException("Symbols with parameters are not supported by construction policies for symbolic expressions. " +
|
---|
104 | "Try to reformulate the problem so that only discrete actions are necessary");
|
---|
105 | // childNode.ResetLocalParameters(Random);
|
---|
106 | }
|
---|
107 | parent.AddSubtree(childNode);
|
---|
108 | actions.Add(alternatives[i]);
|
---|
109 |
|
---|
110 | // states might be defined differently be different policies
|
---|
111 | // this allows policies to calculate the state as a function of the current tree and the position where it is changed,
|
---|
112 | // or as a function of the list of actions so far,
|
---|
113 | // or as a function of both
|
---|
114 | followStates[i] = CreateState(root, actions, parent, childIdx);
|
---|
115 |
|
---|
116 | // roll back the change
|
---|
117 | parent.RemoveSubtree(parent.SubtreeCount - 1);
|
---|
118 | actions.RemoveAt(actions.Count - 1);
|
---|
119 | }
|
---|
120 |
|
---|
121 | // select one of the follow states and prepare for the next step
|
---|
122 | var selectedIdx = Select(followStates, Random);
|
---|
123 | actions.Add(alternatives[selectedIdx]);
|
---|
124 | states.Add(followStates[selectedIdx]);
|
---|
125 |
|
---|
126 | {
|
---|
127 | // and add child node to parent
|
---|
128 | var childNode = alternatives[selectedIdx].CreateTreeNode();
|
---|
129 |
|
---|
130 | Contract.Assert(parent.SubtreeCount == childIdx); // enforce left-canonical derivation
|
---|
131 | parent.AddSubtree(childNode);
|
---|
132 | treeSize++;
|
---|
133 |
|
---|
134 | // push new slots
|
---|
135 | for (int chIdx = g.GetMinimumSubtreeCount(childNode.Symbol) -1 ; chIdx >= 0; chIdx--) {
|
---|
136 | int minForChild = g.GetAllowedChildSymbols(childNode.Symbol, chIdx)
|
---|
137 | .Min(a => g.GetMinimumExpressionLength(a)); // min length of all possible alts for the slot
|
---|
138 | openSlots.Push(new Slot() { parent = childNode, childIdx = chIdx, minSize = minForChild });
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 | // if this is the last slot we never have to revisit selectedIdx
|
---|
143 | if (!openSlots.Any()) {
|
---|
144 | searchTree.RemoveBranch(alternatives[selectedIdx]);
|
---|
145 | } else {
|
---|
146 | searchTree.Follow(alternatives[selectedIdx]);
|
---|
147 | }
|
---|
148 | }
|
---|
149 |
|
---|
150 | stateSequence = states;
|
---|
151 | return new SymbolicExpressionTree(root);
|
---|
152 | }
|
---|
153 |
|
---|
154 | /// <summary>
|
---|
155 | /// Choose one of the follow states
|
---|
156 | /// </summary>
|
---|
157 | /// <param name="followStates"></param>
|
---|
158 | /// <param name="random"></param>
|
---|
159 | /// <returns>The index of the selected follow state</returns>
|
---|
160 | protected abstract int Select(IReadOnlyList<object> followStates, IRandom random);
|
---|
161 | public abstract void Update(IEnumerable<object> stateSequence, double quality);
|
---|
162 |
|
---|
163 | protected abstract object CreateState(ISymbolicExpressionTreeNode root, List<ISymbol> actionSequence, ISymbolicExpressionTreeNode parent, int childIdx);
|
---|
164 | }
|
---|
165 | }
|
---|