#region License Information /* HeuristicLab * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Text; using HeuristicLab.Core; using System.Diagnostics; using HeuristicLab.Constraints; using HeuristicLab.DataAnalysis; using System.Xml; using System.Reflection; using System.CodeDom; using System.CodeDom.Compiler; using Microsoft.CSharp; using System.IO; using HeuristicLab.Operators.Programmable; namespace HeuristicLab.Functions { public class ProgrammableFunction : ProgrammableOperator, IFunction { private MethodInfo applyMethod; public ProgrammableFunction() : base() { Code = "return 0.0;"; SetDescription("A function that can be programmed for arbitrary needs."); applyMethod = null; } public override void Compile() { CodeNamespace ns = new CodeNamespace("HeuristicLab.Functions.CustomFunctions"); CodeTypeDeclaration typeDecl = new CodeTypeDeclaration("Function"); typeDecl.IsClass = true; typeDecl.TypeAttributes = TypeAttributes.Public; CodeMemberMethod method = new CodeMemberMethod(); method.Name = "Apply"; method.ReturnType = new CodeTypeReference(typeof(double)); method.Attributes = MemberAttributes.Public | MemberAttributes.Static; method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Dataset), "dataset")); method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "index")); foreach(IVariableInfo info in VariableInfos) method.Parameters.Add(new CodeParameterDeclarationExpression(info.DataType, info.FormalName)); method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(double[]), "args")); string code = Code; method.Statements.Add(new CodeSnippetStatement(code)); typeDecl.Members.Add(method); ns.Types.Add(typeDecl); ns.Imports.Add(new CodeNamespaceImport("System")); ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic")); ns.Imports.Add(new CodeNamespaceImport("System.Text")); ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Core")); ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Functions")); foreach(IVariableInfo variableInfo in VariableInfos) ns.Imports.Add(new CodeNamespaceImport(variableInfo.DataType.Namespace)); CodeCompileUnit unit = new CodeCompileUnit(); unit.Namespaces.Add(ns); CompilerParameters parameters = new CompilerParameters(); parameters.GenerateExecutable = false; parameters.GenerateInMemory = true; parameters.IncludeDebugInformation = false; Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach(Assembly loadedAssembly in loadedAssemblies) parameters.ReferencedAssemblies.Add(loadedAssembly.Location); CodeDomProvider provider = new CSharpCodeProvider(); CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit); applyMethod = null; if(results.Errors.HasErrors) { StringWriter writer = new StringWriter(); CodeGeneratorOptions options = new CodeGeneratorOptions(); options.BlankLinesBetweenMembers = false; options.ElseOnClosing = true; options.IndentString = " "; provider.GenerateCodeFromCompileUnit(unit, writer, options); writer.Flush(); string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None); StringBuilder builder = new StringBuilder(); for(int i = 0; i < source.Length; i++) builder.AppendLine((i + 1).ToString("###") + " " + source[i]); builder.AppendLine(); builder.AppendLine(); builder.AppendLine(); foreach(CompilerError error in results.Errors) { builder.Append("Line " + error.Line.ToString()); builder.Append(", Column " + error.Column.ToString()); builder.AppendLine(": " + error.ErrorText); } throw new Exception("Compile Errors:\n\n" + builder.ToString()); } else { Assembly assembly = results.CompiledAssembly; Type[] types = assembly.GetTypes(); applyMethod = types[0].GetMethod("Apply"); } } #region IFunction Members public void Accept(IFunctionVisitor visitor) { visitor.Visit(this); } public double Evaluate(Dataset dataset, int sampleIndex, IFunctionTree tree) { // evaluate sub-trees double[] evaluationResults = new double[tree.SubTrees.Count]; for(int subTree=0; subTree < tree.SubTrees.Count; subTree++) { evaluationResults[subTree] = tree.SubTrees[subTree].Evaluate(dataset, sampleIndex); } // lazy activation of the user-programmed code if(applyMethod == null) { Compile(); } // collect parameters object[] parameters = new object[VariableInfos.Count + 3]; parameters[0] = dataset; parameters[1] = sampleIndex; int i = 2; // all local variables are available in the custom function foreach(IVariable variable in tree.LocalVariables) { parameters[i] = variable; i++; } parameters[i] = evaluationResults; return (double)applyMethod.Invoke(null, parameters); } // application of programmable-function is not possible public double Apply(Dataset dataset, int sampleIndex, double[] args) { throw new NotSupportedException(); } #endregion #region disabled operator functionality // operator-tree style evaluation is not supported for functions. public override IOperation Apply(IScope scope) { throw new NotSupportedException(); } private static readonly List emptySubOperatorList = new List(); public override IList SubOperators { get { return emptySubOperatorList; } } public override void AddSubOperator(IOperator subOperator) { throw new NotSupportedException(); } public override bool TryAddSubOperator(IOperator subOperator) { throw new NotSupportedException(); } public override bool TryAddSubOperator(IOperator subOperator, int index) { throw new NotSupportedException(); } public override bool TryAddSubOperator(IOperator subOperator, int index, out ICollection violatedConstraints) { throw new NotSupportedException(); } public override bool TryAddSubOperator(IOperator subOperator, out ICollection violatedConstraints) { throw new NotSupportedException(); } public override void AddSubOperator(IOperator subOperator, int index) { throw new NotSupportedException(); } public override void RemoveSubOperator(int index) { throw new NotSupportedException(); } public override bool TryRemoveSubOperator(int index) { throw new NotSupportedException(); } public override bool TryRemoveSubOperator(int index, out ICollection violatedConstraints) { throw new NotSupportedException(); } #endregion } }