Free cookie consent management tool by TermsFeed Policy Generator

source: addons/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/PushProgram.cs

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

#2665 Solution Cleanup

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