Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs @ 16749

Last change on this file since 16749 was 15606, checked in by gkronber, 7 years ago

#2796: comments and typos

File size: 9.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21using System;
22using System.Collections.Generic;
23using System.Diagnostics.Contracts;
24using System.Linq;
25
26// code for hashing of expressions based on symbolic analysis of monomials and polynomials.
27// slow, but easy to implement correctly.
28namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
29  internal enum UnaryFunctionType { Log, Exp, Inv };
30  internal interface Factor { }
31  internal class SymbolFactor : Factor {
32    internal char symbolId;
33    public SymbolFactor(char symbolId) {
34      this.symbolId = symbolId;
35    }
36    public override int GetHashCode() {
37      return symbolId.GetHashCode();
38    }
39    public override bool Equals(object obj) {
40      SymbolFactor other = obj as SymbolFactor;
41      if (other == null) return false;
42      else return other.symbolId == this.symbolId;
43    }
44  }
45    internal class FunctionFactor : Factor {
46    internal UnaryFunctionType functionType;
47    internal Polynomial argument;
48
49    public FunctionFactor(UnaryFunctionType functionType, Polynomial argument) {
50      this.functionType = functionType;
51      this.argument = argument;
52    }
53
54    public override int GetHashCode() {
55      var h = functionType.GetHashCode();
56      return ((h<<5)+h) ^ argument.GetHashCode();
57    }
58    public override bool Equals(object obj) {
59      FunctionFactor other = obj as FunctionFactor;
60      if (other == null) return false;
61      return
62        other.functionType == this.functionType &&
63        other.argument == this.argument;
64    }
65  }
66
67  internal class Monomial {
68    internal List<Factor> factors = new List<Factor>();
69    public Monomial(params Factor[] factor) {
70      foreach (var f in factor) factors.Add(f);
71    }
72    public override int GetHashCode() {
73      return factors.OrderBy(ti => ti.GetHashCode()).Aggregate(0, (a, ti) => ((a  << 5) + a) ^ ti.GetHashCode());
74    }
75    public override bool Equals(object obj) {
76      Monomial other = obj as Monomial;
77      if (other == null) return false;
78      if (other.factors.Count != this.factors.Count) return false;
79      return factors.All(ti => other.factors.Contains(ti)) &&
80        other.factors.All(ti => factors.Contains(ti));
81    }
82
83    public static Monomial Product(Monomial a, Monomial b) {
84      var p = new Monomial();
85      var invFactorArg = new Polynomial();
86      var expFactorArg = new Polynomial();
87      var expFactor = new FunctionFactor(UnaryFunctionType.Exp, expFactorArg);
88
89      foreach (var aFactor in a.factors.Concat(b.factors)) {
90        // collect all exp and inv factors into one and simplify
91        var funcFactor = aFactor as FunctionFactor;
92        if (funcFactor != null && funcFactor.functionType == UnaryFunctionType.Exp) {
93          expFactorArg.Add(funcFactor.argument);
94        } else if (funcFactor != null && funcFactor.functionType == UnaryFunctionType.Inv) {
95          if (!invFactorArg.terms.Any()) invFactorArg.terms = new HashSet<Monomial>(funcFactor.argument.terms);
96          else invFactorArg.Mul(funcFactor.argument);
97        } else {
98          p.factors.Add(aFactor);
99        }
100      }
101      if (expFactorArg.terms.Any()) {
102        p.factors.Add(expFactor);
103      }
104      if (invFactorArg.terms.Any()) {
105        var invFactor = new FunctionFactor(UnaryFunctionType.Inv, invFactorArg);
106        p.factors.Add(invFactor);
107      }
108      return p;
109    }
110  }
111
112  internal class Polynomial {
113    internal HashSet<Monomial> terms = new HashSet<Monomial>();
114    public Polynomial(params Monomial[] term) {
115      foreach (var t in term) terms.Add(t);
116    }
117    public void Add(Polynomial other) {
118      this.terms.UnionWith(other.terms);
119    }
120    public void Mul(Polynomial other) {
121      var myTerms = terms;
122      var otherTerms = other.terms;
123      var newTerms = new HashSet<Monomial>();
124      foreach (var a in myTerms) {
125        foreach (var b in otherTerms) {
126          newTerms.Add(Monomial.Product(a, b));
127        }
128      }
129      terms = newTerms;
130    }
131    public override int GetHashCode() {
132      return terms.OrderBy(ti => ti.GetHashCode()).Aggregate(0, (a, ti) => ((a<<5)+a) ^ ti.GetHashCode());
133    }
134    public override bool Equals(object obj) {
135      Polynomial other = obj as Polynomial;
136      if (other == null) return false;
137      if (other.terms.Count != this.terms.Count) return false;
138      return terms.All(ti => other.terms.Contains(ti)) &&
139        other.terms.All(ti => terms.Contains(ti));
140    }
141  }
142
143
144  // calculates a hash-code for expressions.
145  public static class ExprHashSymbolic {
146
147
148    const int MaxStackSize = 100;
149    const int MaxVariables = 26;
150    private static SymbolFactor[] varSymbols;
151    private static SymbolFactor zero;
152    private static SymbolFactor one;
153
154
155    static ExprHashSymbolic() {
156      const string symbols = "abcdefghijklmnopqrstuvwxyz"; // limited to 26 variables -> TODO
157
158      varSymbols = new SymbolFactor[MaxVariables];
159      for (int i = 0; i < MaxVariables; i++) {
160        varSymbols[i] = new SymbolFactor(symbols[i]);
161      }
162      zero = new SymbolFactor('0');
163      one = new SymbolFactor('1');
164    }
165
166    public static int GetHash(byte[] code, int nParams) {
167      return Eval(code, nParams);
168    }
169
170 
171  // takes an array of code and transforms it into a polynomial for hashing.
172  // slow!
173    private static int Eval(byte[] code, int nParams) {
174      // The hash code calculation already preserves commutativity, associativity and distributivity of operations.
175      // We assume that the structure contains numeric parameters
176      // x = c*x
177      // exp(x) = c*exp(c*x)
178      // log(x) = c*log(x+c)
179      // inv(x) = c/(x+c)
180      // Accordingly, each structure represents a class of functions.
181
182      // The following expressions should hash to the same value as they represent
183      // equivalent function classes
184      // - x1 + x1 is equivalent to x1
185      // - exp(x1) * exp(x1) is equivalent to exp(x1)
186      //
187      // The following expressions must not hash to the same value.
188      // - exp(x1) + exp(x1) is different from exp(x1), because c1*exp(c2*x1) + c3*exp(c4*x1) cannot be simplified to c5*exp(c6*x1) for all values of c2 and c4
189      // - log(x1) + log(x1) is different from log(x1), same as above
190      // - 1/x1 + 1/x1 is different from 1/x1, same as above 1/(x1 + c1) + 1/(x1 + c2)
191      // - TODO: list further exceptions
192   
193
194      var stack = new Polynomial[MaxStackSize];
195      int topOfStack = -1;
196      int pc = 0;
197      int nextParamIdx = -1;
198      OpCodes op;
199      short arg;
200      while (true) {
201        ReadNext(code, ref pc, out op, out arg);
202        switch (op) {
203          case OpCodes.Nop: break;
204          case OpCodes.LoadConst0: {
205              ++topOfStack;
206              stack[topOfStack] = new Polynomial(new Monomial(zero));
207              break;
208            }
209          case OpCodes.LoadConst1: {
210              ++topOfStack;
211              stack[topOfStack] = new Polynomial(new Monomial(one));
212              break;
213            }
214          case OpCodes.LoadParamN: {
215              ++topOfStack;
216              stack[topOfStack] = new Polynomial(new Monomial(one)); // TODO empty, or unique?
217              break;
218            }
219          case OpCodes.LoadVar: {
220              ++topOfStack;
221              stack[topOfStack] = new Polynomial(new Monomial(varSymbols[arg]));
222
223              break;
224            }
225          case OpCodes.Add: {
226              stack[topOfStack - 1].Add(stack[topOfStack]);
227              topOfStack--;
228              break;
229            }
230          case OpCodes.Mul: {
231              stack[topOfStack - 1].Mul(stack[topOfStack]);
232              topOfStack--;
233              break;
234            }
235          case OpCodes.Log: {
236              var v1 = stack[topOfStack];
237              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Log, v1)));
238              break;
239            }
240          case OpCodes.Exp: {
241              var v1 = stack[topOfStack];
242              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Exp, v1)));
243              break;
244            }
245          case OpCodes.Inv: {
246              var v1 = stack[topOfStack];
247              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Inv, v1)));
248              break;
249            }
250          case OpCodes.Exit:
251            Contract.Assert(topOfStack == 0);
252            return stack[topOfStack].GetHashCode();
253          default: throw new InvalidOperationException();
254        }
255      }
256    }
257
258    private static void EvalTerms(HashSet<double> terms, double[] stack, ref int topOfStack) {
259      ++topOfStack;
260      stack[topOfStack] = terms.Sum();
261      terms.Clear();
262    }
263
264    private static void ReadNext(byte[] code, ref int pc, out OpCodes op, out short s) {
265      op = (OpCodes)Enum.ToObject(typeof(OpCodes), code[pc++]);
266      s = 0;
267      if (op == OpCodes.LoadVar) {
268        s = (short)((code[pc] << 8) | code[pc + 1]);
269        pc += 2;
270      }
271    }
272  }
273}
Note: See TracBrowser for help on using the repository browser.