Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Collections/sources/HeuristicLab.Functions/ProgrammableFunction.cs @ 755

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

fixed #145

File size: 10.0 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.Text;
25using HeuristicLab.Core;
26using System.Diagnostics;
27using HeuristicLab.Constraints;
28using HeuristicLab.DataAnalysis;
29using System.Xml;
30using System.Reflection;
31using System.CodeDom;
32using System.CodeDom.Compiler;
33using Microsoft.CSharp;
34using System.IO;
35using HeuristicLab.Operators.Programmable;
36using HeuristicLab.Data;
37using System.Collections;
38
39namespace HeuristicLab.Functions {
40  public sealed class ProgrammableFunction : ProgrammableOperator, IFunction {
41    private MethodInfo applyMethod;
42    public ProgrammableFunction() {
43      // clear the variableinfo that was added by the default constructor of ProgrammableOperator
44      RemoveVariableInfo("Result");
45      Code = "return 0.0;";
46      SetDescription("A function that can be programmed for arbitrary needs.");
47      applyMethod = null;
48    }
49
50    public override void Compile() {
51      CodeNamespace ns = new CodeNamespace("HeuristicLab.Functions.CustomFunctions");
52      CodeTypeDeclaration typeDecl = new CodeTypeDeclaration("Function");
53      typeDecl.IsClass = true;
54      typeDecl.TypeAttributes = TypeAttributes.Public;
55
56      CodeMemberMethod method = new CodeMemberMethod();
57      method.Name = "Apply";
58      method.ReturnType = new CodeTypeReference(typeof(double));
59      method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
60      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Dataset), "dataset"));
61      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "index"));
62      foreach(IVariableInfo info in VariableInfos)
63        method.Parameters.Add(new CodeParameterDeclarationExpression(info.DataType, info.FormalName));
64      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(double[]), "args"));
65      string code = Code;
66      method.Statements.Add(new CodeSnippetStatement(code));
67      typeDecl.Members.Add(method);
68
69      ns.Types.Add(typeDecl);
70      ns.Imports.Add(new CodeNamespaceImport("System"));
71      ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
72      ns.Imports.Add(new CodeNamespaceImport("System.Text"));
73      ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Core"));
74      ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Functions"));
75      foreach(IVariableInfo variableInfo in VariableInfos)
76        ns.Imports.Add(new CodeNamespaceImport(variableInfo.DataType.Namespace));
77
78      CodeCompileUnit unit = new CodeCompileUnit();
79      unit.Namespaces.Add(ns);
80      CompilerParameters parameters = new CompilerParameters();
81      parameters.GenerateExecutable = false;
82      parameters.GenerateInMemory = true;
83      parameters.IncludeDebugInformation = false;
84      Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
85      foreach(Assembly loadedAssembly in loadedAssemblies)
86        parameters.ReferencedAssemblies.Add(loadedAssembly.Location);
87      CodeDomProvider provider = new CSharpCodeProvider();
88      CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit);
89
90      applyMethod = null;
91      if(results.Errors.HasErrors) {
92        StringWriter writer = new StringWriter();
93        CodeGeneratorOptions options = new CodeGeneratorOptions();
94        options.BlankLinesBetweenMembers = false;
95        options.ElseOnClosing = true;
96        options.IndentString = "  ";
97        provider.GenerateCodeFromCompileUnit(unit, writer, options);
98        writer.Flush();
99        string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
100        StringBuilder builder = new StringBuilder();
101        for(int i = 0; i < source.Length; i++)
102          builder.AppendLine((i + 1).ToString("###") + "     " + source[i]);
103        builder.AppendLine();
104        builder.AppendLine();
105        builder.AppendLine();
106        foreach(CompilerError error in results.Errors) {
107          builder.Append("Line " + error.Line.ToString());
108          builder.Append(", Column " + error.Column.ToString());
109          builder.AppendLine(": " + error.ErrorText);
110        }
111        throw new Exception("Compile Errors:\n\n" + builder.ToString());
112      } else {
113        Assembly assembly = results.CompiledAssembly;
114        Type[] types = assembly.GetTypes();
115        applyMethod = types[0].GetMethod("Apply");
116      }
117    }
118
119    #region IFunction Members
120    public void Accept(IFunctionVisitor visitor) {
121      visitor.Visit(this);
122    }
123
124    public IFunctionTree GetTreeNode() {
125      return new BakedFunctionTree(this);
126    }
127
128    public double Apply(Dataset dataset, int sampleIndex, double[] args) {
129      // collect parameters
130      ArrayList parameters = new ArrayList(args.Length);
131      parameters.Add(dataset);
132      parameters.Add(sampleIndex);
133      int j = 0;
134      List<double> backupValues = new List<double>();
135      // all local variables are available in the custom function
136      // values of local variables need to be adjusted based on the arguments we recieve from the evaluator
137      // because each instance of programmable-function can have different values for the local variables
138      foreach(IVariableInfo info in VariableInfos) {
139        if(info.Local) {
140          IVariable localVariable = GetVariable(info.FormalName);
141          double curValue = GetDoubleValue(localVariable);
142          backupValues.Add(curValue);
143          SetFromDoubleValue(localVariable, args[j++]);
144          parameters.Add(localVariable.Value);
145        }
146      }
147      // copy the evaluation results of the sub-branches into a separate array (last argument of user-defined function)
148      double[] evaluationResults = new double[args.Length - j];
149      Array.Copy(args, j, evaluationResults, 0, evaluationResults.Length);
150      parameters.Add(evaluationResults);
151      // lazy activation of the user-programmed code
152      if(applyMethod == null) {
153        Compile();
154      }
155      double result = (double)applyMethod.Invoke(null, parameters.ToArray());
156
157      // restore backed up values of variables (just a savety measure. changes of the function-library are unwanted)
158      j = 0;
159      foreach(IVariableInfo info in VariableInfos) {
160        if(info.Local) {
161          SetFromDoubleValue(GetVariable(info.FormalName), backupValues[j]);
162        }
163      }
164      return result;
165    }
166
167    private double GetDoubleValue(IVariable localVariable) {
168      IItem value = localVariable.Value;
169      if(value is ConstrainedDoubleData) {
170        return ((ConstrainedDoubleData)value).Data;
171      } else if(value is ConstrainedIntData) {
172        return (double)((ConstrainedIntData)value).Data;
173      } else if(value is DoubleData) {
174        return ((DoubleData)value).Data;
175      } else if(value is IntData) {
176        return (double)((IntData)value).Data;
177      } else throw new NotSupportedException("Datatype of variable " + localVariable.Name + " is not supported as local variable for programmable-functions.");
178    }
179
180    private void SetFromDoubleValue(IVariable variable, double x) {
181      IItem value = variable.Value;
182      if(value is ConstrainedDoubleData) {
183        ((ConstrainedDoubleData)value).Data = x;
184      } else if(value is ConstrainedIntData) {
185        ((ConstrainedIntData)value).Data = (int)x;
186      } else if(value is DoubleData) {
187        ((DoubleData)value).Data = x;
188      } else if(value is IntData) {
189        ((IntData)value).Data = (int)x;
190      } else throw new NotSupportedException("Datatype of variable " + variable.Name + " is not supported as local variable for programmable-functions.");
191    }
192
193    #endregion
194
195    #region disabled operator functionality
196    // operator-tree style evaluation is not supported for functions.
197    public override IOperation Apply(IScope scope) {
198      throw new NotSupportedException();
199    }
200
201    private static readonly List<IOperator> emptySubOperatorList = new List<IOperator>();
202    public override IList<IOperator> SubOperators {
203      get { return emptySubOperatorList; }
204    }
205
206    public override void AddSubOperator(IOperator subOperator) {
207      throw new NotSupportedException();
208    }
209
210    public override bool TryAddSubOperator(IOperator subOperator) {
211      throw new NotSupportedException();
212    }
213
214    public override bool TryAddSubOperator(IOperator subOperator, int index) {
215      throw new NotSupportedException();
216    }
217
218    public override bool TryAddSubOperator(IOperator subOperator, int index, out ICollection<IConstraint> violatedConstraints) {
219      throw new NotSupportedException();
220    }
221
222    public override bool TryAddSubOperator(IOperator subOperator, out ICollection<IConstraint> violatedConstraints) {
223      throw new NotSupportedException();
224    }
225
226    public override void AddSubOperator(IOperator subOperator, int index) {
227      throw new NotSupportedException();
228    }
229
230    public override void RemoveSubOperator(int index) {
231      throw new NotSupportedException();
232    }
233
234    public override bool TryRemoveSubOperator(int index) {
235      throw new NotSupportedException();
236    }
237
238    public override bool TryRemoveSubOperator(int index, out ICollection<IConstraint> violatedConstraints) {
239      throw new NotSupportedException();
240    }
241    #endregion
242  }
243}
Note: See TracBrowser for help on using the repository browser.