[14745] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.Linq;
|
---|
[15771] | 4 | using System.Diagnostics;
|
---|
| 5 | using System.Diagnostics.CodeAnalysis;
|
---|
[14727] | 6 |
|
---|
[15771] | 7 | using HeuristicLab.Common;
|
---|
| 8 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
[15334] | 9 |
|
---|
[15771] | 10 | namespace 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 | }
|
---|