Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3136_Structural_GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeBatchInterpreter.cs @ 18133

Last change on this file since 18133 was 18133, checked in by dpiringe, 2 years ago

#3136

  • updated interpeters of type ISymbolicDataAnalysisExpressionTreeInterpreter to support symbols of type SubFunctionSymbol
File size: 10.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
30using HeuristicLab.Parameters;
31using HEAL.Attic;
32
33using static HeuristicLab.Problems.DataAnalysis.Symbolic.BatchOperations;
34
35namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
36  [Item("SymbolicDataAnalysisExpressionTreeBatchInterpreter", "An interpreter that uses batching and vectorization techniques to achieve faster performance.")]
37  [StorableType("BEB15146-BB95-4838-83AC-6838543F017B")]
38  public class SymbolicDataAnalysisExpressionTreeBatchInterpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter {
39    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
40
41    #region parameters
42    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter {
43      get { return (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
44    }
45    #endregion
46
47    #region properties
48    public int EvaluatedSolutions {
49      get { return EvaluatedSolutionsParameter.Value.Value; }
50      set { EvaluatedSolutionsParameter.Value.Value = value; }
51    }
52    #endregion
53
54    public void ClearState() { }
55
56    public SymbolicDataAnalysisExpressionTreeBatchInterpreter() {
57      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
58    }
59
60    [StorableConstructor]
61    protected SymbolicDataAnalysisExpressionTreeBatchInterpreter(StorableConstructorFlag _) : base(_) { }
62    protected SymbolicDataAnalysisExpressionTreeBatchInterpreter(SymbolicDataAnalysisExpressionTreeBatchInterpreter original, Cloner cloner) : base(original, cloner) {
63    }
64    public override IDeepCloneable Clone(Cloner cloner) {
65      return new SymbolicDataAnalysisExpressionTreeBatchInterpreter(this, cloner);
66    }
67
68    private void LoadData(BatchInstruction instr, int[] rows, int rowIndex, int batchSize) {
69      for (int i = 0; i < batchSize; ++i) {
70        var row = rows[rowIndex] + i;
71        instr.buf[i] = instr.weight * instr.data[row];
72      }
73    }
74
75    private void Evaluate(BatchInstruction[] code, int[] rows, int rowIndex, int batchSize) {
76      for (int i = code.Length - 1; i >= 0; --i) {
77        var instr = code[i];
78        var c = instr.childIndex;
79        var n = instr.narg;
80
81        switch (instr.opcode) {
82          case OpCodes.Variable: {
83              LoadData(instr, rows, rowIndex, batchSize);
84              break;
85            }
86          case OpCodes.Constant: break; // nothing to do here, don't remove because we want to prevent falling into the default case here.
87          case OpCodes.Add: {
88              Load(instr.buf, code[c].buf);
89              for (int j = 1; j < n; ++j) {
90                Add(instr.buf, code[c + j].buf);
91              }
92              break;
93            }
94
95          case OpCodes.Sub: {
96              if (n == 1) {
97                Neg(instr.buf, code[c].buf);
98              } else {
99                Load(instr.buf, code[c].buf);
100                for (int j = 1; j < n; ++j) {
101                  Sub(instr.buf, code[c + j].buf);
102                }
103              }
104              break;
105            }
106
107          case OpCodes.Mul: {
108              Load(instr.buf, code[c].buf);
109              for (int j = 1; j < n; ++j) {
110                Mul(instr.buf, code[c + j].buf);
111              }
112              break;
113            }
114
115          case OpCodes.Div: {
116              if (n == 1) {
117                Inv(instr.buf, code[c].buf);
118              } else {
119                Load(instr.buf, code[c].buf);
120                for (int j = 1; j < n; ++j) {
121                  Div(instr.buf, code[c + j].buf);
122                }
123              }
124              break;
125            }
126
127          case OpCodes.Square: {
128              Square(instr.buf, code[c].buf);
129              break;
130            }
131
132          case OpCodes.Root: {
133              Load(instr.buf, code[c].buf);
134              Root(instr.buf, code[c + 1].buf);
135              break;
136            }
137
138          case OpCodes.SquareRoot: {
139              Sqrt(instr.buf, code[c].buf);
140              break;
141            }
142
143          case OpCodes.Cube: {
144              Cube(instr.buf, code[c].buf);
145              break;
146            }
147          case OpCodes.CubeRoot: {
148              CubeRoot(instr.buf, code[c].buf);
149              break;
150            }
151
152          case OpCodes.Power: {
153              Load(instr.buf, code[c].buf);
154              Pow(instr.buf, code[c + 1].buf);
155              break;
156            }
157
158          case OpCodes.Exp: {
159              Exp(instr.buf, code[c].buf);
160              break;
161            }
162
163          case OpCodes.Log: {
164              Log(instr.buf, code[c].buf);
165              break;
166            }
167
168          case OpCodes.Sin: {
169              Sin(instr.buf, code[c].buf);
170              break;
171            }
172
173          case OpCodes.Cos: {
174              Cos(instr.buf, code[c].buf);
175              break;
176            }
177
178          case OpCodes.Tan: {
179              Tan(instr.buf, code[c].buf);
180              break;
181            }
182
183          case OpCodes.Tanh: {
184              Tanh(instr.buf, code[c].buf);
185              break;
186            }
187
188          case OpCodes.Absolute: {
189              Absolute(instr.buf, code[c].buf);
190              break;
191            }
192
193          case OpCodes.AnalyticQuotient: {
194              Load(instr.buf, code[c].buf);
195              AnalyticQuotient(instr.buf, code[c + 1].buf);
196              break;
197            }
198
199          case OpCodes.SubFunction: {
200              Load(instr.buf, code[c].buf);
201              break;
202            }
203          default: throw new NotSupportedException($"This interpreter does not support {(OpCode)instr.opcode}");
204        }
205      }
206    }
207
208    private readonly object syncRoot = new object();
209
210    [ThreadStatic]
211    private static Dictionary<string, double[]> cachedData;
212
213    [ThreadStatic]
214    private static IDataset cachedDataset;
215
216    private void InitCache(IDataset dataset) {
217      cachedDataset = dataset;
218      cachedData = new Dictionary<string, double[]>();
219      foreach (var v in dataset.DoubleVariables) {
220        cachedData[v] = dataset.GetDoubleValues(v).ToArray();
221      }
222    }
223
224    public void InitializeState() {
225      cachedData = null;
226      cachedDataset = null;
227      EvaluatedSolutions = 0;
228    }
229
230    private double[] GetValues(ISymbolicExpressionTree tree, IDataset dataset, int[] rows) {
231      if (cachedData == null || cachedDataset != dataset || cachedDataset is ModifiableDataset) {
232        InitCache(dataset);
233      }
234
235      var code = Compile(tree, dataset, OpCodes.MapSymbolToOpCode);
236      var remainingRows = rows.Length % BATCHSIZE;
237      var roundedTotal = rows.Length - remainingRows;
238
239      var result = new double[rows.Length];
240
241      for (int rowIndex = 0; rowIndex < roundedTotal; rowIndex += BATCHSIZE) {
242        Evaluate(code, rows, rowIndex, BATCHSIZE);
243        Array.Copy(code[0].buf, 0, result, rowIndex, BATCHSIZE);
244      }
245
246      if (remainingRows > 0) {
247        Evaluate(code, rows, roundedTotal, remainingRows);
248        Array.Copy(code[0].buf, 0, result, roundedTotal, remainingRows);
249      }
250
251      // when evaluation took place without any error, we can increment the counter
252      lock (syncRoot) {
253        EvaluatedSolutions++;
254      }
255
256      return result;
257    }
258
259    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, int[] rows) {
260      return GetValues(tree, dataset, rows);
261    }
262
263    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, IEnumerable<int> rows) {
264      return GetSymbolicExpressionTreeValues(tree, dataset, rows.ToArray());
265    }
266
267    private BatchInstruction[] Compile(ISymbolicExpressionTree tree, IDataset dataset, Func<ISymbolicExpressionTreeNode, byte> opCodeMapper) {
268      var root = tree.Root.GetSubtree(0).GetSubtree(0);
269      var code = new BatchInstruction[root.GetLength()];
270      if (root.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)");
271      int c = 1, i = 0;
272      foreach (var node in root.IterateNodesBreadth()) {
273        if (node.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)");
274        code[i] = new BatchInstruction {
275          opcode = opCodeMapper(node),
276          narg = (ushort)node.SubtreeCount,
277          buf = new double[BATCHSIZE],
278          childIndex = c
279        };
280        if (node is VariableTreeNode variable) {
281          code[i].weight = variable.Weight;
282          if (cachedData.ContainsKey(variable.VariableName)) {
283            code[i].data = cachedData[variable.VariableName];
284          } else {
285            code[i].data = dataset.GetReadOnlyDoubleValues(variable.VariableName).ToArray();
286            cachedData[variable.VariableName] = code[i].data;
287          }
288        } else if (node is ConstantTreeNode constant) {
289          code[i].value = constant.Value;
290          for (int j = 0; j < BATCHSIZE; ++j)
291            code[i].buf[j] = code[i].value;
292        }
293        c += node.SubtreeCount;
294        ++i;
295      }
296      return code;
297    }
298  }
299}
Note: See TracBrowser for help on using the repository browser.