#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.Linq; using System.Text; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.GP.Interfaces; using HeuristicLab.Modeling; using HeuristicLab.DataAnalysis; using System.Diagnostics; using HeuristicLab.Common; namespace HeuristicLab.GP.StructureIdentification.Networks { public class NetworkToFunctionTransformer : OperatorBase { public NetworkToFunctionTransformer() : base() { AddVariableInfo(new VariableInfo("Network", "The network (open expression)", typeof(IGeneticProgrammingModel), VariableKind.In)); AddVariableInfo(new VariableInfo("TargetVariables", "Name of the target variables", typeof(ItemList), VariableKind.In)); AddVariableInfo(new VariableInfo("FunctionTree", "The function tree with all targetvaribales", typeof(IGeneticProgrammingModel), VariableKind.New)); } public override string Description { get { return "Extracts the network (function tree with unbound parameters) and creates a closed form function tree for each target variable."; } } public override IOperation Apply(IScope scope) { IGeneticProgrammingModel model = GetVariableValue("Network", scope, true); ItemList targetVariables = GetVariableValue>("TargetVariables", scope, true); // clear old sub-scopes while (scope.SubScopes.Count > 0) scope.RemoveSubScope(scope.SubScopes[0]); var targetVariableEnumerator = targetVariables.Select(x => x.Data).GetEnumerator(); List transformedTrees = new List(Transform(model.FunctionTree, targetVariables.Select(x => x.Data))); // create a new sub-scope for each target variable with the transformed expression foreach (IFunctionTree transformedTree in transformedTrees) { targetVariableEnumerator.MoveNext(); Scope exprScope = new Scope(); scope.AddSubScope(exprScope); exprScope.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), new GeneticProgrammingModel(transformedTree))); exprScope.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TargetVariable"), new StringData(targetVariableEnumerator.Current))); Debug.Assert(!(transformedTree is VariableFunctionTree) || ((VariableFunctionTree)transformedTree).VariableName != targetVariableEnumerator.Current); } return null; } private static IEnumerable Transform(IFunctionTree networkDescription, IEnumerable targetVariables) { // bind open parameters of network to target variables //IFunctionTree openExpression = RemoveOpenParameters(networkDescription); IFunctionTree paritallyEvaluatedOpenExpression = ApplyMetaFunctions((IFunctionTree)networkDescription.Clone()); IFunctionTree boundExpression = BindVariables(paritallyEvaluatedOpenExpression, targetVariables.GetEnumerator()); // create a new sub-scope for each target variable with the transformed expression foreach (var targetVariable in targetVariables) { yield return TransformExpression((IFunctionTree)boundExpression.Clone(), targetVariable, targetVariables.Except(new string[] { targetVariable })); } } /// /// applies all tree-transforming meta functions (= cycle and flip) /// precondition: root is a F2 function (possibly cycle) and the tree contains 0 or n flip functions, each branch has an openparameter symbol in the bottom left /// postconditon: root is any F2 function (but cycle) and the tree doesn't contains any flips, each branch has an openparameter symbol in the bottom left /// /// /// private static IFunctionTree ApplyMetaFunctions(IFunctionTree tree) { return ApplyFlips(ApplyCycles(tree)); } private static IFunctionTree ApplyFlips(IFunctionTree tree) { if (tree.SubTrees.Count == 0) { return tree; } else if (tree.Function is Flip) { var partiallyAppliedBranch = ApplyFlips(tree.SubTrees[0]); if (partiallyAppliedBranch.Function is OpenParameter) { var openParFunTree = (OpenParameterFunctionTree)partiallyAppliedBranch; openParFunTree.Weight = 1.0 / openParFunTree.Weight; return partiallyAppliedBranch; } else return InvertChain(partiallyAppliedBranch); } else { List subTrees = new List(tree.SubTrees); while (tree.SubTrees.Count > 0) tree.RemoveSubTree(0); foreach (var subTree in subTrees) { tree.AddSubTree(ApplyFlips(subTree)); } return tree; } } /// /// inverts and reverses chain of functions. /// precondition: tree is any F1 non-terminal that ends with an openParameter /// postcondition: tree is inverted and reversed chain of F1 non-terminals and ends with an openparameter. /// /// /// private static IFunctionTree InvertChain(IFunctionTree tree) { List currentChain = new List(IterateChain(tree)); // get a list of function trees from bottom to top List reversedChain = new List(currentChain.Reverse().Skip(1)); OpenParameterFunctionTree openParam = (OpenParameterFunctionTree)currentChain.Last(); // build new tree by inverting every function in the reversed chain and keeping f0 branches untouched. IFunctionTree parent = reversedChain[0]; IFunctionTree invParent = GetInvertedFunction(parent.Function).GetTreeNode(); for (int j = 1; j < parent.SubTrees.Count; j++) { invParent.AddSubTree(parent.SubTrees[j]); } IFunctionTree root = invParent; for (int i = 1; i < reversedChain.Count(); i++) { IFunctionTree child = reversedChain[i]; IFunctionTree invChild = GetInvertedFunction(child.Function).GetTreeNode(); invParent.InsertSubTree(0, invChild); parent = child; invParent = invChild; for (int j = 1; j < parent.SubTrees.Count; j++) { invParent.AddSubTree(parent.SubTrees[j]); } } // invert factor of openParam openParam.Weight = 1.0 / openParam.Weight; // append open param at the end invParent.InsertSubTree(0, openParam); return root; } private static IEnumerable IterateChain(IFunctionTree tree) { while (tree.SubTrees.Count > 0) { yield return tree; tree = tree.SubTrees[0]; } yield return tree; } private static Dictionary invertedFunction = new Dictionary() { { typeof(AdditionF1), new SubtractionF1() }, { typeof(SubtractionF1), new AdditionF1() }, { typeof(MultiplicationF1), new DivisionF1() }, { typeof(DivisionF1), new MultiplicationF1() }, { typeof(OpenLog), new OpenExp() }, { typeof(OpenExp), new OpenLog() }, //{ typeof(OpenSqr), new OpenSqrt() }, //{ typeof(OpenSqrt), new OpenSqr() }, { typeof(Flip), new Flip()}, { typeof(Addition), new Subtraction()}, { typeof(Subtraction), new Addition()}, { typeof(Multiplication), new Division()}, { typeof(Division), new Multiplication()}, { typeof(Exponential), new Logarithm()}, { typeof(Logarithm), new Exponential()} }; private static IFunction GetInvertedFunction(IFunction function) { return invertedFunction[function.GetType()]; } private static IFunctionTree ApplyCycles(IFunctionTree tree) { int nRotations = 0; while (tree.Function is Cycle) { nRotations++; tree = tree.SubTrees[0]; } if (nRotations > 0 && nRotations % tree.SubTrees.Count > 0) { IFunctionTree[] subTrees = tree.SubTrees.ToArray(); while (tree.SubTrees.Count > 0) tree.RemoveSubTree(0); nRotations = nRotations % subTrees.Length; Array.Reverse(subTrees, 0, nRotations); Array.Reverse(subTrees, nRotations, subTrees.Length - nRotations); Array.Reverse(subTrees, 0, subTrees.Length); for (int i = 0; i < subTrees.Length; i++) { tree.AddSubTree(subTrees[i]); } } return tree; } //private static IFunctionTree AppendLeft(IFunctionTree tree, IFunctionTree node) { // IFunctionTree originalTree = tree; // while (!IsBottomLeft(tree)) tree = tree.SubTrees[0]; // tree.InsertSubTree(0, node); // return originalTree; //} private static bool IsBottomLeft(IFunctionTree tree) { if (tree.SubTrees.Count == 0) return true; else if (tree.SubTrees[0].Function is Variable) return true; else if (tree.SubTrees[0].Function is Constant) return true; else return false; } /// /// recieves a function tree transforms it into a function-tree for the given target variable /// /// /// /// private static IFunctionTree TransformExpression(IFunctionTree tree, string targetVariable, IEnumerable parameters) { if (tree.Function is Constant) return tree; if (tree.Function is Variable || tree.Function is Differential) { VariableFunctionTree varTree = (VariableFunctionTree)tree; varTree.Weight = 1.0; if (varTree.VariableName == targetVariable) varTree.VariableName = parameters.First(); return varTree; } if (tree.Function is Addition || tree.Function is Subtraction || tree.Function is Multiplication || tree.Function is Division || tree.Function is Exponential || tree.Function is Logarithm) { var occuringVariables = from x in FunctionTreeIterator.IteratePrefix(tree) where x is VariableFunctionTree let name = ((VariableFunctionTree)x).VariableName select name; var openParameters = (new string[] { targetVariable }).Concat(parameters); var missingVariables = openParameters.Except(occuringVariables); if (missingVariables.Count() > 0) { VariableFunctionTree varTree = (VariableFunctionTree)(new Variable()).GetTreeNode(); varTree.VariableName = missingVariables.First(); varTree.SampleOffset = 0; varTree.Weight = 1.0; tree = (IFunctionTree)tree.Clone(); tree.InsertSubTree(0, varTree); } } int targetIndex = -1; IFunctionTree combinator = null; List subTrees = new List(tree.SubTrees); if (HasTargetVariable(subTrees[0], targetVariable)) { targetIndex = 0; combinator = FunctionFromCombinator(tree).GetTreeNode(); } else { for (int i = 1; i < subTrees.Count; i++) { if (HasTargetVariable(subTrees[i], targetVariable)) { targetIndex = i; combinator = GetInvertedFunction(FunctionFromCombinator(tree)).GetTreeNode(); break; } } } if (targetIndex == -1) { // target variable was not found return tree; } else { // target variable was found for (int i = 0; i < subTrees.Count; i++) { if (i != targetIndex) combinator.AddSubTree(subTrees[i]); } if (subTrees[targetIndex].Function is Variable) return MakeMultiplication(combinator, 1.0 / GetTargetVariableWeight(subTrees[targetIndex])); else { IFunctionTree bottomLeft; IFunctionTree targetChain = InvertF0Chain(subTrees[targetIndex], out bottomLeft); bottomLeft.InsertSubTree(0, combinator); return MakeMultiplication(targetChain, 1.0 / GetTargetVariableWeight(subTrees[targetIndex])); } } } private static IFunctionTree MakeMultiplication(IFunctionTree tree, double p) { if (p.IsAlmost(1.0)) return tree; var mul = (new Multiplication()).GetTreeNode(); var constP = (ConstantFunctionTree)(new Constant()).GetTreeNode(); constP.Value = p; mul.AddSubTree(tree); mul.AddSubTree(constP); return mul; } private static double GetTargetVariableWeight(IFunctionTree tree) { while (tree.SubTrees.Count > 0) { tree = tree.SubTrees[0]; } return ((VariableFunctionTree)tree).Weight; } // inverts a chain of F0 functions // precondition: left bottom is a variable (the selected target variable) // postcondition: the chain is inverted. the target variable is removed private static IFunctionTree InvertF0Chain(IFunctionTree tree, out IFunctionTree bottomLeft) { List currentChain = IterateChain(tree).ToList(); List reversedChain = currentChain.Reverse().Skip(1).ToList(); // build new tree by inverting every function in the reversed chain and keeping f0 branches untouched. IFunctionTree parent = reversedChain[0]; IFunctionTree invParent = GetInvertedFunction(parent.Function).GetTreeNode(); for (int j = 1; j < parent.SubTrees.Count; j++) { invParent.AddSubTree(parent.SubTrees[j]); } IFunctionTree root = invParent; for (int i = 1; i < reversedChain.Count(); i++) { IFunctionTree child = reversedChain[i]; IFunctionTree invChild = GetInvertedFunction(child.Function).GetTreeNode(); invParent.InsertSubTree(0, invChild); parent = child; invParent = invChild; for (int j = 1; j < parent.SubTrees.Count; j++) { invParent.AddSubTree(parent.SubTrees[j]); } } bottomLeft = invParent; return root; } //private static IFunctionTree InvertCombinator(IFunctionTree tree) { // if (tree.Function is OpenAddition) { // return (new OpenSubtraction()).GetTreeNode(); // } else if (tree.Function is OpenSubtraction) { // return (new OpenAddition()).GetTreeNode(); // } else if (tree.Function is OpenMultiplication) { // return (new OpenDivision()).GetTreeNode(); // } else if (tree.Function is OpenDivision) { // return (new OpenMultiplication()).GetTreeNode(); // } else throw new InvalidOperationException(); //} private static Dictionary combinatorFunction = new Dictionary() { { typeof(OpenAddition), new Addition()}, { typeof(OpenSubtraction), new Subtraction()}, { typeof(OpenDivision), new Division()}, { typeof(OpenMultiplication), new Multiplication()}, { typeof(Addition), new Addition()}, { typeof(Subtraction), new Subtraction()}, { typeof(Division), new Division()}, { typeof(Multiplication), new Multiplication()}, { typeof(Logarithm), new Logarithm()}, { typeof(Exponential), new Exponential()}, }; private static IFunction FunctionFromCombinator(IFunctionTree tree) { return combinatorFunction[tree.Function.GetType()]; } private static bool HasTargetVariable(IFunctionTree tree, string targetVariable) { if (tree.SubTrees.Count == 0) { var varTree = tree as VariableFunctionTree; if (varTree != null) return varTree.VariableName == targetVariable; else return false; } else return (from x in tree.SubTrees where HasTargetVariable(x, targetVariable) select true).Any(); } private static Dictionary closedForm = new Dictionary() { {typeof(OpenAddition), new OpenAddition()}, {typeof(OpenSubtraction), new OpenSubtraction()}, {typeof(OpenMultiplication), new OpenMultiplication()}, {typeof(OpenDivision), new OpenDivision()}, {typeof(AdditionF1), new Addition()}, {typeof(SubtractionF1), new Subtraction()}, {typeof(MultiplicationF1), new Multiplication()}, {typeof(DivisionF1), new Division()}, {typeof(OpenExp), new Exponential()}, {typeof(OpenLog), new Logarithm()}, //{typeof(OpenSqr), new Power()}, //{typeof(OpenSqrt), new Sqrt()}, {typeof(OpenParameter), new Variable()}, }; /// /// transforms a tree that contains F2 and F1 functions into a tree composed of F2 and F0 functions. /// precondition: the tree doesn't contains cycle or flip symbols. the tree has openparameters in the bottom left /// postcondition: all F1 and functions are replaced by matching F0 functions /// /// /// /// private static IFunctionTree BindVariables(IFunctionTree tree, IEnumerator targetVariables) { if (!closedForm.ContainsKey(tree.Function.GetType())) return tree; IFunction matchingFunction = closedForm[tree.Function.GetType()]; IFunctionTree matchingTree = matchingFunction.GetTreeNode(); if (matchingFunction is Variable) { targetVariables.MoveNext(); var varTreeNode = (VariableFunctionTree)matchingTree; varTreeNode.VariableName = targetVariables.Current; varTreeNode.SampleOffset = ((OpenParameterFunctionTree)tree).SampleOffset; varTreeNode.Weight = ((OpenParameterFunctionTree)tree).Weight; return varTreeNode; //} else if (matchingFunction is Power) { // matchingTree.AddSubTree(BindVariables(tree.SubTrees[0], targetVariables)); // var const2 = (ConstantFunctionTree)(new Constant()).GetTreeNode(); // const2.Value = 2.0; // matchingTree.AddSubTree(const2); } else { foreach (IFunctionTree subTree in tree.SubTrees) { matchingTree.AddSubTree(BindVariables(subTree, targetVariables)); } } return matchingTree; } } }