using System; using System.Collections.Generic; using System.Linq; using System.Security.Policy; using HeuristicLab.Common; namespace HeuristicLab.Problems.GrammaticalOptimization { public class PartialExpressionInterpreter { private static readonly double[] emptyErc = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private string sentence; private int syIdx; private HashSet intermediateValues = new HashSet(); private Stack stack = new Stack(); // interprets sentences from L(G(Expr)): // Expr -> Term { ('+' | '-' | '^' ) Term } // Term -> Fact { ('*' | '%') Fact } // Fact -> '!' Expr | '(' Expr ')' | Var | const // Var -> 'a'..'z' // const -> '0' .. '9' // uses protected division symbol % // constants are Koza-style ephemeral random constants (ERC). for now we only allow up to 10 constants. // The constant symbols '0' .. '9' are treated as ERC indices public IEnumerable Interpret(string sentence, double[] vars) { return Interpret(sentence, vars, emptyErc); } public IEnumerable Interpret(string sentence, double[] vars, double[] erc) { InitLex(sentence); intermediateValues.Clear(); stack.Clear(); Expr(vars, erc); return intermediateValues; } private void InitLex(string sentence) { this.sentence = sentence; this.syIdx = 0; } private char CurSy() { if (syIdx >= sentence.Length) return '\0'; return sentence[syIdx]; } private void NewSy() { if (syIdx < sentence.Length) syIdx++; } // helper for xor private double Not(double x) { return DoubleExtensions.IsAlmost(x, 0) ? 1.0 : 0.0; } private bool Expr(double[] d, double[] erc) { if (!Term(d, erc)) return false; var curSy = CurSy(); while (curSy == '+' || curSy == '-' || curSy == '^') { if (curSy == '+') { NewSy(); if (!Term(d, erc)) { return false; } stack.Push(stack.Pop() + stack.Pop()); intermediateValues.Add(stack.Peek()); } else if (curSy == '-') { NewSy(); if (!Term(d, erc)) { return false; } stack.Push(-stack.Pop() + stack.Pop()); intermediateValues.Add(stack.Peek()); } else { NewSy(); if (!Term(d, erc)) { return false; } var e = stack.Pop(); var r = stack.Pop(); stack.Push(Not(r) * e + r * Not(e)); // xor = (!x AND y) OR (x AND !y) intermediateValues.Add(stack.Peek()); } curSy = CurSy(); } return true; } private bool Term(double[] d, double[] erc) { if (!Fact(d, erc)) { return false; } var curSy = CurSy(); while (curSy == '*' || curSy == '%') { if (curSy == '*') { NewSy(); if (!Fact(d, erc)) { return false; } stack.Push(stack.Pop() * stack.Pop()); intermediateValues.Add(stack.Peek()); } else { NewSy(); if (!Fact(d, erc)) { return false; } var nom = stack.Pop(); var r = stack.Pop(); if (HeuristicLab.Common.Extensions.IsAlmost(nom, 0.0)) nom = 1.0; stack.Push(r / nom); intermediateValues.Add(stack.Peek()); } curSy = CurSy(); } return true; } private bool Fact(double[] d, double[] erc) { var curSy = CurSy(); if (curSy == '!') { //NewSy(); //if (!Expr(d, erc)) { stack.Push(-7.0); return false; } //stack.Push(Not(stack.Pop())); } else if (curSy == '(') { //NewSy(); //if (!Expr(d, erc)) { stack.Push(-8.0); return false; } //if (CurSy() != ')') throw new ArgumentException(); //NewSy(); } else if (curSy >= 'a' && curSy <= 'z') { int o = (byte)curSy - (byte)'a'; //int o = Convert.ToByte(CurSy()) - Convert.ToByte('a'); if (o < 0 || o >= d.Length) throw new ArgumentException(); stack.Push(d[o]); intermediateValues.Add(stack.Peek()); NewSy(); } else if (curSy == '/') { // /-symbol is used in the expressionextender to represent inverse (1/x). // this is necessary because we also use symbols 0..9 as indices for ERCs //NewSy(); //if (!Fact(d, erc)) { stack.Push(-9.0); return false; } //stack.Push(1.0 / stack.Pop()); } else if (curSy >= '0' && curSy <= '9') { int o = (byte)curSy - (byte)'0'; //int o = Convert.ToByte(CurSy()) - Convert.ToByte('a'); if (o < 0 || o >= 10) throw new ArgumentException(); stack.Push(erc[o]); intermediateValues.Add(stack.Peek()); NewSy(); } else { return false; } return true; } } }