Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/PushProgram.cs @ 15017

Last change on this file since 15017 was 15017, checked in by pkimmesw, 7 years ago

#2665 Fixed Benchmark Problem Definition, Converted LoopExpressions to stateless expressions, Added several unit test to ensure funcionality, Fixed UI bugs

File size: 11.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5namespace HeuristicLab.Problems.ProgramSynthesis.Push.Expressions {
6  using System.Diagnostics;
7  using Constants;
8  using Data.Pool;
9  using Extensions;
10  using Interpreter;
11  using Persistence.Default.CompositeSerializers.Storable;
12
13  [Serializable]
14  [StorableClass]
15  public sealed class PushProgram : Expression, IPooledObject {
16#if DEBUG
17    private const int STRING_REPRESENTATION_COUNT_BEFORE_AGGREGATE = 10;
18#else
19    private const int STRING_REPRESENTATION_COUNT_BEFORE_AGGREGATE = 1;
20#endif
21
22    private static readonly Expression[] EmptyExpressions = new Expression[0];
23    public static readonly PushProgram Empty = new PushProgram();
24
25    private const string Prefix = PushEnvironment.ProgramStartSymbolStr + " ";
26    private const string Postfix = " " + PushEnvironment.ProgramEndSymbolStr;
27    private const string PrefixReduced = "|";
28    private const string PostfixReduced = "|";
29    private const string Delimiter = " ";
30
31    [Storable]
32    private IReadOnlyList<Expression> expressions;
33
34    public PushProgram() : this(EmptyExpressions) { }
35
36    [StorableConstructor]
37    public PushProgram(bool deserializing) : this(EmptyExpressions) { }
38
39    public PushProgram(IReadOnlyList<Expression> expressions) {
40      this.expressions = expressions;
41    }
42
43    public bool IsEmpty { get { return Count == 0; } }
44    public int Count { get { return expressions.Count; } }
45
46    public IReadOnlyList<Expression> Expressions { get { return expressions; } }
47
48    public static PushProgram Create(IManagedPool<PushProgram> pool, IReadOnlyList<Expression> expressions) {
49      var program = pool.Get();
50      program.expressions = expressions;
51      return program;
52    }
53
54    void IPooledObject.Reset() {
55      expressions = null;
56      stringRepresentation = null;
57      depth = null;
58      treeIndex = null;
59      hashCode = null;
60    }
61
62    public static PushProgram Merge(IManagedPool<PushProgram> pool, IManagedPool<PooledList<Expression>> expressionListPool,
63      PushProgram first, Expression second) {
64      var program = pool.Get();
65      var list = expressionListPool.Get();
66
67      list.AddRange(first.Expressions);
68      list.Add(second);
69
70      program.expressions = list;
71
72      return program;
73    }
74
75    public static PushProgram Merge(IManagedPool<PushProgram> pool, IManagedPool<PooledList<Expression>> expressionListPool,
76      Expression first, PushProgram second) {
77      var program = pool.Get();
78      var list = expressionListPool.Get();
79
80      list.Add(first);
81      list.AddRange(second.Expressions);
82
83      program.expressions = list;
84
85      return program;
86    }
87
88    public static PushProgram Merge(IManagedPool<PushProgram> pool, IManagedPool<PooledList<Expression>> expressionListPool,
89      PushProgram first, PushProgram second) {
90      var program = pool.Get();
91      var list = expressionListPool.Get();
92
93      list.AddRange(first.Expressions);
94      list.AddRange(second.Expressions);
95
96      program.expressions = list;
97
98      return program;
99    }
100
101    public int IndexOf(Expression expression) {
102      var max = expressions.Count - 1;
103      for (var i = max; i >= 0; i--) {
104        if (expression.Equals(expressions[i])) return max - i;
105      }
106
107      return -1;
108    }
109
110    [NonSerialized]
111    private string stringRepresentation;
112    public override string StringRepresentation
113    {
114      get
115      {
116        return stringRepresentation ?? (stringRepresentation = BuildStringRepresentation());
117      }
118    }
119
120
121    [NonSerialized]
122    private int? depth;
123    public int Depth
124    {
125      get
126      {
127        if (depth == null) depth = CalcDepth();
128        return depth.Value;
129      }
130    }
131
132    [NonSerialized]
133    private int[] treeIndex;
134    private int[] TreeIndex
135    {
136      get
137      {
138        return treeIndex ?? (treeIndex = BuildTreeIndex());
139      }
140    }
141
142    [NonSerialized]
143    private int? hashCode;
144    public override int GetHashCode() {
145      if (hashCode == null) hashCode = expressions.HashCode();
146      return hashCode.Value;
147    }
148
149    public override bool Equals(object obj) {
150      if (obj == null)
151        return false;
152
153      if (ReferenceEquals(this, obj))
154        return true;
155
156      if (GetType() != obj.GetType())
157        return false;
158
159      var other = (PushProgram)obj;
160
161      return ReferenceEquals(expressions, other.expressions) ||
162             GetHashCode() == other.GetHashCode();
163    }
164
165    public bool Equals(PushProgram other) {
166      return ReferenceEquals(this, other) ||
167             ReferenceEquals(expressions, other.expressions) ||
168             GetHashCode() == other.GetHashCode();
169    }
170
171    public override bool IsNoop(IInternalPushInterpreter interpreter) {
172      return IsEmpty;
173    }
174
175    public override void Eval(IInternalPushInterpreter interpreter) {
176      interpreter.ExecStack.Push(expressions);
177    }
178
179    public override string ToString() {
180      return StringRepresentation;
181    }
182
183    [NonSerialized]
184    private int? totalCount;
185
186    /// <summary>
187    /// Returns the amount of elements in the whole tree
188    /// </summary>
189    /// <returns></returns>
190    public int TotalCount
191    {
192      get
193      {
194        if (totalCount == null) {
195          totalCount = IsEmpty
196            ? 1
197            // + 1 because "this" is also counted
198            : TreeIndex[0] + 1;
199        }
200
201        return totalCount.Value;
202      }
203    }
204
205    [NonSerialized]
206    private int? totalExpressionCount;
207    /// <summary>
208    /// Returns the amount of none program expressions
209    /// </summary>
210    /// <returns></returns>
211    public int TotalExpressionCount
212    {
213      get
214      {
215        if (totalExpressionCount == null) {
216          totalExpressionCount = 0;
217
218          for (var i = 0; i < Count; i++) {
219            var expression = expressions[i];
220
221            totalExpressionCount += expression.IsProgram
222              ? ((PushProgram)expression).TotalExpressionCount
223              : 1;
224          }
225        }
226
227        return totalExpressionCount.Value;
228      }
229    }
230
231    [NonSerialized]
232    private int? branches;
233    /// <summary>
234    /// Returns the amount of branches in the whole tree. Even an empty program represents at least one branch
235    /// </summary>
236    /// <returns></returns>
237    public int Branches
238    {
239      get
240      {
241        if (branches == null) {
242          branches = 1;
243
244          for (var i = 0; i < Count; i++) {
245            var expression = expressions[i];
246
247            if (!expression.IsProgram)
248              continue;
249
250            var program = (PushProgram)expression;
251            branches += program.Branches;
252          }
253        }
254
255        return branches.Value;
256      }
257    }
258
259    public IEnumerable<Expression> DepthLast() {
260      foreach (var expr in expressions) {
261        if (expr.IsProgram)
262          foreach (var sub in ((PushProgram)expr).DepthLast())
263            yield return sub;
264        else yield return expr;
265      }
266    }
267
268    public PushProgram Copy() {
269      if (IsEmpty) return this;
270
271      return new PushProgram(expressions) {
272        stringRepresentation = stringRepresentation,
273        hashCode = hashCode,
274        depth = depth,
275        treeIndex = treeIndex
276      };
277    }
278
279    public PushProgram Copy(IManagedPool<PushProgram> pool) {
280      if (IsEmpty) return this;
281
282      var program = pool.Get(reset: false);
283
284      program.expressions = expressions;
285      program.stringRepresentation = stringRepresentation;
286      program.hashCode = hashCode;
287      program.depth = depth;
288      program.treeIndex = treeIndex;
289
290      return program;
291    }
292
293    public IList<Expression> CopyExpressions() {
294      return new List<Expression>(expressions);
295    }
296
297    public IList<Expression> CopyExpressions(IManagedPool<PooledList<Expression>> expressionListPool) {
298      if (IsEmpty) return EmptyExpressions;
299
300      var copy = expressionListPool.Get();
301      copy.AddRange(expressions);
302
303      return copy;
304    }
305
306    private int CalcDepth() {
307      var maxDepth = 0;
308      for (var i = 0; i < Count; i++) {
309        var expression = expressions[i];
310        if (!expression.IsProgram) continue;
311
312        var expandExpression = (PushProgram)expression;
313        if (expandExpression.Depth > maxDepth)
314          maxDepth = expandExpression.Depth;
315      }
316
317      return maxDepth + 1;
318    }
319
320    private string BuildStringRepresentation() {
321      // prevent too big string
322      return Count > STRING_REPRESENTATION_COUNT_BEFORE_AGGREGATE
323        ? PrefixReduced + Count + PostfixReduced
324        : Prefix + string.Join(Delimiter, expressions.Reverse()) + Postfix;
325    }
326
327    private int[] BuildTreeIndex() {
328      var localTreeIndex = new int[Count];
329      var next = 1;
330
331      for (var i = Count - 1; i >= 0; i--) {
332        var subExpression = expressions[i];
333
334        localTreeIndex[i] = next;
335
336        if (subExpression.IsProgram) {
337          next += ((PushProgram)subExpression).TotalCount;
338        } else {
339          next++;
340        }
341      }
342
343      return localTreeIndex;
344    }
345
346    private static void CheckIfNested(int level, PushProgram target, PushProgram current,
347        IList<PushProgram> visited) {
348      if (visited.Any(x => ReferenceEquals(x, current)))
349        Debugger.Break();
350
351      visited.Add(current);
352      if (level <= 0) {
353        // hierarchy depth > level
354        Debugger.Break();
355        return;
356      }
357
358      for (var i = 0; i < current.Count; i++) {
359        if (current.expressions[i].IsProgram)
360          continue;
361
362        var subExpression = current.expressions[i] as PushProgram;
363
364        if (ReferenceEquals(target, subExpression)) {
365          // Endless recursion dedected
366          Debugger.Break();
367        }
368
369        CheckIfNested(level - 1, target, subExpression, visited);
370      }
371
372      visited.RemoveAt(visited.Count - 1);
373    }
374
375    /// <summary>
376    ///     Returns the element with the given index whereby the tree is indexed in depth first manner
377    /// </summary>
378    /// <param name="index">The index of the required element</param>
379    /// <returns>The required element</returns>
380    public Expression GetFromTree(int index) {
381      return GetFromTree(index, (root, parent, child, childIndex, parentIndex) => child);
382    }
383
384    /// <returns>The required element</returns>
385    public T GetFromTree<T>(int index,
386        Func<PushProgram, PushProgram, Expression, int, int, T> resolver) {
387      return GetFromTree(index, 0, null, resolver);
388    }
389
390    private T GetFromTree<T>(int index, int parentIndex, PushProgram parent,
391        Func<PushProgram, PushProgram, Expression, int, int, T> resolver) {
392      if (index == 0) return resolver(parent, parent, this, 0, parentIndex);
393
394      var min = Count - 1;
395      var max = 0;
396      var mid = (min + max) / 2;
397
398      while ((max <= min) && (TreeIndex[mid] != index)) {
399        if (TreeIndex[mid] > index) max = mid + 1;
400        else min = mid - 1;
401
402        mid = (min + max) / 2;
403      }
404
405      return TreeIndex[mid] == index
406          ? resolver(parent, this, expressions[mid], mid, parentIndex)
407          : (expressions[mid + 1] as PushProgram).GetFromTree(index - TreeIndex[mid + 1], mid + 1,
408              this, resolver);
409    }
410  }
411}
Note: See TracBrowser for help on using the repository browser.