Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Functions/BakedFunctionTree.cs @ 260

Last change on this file since 260 was 260, checked in by gkronber, 16 years ago

fixed #146 (serialization of function-trees in linear form)

File size: 10.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 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;
25using System.Text;
26using HeuristicLab.Core;
27using HeuristicLab.DataAnalysis;
28using HeuristicLab.Data;
29using System.Xml;
30using System.Globalization;
31
32namespace HeuristicLab.Functions {
33  class BakedFunctionTree : ItemBase, IFunctionTree {
34    private List<int> code;
35    private List<double> data;
36    private static EvaluatorSymbolTable symbolTable = EvaluatorSymbolTable.SymbolTable;
37    public BakedFunctionTree() {
38      code = new List<int>();
39      data = new List<double>();
40    }
41
42    internal BakedFunctionTree(IFunction function)
43      : this() {
44      code.Add(0);
45      code.Add(symbolTable.MapFunction(function));
46      code.Add(0);
47      treesExpanded = true;
48      subTrees = new List<IFunctionTree>();
49      variables = new List<IVariable>();
50      variablesExpanded = true;
51      foreach(IVariableInfo variableInfo in function.VariableInfos) {
52        if(variableInfo.Local) {
53          variables.Add((IVariable)function.GetVariable(variableInfo.FormalName).Clone());
54        }
55      }
56    }
57
58    internal BakedFunctionTree(IFunctionTree tree)
59      : this() {
60      code.Add(0);
61      code.Add(symbolTable.MapFunction(tree.Function));
62      code.Add(tree.LocalVariables.Count);
63      foreach(IVariable variable in tree.LocalVariables) {
64        IItem value = variable.Value;
65        data.Add(GetDoubleValue(value));
66      }
67      foreach(IFunctionTree subTree in tree.SubTrees) {
68        AddSubTree(new BakedFunctionTree(subTree));
69      }
70    }
71
72    private double GetDoubleValue(IItem value) {
73      if(value is DoubleData) {
74        return ((DoubleData)value).Data;
75      } else if(value is ConstrainedDoubleData) {
76        return ((ConstrainedDoubleData)value).Data;
77      } else if(value is IntData) {
78        return ((IntData)value).Data;
79      } else if(value is ConstrainedIntData) {
80        return ((ConstrainedIntData)value).Data;
81      } else throw new NotSupportedException("Invalid datatype of local variable for GP");
82    }
83
84    private void BranchLength(int branchRoot, out int codeLength, out int dataLength) {
85      int arity = code[branchRoot];
86      int nLocalVariables = code[branchRoot + 2];
87      codeLength = 3;
88      dataLength = nLocalVariables;
89      int subBranchStart = branchRoot + codeLength;
90      for(int i = 0; i < arity; i++) {
91        int branchCodeLength;
92        int branchDataLength;
93        BranchLength(subBranchStart, out branchCodeLength, out branchDataLength);
94        subBranchStart += branchCodeLength;
95        codeLength += branchCodeLength;
96        dataLength += branchDataLength;
97      }
98    }
99
100    private void FlattenTrees() {
101      if(treesExpanded) {
102        code[0] = subTrees.Count;
103        foreach(BakedFunctionTree subTree in subTrees) {
104          subTree.FlattenVariables();
105          subTree.FlattenTrees();
106          code.AddRange(subTree.code);
107          data.AddRange(subTree.data);
108        }
109        treesExpanded = false;
110        subTrees = null;
111      }
112    }
113
114    private void FlattenVariables() {
115      if(variablesExpanded) {
116        code[2] = variables.Count;
117        foreach(IVariable variable in variables) {
118          data.Add(GetDoubleValue(variable.Value));
119        }
120        variablesExpanded = false;
121        variables = null;
122      }
123    }
124
125    private bool treesExpanded = false;
126    private List<IFunctionTree> subTrees;
127    public IList<IFunctionTree> SubTrees {
128      get {
129        if(!treesExpanded) {
130          subTrees = new List<IFunctionTree>();
131          int arity = code[0];
132          int nLocalVariables = code[2];
133          int branchIndex = 3;
134          int dataIndex = nLocalVariables; // skip my local variables to reach the local variables of the first branch
135          for(int i = 0; i < arity; i++) {
136            BakedFunctionTree subTree = new BakedFunctionTree();
137            int codeLength;
138            int dataLength;
139            BranchLength(branchIndex, out codeLength, out dataLength);
140            subTree.code = code.GetRange(branchIndex, codeLength);
141            subTree.data = data.GetRange(dataIndex, dataLength);
142            branchIndex += codeLength;
143            dataIndex += dataLength;
144            subTrees.Add(subTree);
145          }
146          treesExpanded = true;
147          code.RemoveRange(3, code.Count - 3);
148          code[0] = 0;
149          data.RemoveRange(nLocalVariables, data.Count - nLocalVariables);
150        }
151        return subTrees;
152      }
153    }
154
155    private bool variablesExpanded = false;
156    private List<IVariable> variables;
157    public ICollection<IVariable> LocalVariables {
158      get {
159        if(!variablesExpanded) {
160          variables = new List<IVariable>();
161          IFunction function = symbolTable.MapSymbol(code[1]);
162          int localVariableIndex = 0;
163          foreach(IVariableInfo variableInfo in function.VariableInfos) {
164            if(variableInfo.Local) {
165              IVariable clone = (IVariable)function.GetVariable(variableInfo.FormalName).Clone();
166              IItem value = clone.Value;
167              if(value is ConstrainedDoubleData) {
168                ((ConstrainedDoubleData)value).Data = data[localVariableIndex];
169              } else if(value is ConstrainedIntData) {
170                ((ConstrainedIntData)value).Data = (int)data[localVariableIndex];
171              } else if(value is DoubleData) {
172                ((DoubleData)value).Data = data[localVariableIndex];
173              } else if(value is IntData) {
174                ((IntData)value).Data = (int)data[localVariableIndex];
175              } else throw new NotSupportedException("Invalid local variable type for GP.");
176              variables.Add(clone);
177              localVariableIndex++;
178            }
179          }
180          variablesExpanded = true;
181          code[2] = 0;
182          data.RemoveRange(0, variables.Count);
183        }
184        return variables;
185      }
186    }
187
188    public IFunction Function {
189      get { return symbolTable.MapSymbol(code[1]); }
190    }
191
192    public IVariable GetLocalVariable(string name) {
193      foreach(IVariable var in LocalVariables) {
194        if(var.Name == name) return var;
195      }
196      return null;
197    }
198
199    public void AddVariable(IVariable variable) {
200      throw new NotSupportedException();
201    }
202
203    public void RemoveVariable(string name) {
204      throw new NotSupportedException();
205    }
206
207    public void AddSubTree(IFunctionTree tree) {
208      if(!treesExpanded) throw new InvalidOperationException();
209      subTrees.Add(tree);
210    }
211
212    public void InsertSubTree(int index, IFunctionTree tree) {
213      if(!treesExpanded) throw new InvalidOperationException();
214      subTrees.Insert(index, tree);
215    }
216
217    public void RemoveSubTree(int index) {
218      // sanity check
219      if(!treesExpanded) throw new InvalidOperationException();
220      subTrees.RemoveAt(index);
221    }
222
223    private BakedTreeEvaluator evaluator = null;
224    public double Evaluate(Dataset dataset, int sampleIndex) {
225      FlattenVariables();
226      FlattenTrees();
227      if(evaluator == null) evaluator = new BakedTreeEvaluator(code, data);
228      return evaluator.Evaluate(dataset, sampleIndex);
229    }
230
231
232    public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary<Guid, IStorable> persistedObjects) {
233      FlattenVariables();
234      FlattenTrees();
235      XmlNode node = base.GetXmlNode(name, document, persistedObjects);
236      if(evaluator != null) {
237        XmlNode evaluatorNode = PersistenceManager.Persist("Evaluator", evaluator, document, persistedObjects);
238        node.AppendChild(evaluatorNode);
239      }
240      XmlAttribute codeAttribute = document.CreateAttribute("Code");
241      codeAttribute.Value = GetString<int>(code);
242      node.Attributes.Append(codeAttribute);
243      XmlAttribute dataAttribute = document.CreateAttribute("Data");
244      dataAttribute.Value = GetString<double>(data);
245      node.Attributes.Append(dataAttribute);
246      return node;
247    }
248
249    public override void Populate(XmlNode node, IDictionary<Guid, IStorable> restoredObjects) {
250      base.Populate(node, restoredObjects);
251      XmlNode evaluatorNode = node.SelectSingleNode("Evaluator");
252      if(evaluatorNode != null) {
253        this.evaluator = (BakedTreeEvaluator)PersistenceManager.Restore(evaluatorNode, restoredObjects);
254      }
255      code = GetList<int>(node.Attributes["Code"].Value, s => int.Parse(s, CultureInfo.InvariantCulture));
256      data = GetList<double>(node.Attributes["Data"].Value, s => double.Parse(s, CultureInfo.InvariantCulture));
257      treesExpanded = false;
258      variablesExpanded = false;
259    }
260
261    private string GetString<T>(IEnumerable<T> xs) where T : IConvertible {
262      StringBuilder builder = new StringBuilder();
263      foreach(T x in xs) {
264        builder.Append(x.ToString(CultureInfo.InvariantCulture) + "; ");
265      }
266      if(builder.Length > 0) builder.Remove(builder.Length - 2, 2);
267      return builder.ToString();
268    }
269
270    private List<T> GetList<T>(string s, Converter<string, T> converter) {
271      List<T> result = new List<T>();
272      string[] tokens = s.Split(new char[] {';',' '}, StringSplitOptions.RemoveEmptyEntries);
273      foreach(string token in tokens) {
274        T x = converter(token.Trim());
275        result.Add(x);
276      }
277      return result;
278    }
279
280    public override object Clone(IDictionary<Guid, object> clonedObjects) {
281      BakedFunctionTree clone = new BakedFunctionTree();
282      // in case the user (de)serialized the tree between evaluation and selection we have to flatten the tree again.
283      if(treesExpanded) FlattenTrees();
284      if(variablesExpanded) FlattenVariables();
285      clone.code.AddRange(code);
286      clone.data.AddRange(data);
287      return clone;
288    }
289
290    public override IView CreateView() {
291      return new FunctionTreeView(this);
292    }
293  }
294}
Note: See TracBrowser for help on using the repository browser.