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
RevLine 
[14745]1using System;
2using System.Collections.Generic;
3using System.Linq;
[15771]4using System.Diagnostics;
5using System.Diagnostics.CodeAnalysis;
[14727]6
[15771]7using HeuristicLab.Common;
8using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
[15334]9
[15771]10namespace HeuristicLab.Problems.ProgramSynthesis {
[15189]11
[14746]12  [Serializable]
[14952]13  [StorableClass]
[15017]14  public sealed class PushProgram : Expression, IPooledObject {
[14777]15#if DEBUG
[15017]16    private const int STRING_REPRESENTATION_COUNT_BEFORE_AGGREGATE = 10;
[14777]17#else
[15017]18    private const int STRING_REPRESENTATION_COUNT_BEFORE_AGGREGATE = 1;
[14777]19#endif
20
[14746]21    private static readonly Expression[] EmptyExpressions = new Expression[0];
[14745]22    public static readonly PushProgram Empty = new PushProgram();
23
[15017]24    private const string Prefix = PushEnvironment.ProgramStartSymbolStr + " ";
25    private const string Postfix = " " + PushEnvironment.ProgramEndSymbolStr;
26    private const string PrefixReduced = "|";
27    private const string PostfixReduced = "|";
[14727]28    private const string Delimiter = " ";
29
[14952]30    [Storable]
[14777]31    private IReadOnlyList<Expression> expressions;
[14744]32
[14952]33    public PushProgram() : this(EmptyExpressions) { }
[14727]34
[14952]35    [StorableConstructor]
[15334]36    private PushProgram(bool deserializing) : this(EmptyExpressions) { }
[14952]37
[14777]38    public PushProgram(IReadOnlyList<Expression> expressions) {
[14746]39      this.expressions = expressions;
[14744]40    }
[14727]41
[15189]42    public PushProgram(PushProgram origin, Cloner cloner) {
43      stringRepresentation = origin.stringRepresentation;
44      hashCode = origin.hashCode;
45      depth = origin.depth;
46      treeIndex = origin.treeIndex;
[15344]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;
[15189]54    }
55
56    public override IDeepCloneable Clone(Cloner cloner) {
57      return new PushProgram(this, cloner);
58    }
59
[14746]60    public bool IsEmpty { get { return Count == 0; } }
[14777]61    public int Count { get { return expressions.Count; } }
[14746]62
[14908]63    public IReadOnlyList<Expression> Expressions { get { return expressions; } }
[14746]64
[14777]65    public static PushProgram Create(IManagedPool<PushProgram> pool, IReadOnlyList<Expression> expressions) {
[14746]66      var program = pool.Get();
67      program.expressions = expressions;
68      return program;
69    }
70
[14777]71    void IPooledObject.Reset() {
72      expressions = null;
[14746]73      stringRepresentation = null;
74      depth = null;
75      treeIndex = null;
76      hashCode = null;
77    }
78
[14777]79    public static PushProgram Merge(IManagedPool<PushProgram> pool, IManagedPool<PooledList<Expression>> expressionListPool,
80      PushProgram first, Expression second) {
[14746]81      var program = pool.Get();
[14777]82      var list = expressionListPool.Get();
[14746]83
[14777]84      list.AddRange(first.Expressions);
85      list.Add(second);
[14746]86
[14777]87      program.expressions = list;
88
[14746]89      return program;
90    }
91
[14777]92    public static PushProgram Merge(IManagedPool<PushProgram> pool, IManagedPool<PooledList<Expression>> expressionListPool,
93      Expression first, PushProgram second) {
[14746]94      var program = pool.Get();
[14777]95      var list = expressionListPool.Get();
[14746]96
[14777]97      list.Add(first);
98      list.AddRange(second.Expressions);
[14746]99
[14777]100      program.expressions = list;
[14746]101
102      return program;
103    }
104
[14777]105    public static PushProgram Merge(IManagedPool<PushProgram> pool, IManagedPool<PooledList<Expression>> expressionListPool,
106      PushProgram first, PushProgram second) {
[14746]107      var program = pool.Get();
[14777]108      var list = expressionListPool.Get();
[14746]109
[14777]110      list.AddRange(first.Expressions);
111      list.AddRange(second.Expressions);
[14746]112
[14777]113      program.expressions = list;
[14746]114
115      return program;
116    }
117
118    public int IndexOf(Expression expression) {
[14777]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;
[14746]125    }
126
[15017]127    [NonSerialized]
[14730]128    private string stringRepresentation;
[15771]129    public override string StringRepresentation {
130      get {
[15017]131        return stringRepresentation ?? (stringRepresentation = BuildStringRepresentation());
[14730]132      }
133    }
134
[14746]135
[15017]136    [NonSerialized]
[14746]137    private int? depth;
[15771]138    public int Depth {
139      get {
[14746]140        if (depth == null) depth = CalcDepth();
141        return depth.Value;
[14730]142      }
143    }
144
[15017]145    [NonSerialized]
[14745]146    private int[] treeIndex;
[15771]147    private int[] TreeIndex {
148      get {
[15017]149        return treeIndex ?? (treeIndex = BuildTreeIndex());
[14730]150      }
151    }
152
[15017]153    [NonSerialized]
[14746]154    private int? hashCode;
[15334]155    [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
[14746]156    public override int GetHashCode() {
[15017]157      if (hashCode == null) hashCode = expressions.HashCode();
[14746]158      return hashCode.Value;
[14730]159    }
160
[15017]161    public override bool Equals(object obj) {
162      if (obj == null)
163        return false;
[14745]164
[14777]165      if (ReferenceEquals(this, obj))
166        return true;
167
[14908]168      if (GetType() != obj.GetType())
[14777]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
[14952]183    public override bool IsNoop(IInternalPushInterpreter interpreter) {
184      return IsEmpty;
185    }
186
187    public override void Eval(IInternalPushInterpreter interpreter) {
[14908]188      interpreter.ExecStack.Push(expressions);
[14745]189    }
190
[14727]191    public override string ToString() {
[14746]192      return StringRepresentation;
[14727]193    }
194
[15017]195    [NonSerialized]
196    private int? totalCount;
197
[14727]198    /// <summary>
199    /// Returns the amount of elements in the whole tree
200    /// </summary>
201    /// <returns></returns>
[15771]202    public int TotalCount {
203      get {
[15017]204        if (totalCount == null) {
205          totalCount = IsEmpty
[14727]206            ? 1
[14744]207            // + 1 because "this" is also counted
[14730]208            : TreeIndex[0] + 1;
[15017]209        }
210
211        return totalCount.Value;
[14727]212      }
213    }
214
[15017]215    [NonSerialized]
[15366]216    private int? totalInstructionCount;
[15017]217    /// <summary>
218    /// Returns the amount of none program expressions
219    /// </summary>
220    /// <returns></returns>
[15771]221    public int TotalInstructionCount {
222      get {
[15366]223        if (totalInstructionCount == null) {
224          totalInstructionCount = 0;
[15017]225
226          for (var i = 0; i < Count; i++) {
227            var expression = expressions[i];
228
[15366]229            totalInstructionCount += expression.IsProgram
230              ? ((PushProgram)expression).TotalInstructionCount
[15017]231              : 1;
232          }
233        }
234
[15366]235        return totalInstructionCount.Value;
[15017]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>
[15771]245    public int Branches {
246      get {
[15334]247        if (branches == null) CountBranches();
248        return branches.Value;
249      }
250    }
[15017]251
[15334]252    private void CountBranches() {
253      branches = 1;
[15017]254
[15334]255      for (var i = 0; i < Count; i++) {
256        var expression = expressions[i];
[15017]257
[15334]258        if (!expression.IsProgram)
259          continue;
[15017]260
[15334]261        var program = (PushProgram)expression;
262        branches += program.Branches;
[15017]263      }
264    }
265
[14834]266    public IEnumerable<Expression> DepthLast() {
[14908]267      foreach (var expr in expressions) {
[14777]268        if (expr.IsProgram)
[14834]269          foreach (var sub in ((PushProgram)expr).DepthLast())
[14727]270            yield return sub;
[14777]271        else yield return expr;
[14727]272      }
273    }
274
[15189]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
[14727]292    public PushProgram Copy() {
[14746]293      if (IsEmpty) return this;
294
[14777]295      return new PushProgram(expressions) {
296        stringRepresentation = stringRepresentation,
297        hashCode = hashCode,
298        depth = depth,
299        treeIndex = treeIndex
[14744]300      };
[14727]301    }
302
[14746]303    public PushProgram Copy(IManagedPool<PushProgram> pool) {
304      if (IsEmpty) return this;
[14727]305
[14777]306      var program = pool.Get(reset: false);
307
308      program.expressions = expressions;
[14746]309      program.stringRepresentation = stringRepresentation;
310      program.hashCode = hashCode;
311      program.depth = depth;
312      program.treeIndex = treeIndex;
[14745]313
[14746]314      return program;
315    }
316
[14777]317    public IList<Expression> CopyExpressions() {
[14908]318      return new List<Expression>(expressions);
[14746]319    }
320
[14777]321    public IList<Expression> CopyExpressions(IManagedPool<PooledList<Expression>> expressionListPool) {
[14746]322      if (IsEmpty) return EmptyExpressions;
323
[14777]324      var copy = expressionListPool.Get();
[14908]325      copy.AddRange(expressions);
[14746]326
[14727]327      return copy;
328    }
329
330    private int CalcDepth() {
[15017]331      var maxDepth = 0;
[14746]332      for (var i = 0; i < Count; i++) {
[14908]333        var expression = expressions[i];
[14746]334        if (!expression.IsProgram) continue;
[15017]335
[14745]336        var expandExpression = (PushProgram)expression;
337        if (expandExpression.Depth > maxDepth)
338          maxDepth = expandExpression.Depth;
[14727]339      }
340
341      return maxDepth + 1;
342    }
343
[15017]344    private string BuildStringRepresentation() {
[14727]345      // prevent too big string
[15017]346      return Count > STRING_REPRESENTATION_COUNT_BEFORE_AGGREGATE
[14777]347        ? PrefixReduced + Count + PostfixReduced
[15017]348        : Prefix + string.Join(Delimiter, expressions.Reverse()) + Postfix;
[14727]349    }
350
351    private int[] BuildTreeIndex() {
[15017]352      var localTreeIndex = new int[Count];
[14727]353      var next = 1;
354
[14746]355      for (var i = Count - 1; i >= 0; i--) {
[14908]356        var subExpression = expressions[i];
[14727]357
[15017]358        localTreeIndex[i] = next;
[14727]359
[14746]360        if (subExpression.IsProgram) {
[14745]361          next += ((PushProgram)subExpression).TotalCount;
[14727]362        } else {
363          next++;
364        }
365      }
366
[15017]367      return localTreeIndex;
[14727]368    }
369
[14745]370    private static void CheckIfNested(int level, PushProgram target, PushProgram current,
371        IList<PushProgram> visited) {
[14727]372      if (visited.Any(x => ReferenceEquals(x, current)))
373        Debugger.Break();
374
375      visited.Add(current);
376      if (level <= 0) {
[14875]377        // hierarchy depth > level
[14727]378        Debugger.Break();
379        return;
380      }
381
[14746]382      for (var i = 0; i < current.Count; i++) {
383        if (current.expressions[i].IsProgram)
[14727]384          continue;
385
[14746]386        var subExpression = current.expressions[i] as PushProgram;
[14727]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    }
[14745]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
[14746]418      var min = Count - 1;
[14745]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
[14908]430          ? resolver(parent, this, expressions[mid], mid, parentIndex)
431          : (expressions[mid + 1] as PushProgram).GetFromTree(index - TreeIndex[mid + 1], mid + 1,
[14745]432              this, resolver);
433    }
[14727]434  }
435}
Note: See TracBrowser for help on using the repository browser.