Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2665 Fixed small issues, testet benchmark suite, added INX Expressions

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