Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2895_PushGP_GenealogyAnalysis/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/PushProgram.cs @ 16724

Last change on this file since 16724 was 15771, checked in by bburlacu, 7 years ago

#2895: Add solution skeleton for PushGP with genealogy analysis.

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