[203] | 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 |
|
---|
| 22 | using System;
|
---|
| 23 | using System.Collections.Generic;
|
---|
| 24 | using System.Linq;
|
---|
| 25 | using System.Text;
|
---|
| 26 | using HeuristicLab.Core;
|
---|
| 27 | using HeuristicLab.DataAnalysis;
|
---|
| 28 | using HeuristicLab.Data;
|
---|
[224] | 29 | using System.Xml;
|
---|
[259] | 30 | using System.Globalization;
|
---|
[203] | 31 |
|
---|
| 32 | namespace HeuristicLab.Functions {
|
---|
| 33 | class BakedFunctionTree : ItemBase, IFunctionTree {
|
---|
[220] | 34 | private List<int> code;
|
---|
| 35 | private List<double> data;
|
---|
[260] | 36 | private static EvaluatorSymbolTable symbolTable = EvaluatorSymbolTable.SymbolTable;
|
---|
[224] | 37 | public BakedFunctionTree() {
|
---|
[220] | 38 | code = new List<int>();
|
---|
| 39 | data = new List<double>();
|
---|
[203] | 40 | }
|
---|
| 41 |
|
---|
[206] | 42 | internal BakedFunctionTree(IFunction function)
|
---|
| 43 | : this() {
|
---|
[203] | 44 | code.Add(0);
|
---|
[260] | 45 | code.Add(symbolTable.MapFunction(function));
|
---|
[203] | 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 |
|
---|
[206] | 58 | internal BakedFunctionTree(IFunctionTree tree)
|
---|
| 59 | : this() {
|
---|
[203] | 60 | code.Add(0);
|
---|
[260] | 61 | code.Add(symbolTable.MapFunction(tree.Function));
|
---|
[220] | 62 | code.Add(tree.LocalVariables.Count);
|
---|
[203] | 63 | foreach(IVariable variable in tree.LocalVariables) {
|
---|
| 64 | IItem value = variable.Value;
|
---|
[220] | 65 | data.Add(GetDoubleValue(value));
|
---|
[203] | 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 |
|
---|
[220] | 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;
|
---|
[203] | 90 | for(int i = 0; i < arity; i++) {
|
---|
[220] | 91 | int branchCodeLength;
|
---|
| 92 | int branchDataLength;
|
---|
| 93 | BranchLength(subBranchStart, out branchCodeLength, out branchDataLength);
|
---|
| 94 | subBranchStart += branchCodeLength;
|
---|
| 95 | codeLength += branchCodeLength;
|
---|
| 96 | dataLength += branchDataLength;
|
---|
[203] | 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);
|
---|
[220] | 107 | data.AddRange(subTree.data);
|
---|
[203] | 108 | }
|
---|
| 109 | treesExpanded = false;
|
---|
[206] | 110 | subTrees = null;
|
---|
[203] | 111 | }
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 | private void FlattenVariables() {
|
---|
| 115 | if(variablesExpanded) {
|
---|
| 116 | code[2] = variables.Count;
|
---|
| 117 | foreach(IVariable variable in variables) {
|
---|
[220] | 118 | data.Add(GetDoubleValue(variable.Value));
|
---|
[203] | 119 | }
|
---|
| 120 | variablesExpanded = false;
|
---|
[206] | 121 | variables = null;
|
---|
[203] | 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>();
|
---|
[220] | 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
|
---|
[203] | 135 | for(int i = 0; i < arity; i++) {
|
---|
| 136 | BakedFunctionTree subTree = new BakedFunctionTree();
|
---|
[220] | 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;
|
---|
[203] | 144 | subTrees.Add(subTree);
|
---|
| 145 | }
|
---|
| 146 | treesExpanded = true;
|
---|
[220] | 147 | code.RemoveRange(3, code.Count - 3);
|
---|
[203] | 148 | code[0] = 0;
|
---|
[220] | 149 | data.RemoveRange(nLocalVariables, data.Count - nLocalVariables);
|
---|
[203] | 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>();
|
---|
[260] | 161 | IFunction function = symbolTable.MapSymbol(code[1]);
|
---|
[220] | 162 | int localVariableIndex = 0;
|
---|
[203] | 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) {
|
---|
[220] | 168 | ((ConstrainedDoubleData)value).Data = data[localVariableIndex];
|
---|
[203] | 169 | } else if(value is ConstrainedIntData) {
|
---|
[220] | 170 | ((ConstrainedIntData)value).Data = (int)data[localVariableIndex];
|
---|
[203] | 171 | } else if(value is DoubleData) {
|
---|
[220] | 172 | ((DoubleData)value).Data = data[localVariableIndex];
|
---|
[203] | 173 | } else if(value is IntData) {
|
---|
[220] | 174 | ((IntData)value).Data = (int)data[localVariableIndex];
|
---|
[203] | 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;
|
---|
[220] | 182 | data.RemoveRange(0, variables.Count);
|
---|
[203] | 183 | }
|
---|
| 184 | return variables;
|
---|
| 185 | }
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | public IFunction Function {
|
---|
[260] | 189 | get { return symbolTable.MapSymbol(code[1]); }
|
---|
[203] | 190 | }
|
---|
| 191 |
|
---|
| 192 | public IVariable GetLocalVariable(string name) {
|
---|
[220] | 193 | foreach(IVariable var in LocalVariables) {
|
---|
| 194 | if(var.Name == name) return var;
|
---|
| 195 | }
|
---|
| 196 | return null;
|
---|
[203] | 197 | }
|
---|
| 198 |
|
---|
| 199 | public void AddVariable(IVariable variable) {
|
---|
[220] | 200 | throw new NotSupportedException();
|
---|
[203] | 201 | }
|
---|
| 202 |
|
---|
| 203 | public void RemoveVariable(string name) {
|
---|
[220] | 204 | throw new NotSupportedException();
|
---|
[203] | 205 | }
|
---|
| 206 |
|
---|
| 207 | public void AddSubTree(IFunctionTree tree) {
|
---|
[220] | 208 | if(!treesExpanded) throw new InvalidOperationException();
|
---|
| 209 | subTrees.Add(tree);
|
---|
[203] | 210 | }
|
---|
| 211 |
|
---|
| 212 | public void InsertSubTree(int index, IFunctionTree tree) {
|
---|
[220] | 213 | if(!treesExpanded) throw new InvalidOperationException();
|
---|
| 214 | subTrees.Insert(index, tree);
|
---|
[203] | 215 | }
|
---|
| 216 |
|
---|
| 217 | public void RemoveSubTree(int index) {
|
---|
[220] | 218 | // sanity check
|
---|
| 219 | if(!treesExpanded) throw new InvalidOperationException();
|
---|
| 220 | subTrees.RemoveAt(index);
|
---|
[203] | 221 | }
|
---|
| 222 |
|
---|
[260] | 223 | private BakedTreeEvaluator evaluator = null;
|
---|
[203] | 224 | public double Evaluate(Dataset dataset, int sampleIndex) {
|
---|
| 225 | FlattenVariables();
|
---|
| 226 | FlattenTrees();
|
---|
[260] | 227 | if(evaluator == null) evaluator = new BakedTreeEvaluator(code, data);
|
---|
[239] | 228 | return evaluator.Evaluate(dataset, sampleIndex);
|
---|
[203] | 229 | }
|
---|
| 230 |
|
---|
| 231 |
|
---|
[224] | 232 | public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary<Guid, IStorable> persistedObjects) {
|
---|
[259] | 233 | FlattenVariables();
|
---|
| 234 | FlattenTrees();
|
---|
[224] | 235 | XmlNode node = base.GetXmlNode(name, document, persistedObjects);
|
---|
[259] | 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);
|
---|
[224] | 246 | return node;
|
---|
[203] | 247 | }
|
---|
| 248 |
|
---|
[224] | 249 | public override void Populate(XmlNode node, IDictionary<Guid, IStorable> restoredObjects) {
|
---|
| 250 | base.Populate(node, restoredObjects);
|
---|
[259] | 251 | XmlNode evaluatorNode = node.SelectSingleNode("Evaluator");
|
---|
| 252 | if(evaluatorNode != null) {
|
---|
[260] | 253 | this.evaluator = (BakedTreeEvaluator)PersistenceManager.Restore(evaluatorNode, restoredObjects);
|
---|
[259] | 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));
|
---|
[260] | 257 | treesExpanded = false;
|
---|
| 258 | variablesExpanded = false;
|
---|
[203] | 259 | }
|
---|
| 260 |
|
---|
[259] | 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 |
|
---|
[203] | 280 | public override object Clone(IDictionary<Guid, object> clonedObjects) {
|
---|
| 281 | BakedFunctionTree clone = new BakedFunctionTree();
|
---|
[224] | 282 | // in case the user (de)serialized the tree between evaluation and selection we have to flatten the tree again.
|
---|
[259] | 283 | if(treesExpanded) FlattenTrees();
|
---|
[224] | 284 | if(variablesExpanded) FlattenVariables();
|
---|
[220] | 285 | clone.code.AddRange(code);
|
---|
| 286 | clone.data.AddRange(data);
|
---|
[203] | 287 | return clone;
|
---|
| 288 | }
|
---|
[220] | 289 |
|
---|
| 290 | public override IView CreateView() {
|
---|
| 291 | return new FunctionTreeView(this);
|
---|
| 292 | }
|
---|
[203] | 293 | }
|
---|
| 294 | }
|
---|