public void SubtreeCrossoverDistributionsTest() { int generations = 5; var trees = new List <ISymbolicExpressionTree>(); var grammar = Grammars.CreateArithmeticAndAdfGrammar(); var random = new MersenneTwister(31415); double msPerCrossoverEvent; for (int i = 0; i < POPULATION_SIZE; i++) { trees.Add(ProbabilisticTreeCreator.Create(random, grammar, 100, 10)); for (int j = random.Next(3); j < 3; j++) { SubroutineCreater.CreateSubroutine(random, trees[i], 100, 10, 3, 3); } } var children = new List <ISymbolicExpressionTree>(trees); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int gCount = 0; gCount < generations; gCount++) { for (int i = 0; i < POPULATION_SIZE; i++) { var par0 = (ISymbolicExpressionTree)trees.SampleRandom(random).Clone(); var par1 = (ISymbolicExpressionTree)trees.SampleRandom(random).Clone(); children[i] = SubtreeCrossover.Cross(random, par0, par1, 0.9, 100, 10); } trees = children; } stopwatch.Stop(); foreach (var tree in trees) { Util.IsValid(tree); } msPerCrossoverEvent = stopwatch.ElapsedMilliseconds / (double)POPULATION_SIZE / (double)generations; Console.WriteLine("SubtreeCrossover: " + Environment.NewLine + msPerCrossoverEvent + " ms per crossover event (~" + Math.Round(1000.0 / (msPerCrossoverEvent)) + "crossovers / s)" + Environment.NewLine + Util.GetSizeDistributionString(trees, 105, 5) + Environment.NewLine + Util.GetFunctionDistributionString(trees) + Environment.NewLine + Util.GetNumberOfSubtreesDistributionString(trees) + Environment.NewLine + Util.GetTerminalDistributionString(trees) + Environment.NewLine ); //mkommend: commented due to performance issues on the builder //Assert.IsTrue(Math.Round(1000.0 / (msPerCrossoverEvent)) > 2000); // must achieve more than 2000 x-overs/s }
/// <summary> /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the second parent, then test all possible crossover points from the first parent to determine the best location for i to be inserted. /// </summary> public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, IExecutionContext context, ISymbolicDataAnalysisSingleObjectiveEvaluator <T> evaluator, T problemData, List <int> rows, int maxDepth, int maxLength) { // randomly choose a node from the second parent var possibleChildren = new List <ISymbolicExpressionTreeNode>(); parent1.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent1.Root) { possibleChildren.Add(n); } }); var selectedChild = possibleChildren.SampleRandom(random); var crossoverPoints = new List <CutPoint>(); var qualities = new List <Tuple <CutPoint, double> >(); parent0.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent0.Root) { var totalDepth = parent0.Root.GetBranchLevel(n) + selectedChild.GetDepth(); var totalLength = parent0.Root.GetLength() - n.GetLength() + selectedChild.GetLength(); if (totalDepth <= maxDepth && totalLength <= maxLength) { var crossoverPoint = new CutPoint(n.Parent, n); if (crossoverPoint.IsMatchingPointType(selectedChild)) { crossoverPoints.Add(crossoverPoint); } } } }); if (crossoverPoints.Any()) { // this loop will perform two swap operations per each crossover point foreach (var crossoverPoint in crossoverPoints) { // save the old parent so we can restore it later var parent = selectedChild.Parent; // perform a swap and check the quality of the solution Swap(crossoverPoint, selectedChild); IExecutionContext childContext = new ExecutionContext(context, evaluator, context.Scope); double quality = evaluator.Evaluate(childContext, parent0, problemData, rows); qualities.Add(new Tuple <CutPoint, double>(crossoverPoint, quality)); // restore the correct parent selectedChild.Parent = parent; // swap the replaced subtree back into the tree so that the structure is preserved Swap(crossoverPoint, crossoverPoint.Child); } qualities.Sort((a, b) => a.Item2.CompareTo(b.Item2)); // assuming this sorts the list in ascending order var crossoverPoint0 = evaluator.Maximization ? qualities.Last().Item1 : qualities.First().Item1; // swap the node that would create the best offspring // this last swap makes a total of (2 * crossoverPoints.Count() + 1) swap operations. Swap(crossoverPoint0, selectedChild); } return(parent0); }
/// <summary> /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the first parent, then test all nodes j from the second parent to determine the best child that would be obtained by swapping i for j. /// </summary> public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, IExecutionContext context, ISymbolicDataAnalysisSingleObjectiveEvaluator <T> evaluator, T problemData, List <int> rows, int maxDepth, int maxLength) { var crossoverPoints0 = new List <CutPoint>(); parent0.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent0.Root) { crossoverPoints0.Add(new CutPoint(n.Parent, n)); } }); CutPoint crossoverPoint0 = crossoverPoints0.SampleRandom(random); int level = parent0.Root.GetBranchLevel(crossoverPoint0.Child); int length = parent0.Root.GetLength() - crossoverPoint0.Child.GetLength(); var allowedBranches = new List <ISymbolicExpressionTreeNode>(); parent1.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent1.Root) { if (n.GetDepth() + level <= maxDepth && n.GetLength() + length <= maxLength && crossoverPoint0.IsMatchingPointType(n)) { allowedBranches.Add(n); } } }); if (allowedBranches.Count == 0) { return(parent0); } // create symbols in order to improvize an ad-hoc tree so that the child can be evaluated ISymbolicExpressionTreeNode selectedBranch = null; var nodeQualities = new List <Tuple <ISymbolicExpressionTreeNode, double> >(); var originalChild = crossoverPoint0.Child; foreach (var node in allowedBranches) { var parent = node.Parent; Swap(crossoverPoint0, node); // the swap will set the nodes parent to crossoverPoint0.Parent IExecutionContext childContext = new ExecutionContext(context, evaluator, context.Scope); double quality = evaluator.Evaluate(childContext, parent0, problemData, rows); Swap(crossoverPoint0, originalChild); // swap the child back (so that the next swap will not affect the currently swapped node from parent1) nodeQualities.Add(new Tuple <ISymbolicExpressionTreeNode, double>(node, quality)); node.Parent = parent; // restore correct parent } nodeQualities.Sort((a, b) => a.Item2.CompareTo(b.Item2)); selectedBranch = evaluator.Maximization ? nodeQualities.Last().Item1 : nodeQualities.First().Item1; // swap the node that would create the best offspring Swap(crossoverPoint0, selectedBranch); return(parent0); }
public static void Shake(IRandom random, ISymbolicExpressionTree tree, double shakingFactor) { List<ISymbolicExpressionTreeNode> parametricNodes = new List<ISymbolicExpressionTreeNode>(); tree.Root.ForEachNodePostfix(n => { if (n.HasLocalParameters) parametricNodes.Add(n); }); if (parametricNodes.Count > 0) { var selectedPoint = parametricNodes.SampleRandom(random); selectedPoint.ShakeLocalParameters(random, shakingFactor); } }
public static void Shake(IRandom random, ISymbolicExpressionTree tree, double shakingFactor) { List <ISymbolicExpressionTreeNode> parametricNodes = new List <ISymbolicExpressionTreeNode>(); tree.Root.ForEachNodePostfix(n => { if (n.HasLocalParameters) { parametricNodes.Add(n); } }); if (parametricNodes.Count > 0) { var selectedPoint = parametricNodes.SampleRandom(random); selectedPoint.ShakeLocalParameters(random, shakingFactor); } }
/// <summary> /// Genotype-to-Phenotype mapper (iterative random approach, where the next non-terminal /// symbol to expand is randomly determined). /// </summary> /// <param name="startNode">first node of the tree with arity 1</param> /// <param name="genotype">integer vector, which should be mapped to a tree</param> /// <param name="grammar">grammar to determine the allowed child symbols for each node</param> /// <param name="maxSubtreeCount">maximum allowed subtrees (= number of used genomes)</param> /// <param name="random">random number generator</param> private void MapRandomIteratively(ISymbolicExpressionTreeNode startNode, IntegerVector genotype, ISymbolicExpressionGrammar grammar, int maxSubtreeCount, IRandom random) { List <ISymbolicExpressionTreeNode> nonTerminals = new List <ISymbolicExpressionTreeNode>(); int genotypeIndex = 0; nonTerminals.Add(startNode); while (nonTerminals.Count > 0) { if (genotypeIndex >= maxSubtreeCount) { // if all genomes were used, only add terminal nodes to the remaining subtrees ISymbolicExpressionTreeNode current = nonTerminals[0]; nonTerminals.RemoveAt(0); current.AddSubtree(GetRandomTerminalNode(current, grammar, random)); } else { // similar to PIGEMapper, but here the current node is determined randomly ... ISymbolicExpressionTreeNode current = nonTerminals.SampleRandom(random); nonTerminals.Remove(current); ISymbolicExpressionTreeNode newNode = GetNewChildNode(current, genotype, grammar, genotypeIndex, random); int arity = SampleArity(random, newNode, grammar); current.AddSubtree(newNode); genotypeIndex++; // new node has subtrees, so add "arity" number of copies of this node to the nonTerminals list for (int i = 0; i < arity; ++i) { nonTerminals.Add(newNode); } } } }
/// <summary> /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the first parent, then for each matching node j from the second parent, calculate the behavioral distance based on the range: /// d(i,j) = 0.5 * ( abs(max(i) - max(j)) + abs(min(i) - min(j)) ). /// Next, assign probabilities for the selection of a node j based on the inversed and normalized behavioral distance, then make a random weighted choice. /// </summary> public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, T problemData, IList <int> rows, int maxDepth, int maxLength) { var crossoverPoints0 = new List <CutPoint>(); parent0.Root.ForEachNodePostfix((n) => { // the if clauses prevent the root or the startnode from being selected, although the startnode can be the parent of the node being swapped. if (n.Parent != null && n.Parent != parent0.Root) { crossoverPoints0.Add(new CutPoint(n.Parent, n)); } }); var crossoverPoint0 = crossoverPoints0.SampleRandom(random); int level = parent0.Root.GetBranchLevel(crossoverPoint0.Child); int length = parent0.Root.GetLength() - crossoverPoint0.Child.GetLength(); var allowedBranches = new List <ISymbolicExpressionTreeNode>(); parent1.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent1.Root) { if (n.GetDepth() + level <= maxDepth && n.GetLength() + length <= maxLength && crossoverPoint0.IsMatchingPointType(n)) { allowedBranches.Add(n); } } }); if (allowedBranches.Count == 0) { return(parent0); } var dataset = problemData.Dataset; // create symbols in order to improvize an ad-hoc tree so that the child can be evaluated var rootSymbol = new ProgramRootSymbol(); var startSymbol = new StartSymbol(); var tree0 = CreateTreeFromNode(random, crossoverPoint0.Child, rootSymbol, startSymbol); // this will change crossoverPoint0.Child.Parent double min0 = 0.0, max0 = 0.0; foreach (double v in interpreter.GetSymbolicExpressionTreeValues(tree0, dataset, rows)) { if (min0 > v) { min0 = v; } if (max0 < v) { max0 = v; } } crossoverPoint0.Child.Parent = crossoverPoint0.Parent; // restore correct parent var weights = new List <double>(); foreach (var node in allowedBranches) { var parent = node.Parent; var tree1 = CreateTreeFromNode(random, node, rootSymbol, startSymbol); double min1 = 0.0, max1 = 0.0; foreach (double v in interpreter.GetSymbolicExpressionTreeValues(tree1, dataset, rows)) { if (min1 > v) { min1 = v; } if (max1 < v) { max1 = v; } } double behavioralDistance = (Math.Abs(min0 - min1) + Math.Abs(max0 - max1)) / 2; // this can be NaN of Infinity because some trees are crazy like exp(exp(exp(...))), we correct that below weights.Add(behavioralDistance); node.Parent = parent; // restore correct node parent } // remove branches with an infinite or NaN behavioral distance for (int i = weights.Count - 1; i >= 0; --i) { if (Double.IsNaN(weights[i]) || Double.IsInfinity(weights[i])) { weights.RemoveAt(i); allowedBranches.RemoveAt(i); } } // check if there are any allowed branches left if (allowedBranches.Count == 0) { return(parent0); } ISymbolicExpressionTreeNode selectedBranch; double sum = weights.Sum(); if (sum.IsAlmost(0.0) || weights.Count == 1) // if there is only one allowed branch, or if all weights are zero { selectedBranch = allowedBranches[0]; } else { for (int i = 0; i != weights.Count; ++i) // normalize and invert values { weights[i] = 1 - weights[i] / sum; } sum = weights.Sum(); // take new sum // compute the probabilities (selection weights) for (int i = 0; i != weights.Count; ++i) { weights[i] /= sum; } #pragma warning disable 612, 618 selectedBranch = allowedBranches.SelectRandom(weights, random); #pragma warning restore 612, 618 } Swap(crossoverPoint0, selectedBranch); return(parent0); }
/// <summary> /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the first parent, then get a node j from the second parent that matches the semantic similarity criteria. /// </summary> public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, T problemData, List <int> rows, int maxDepth, int maxLength, DoubleRange range) { var crossoverPoints0 = new List <CutPoint>(); parent0.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent0.Root) { crossoverPoints0.Add(new CutPoint(n.Parent, n)); } }); var crossoverPoint0 = crossoverPoints0.SampleRandom(random); int level = parent0.Root.GetBranchLevel(crossoverPoint0.Child); int length = parent0.Root.GetLength() - crossoverPoint0.Child.GetLength(); var allowedBranches = new List <ISymbolicExpressionTreeNode>(); parent1.Root.ForEachNodePostfix((n) => { if (n.Parent != null && n.Parent != parent1.Root) { if (n.GetDepth() + level <= maxDepth && n.GetLength() + length <= maxLength && crossoverPoint0.IsMatchingPointType(n)) { allowedBranches.Add(n); } } }); if (allowedBranches.Count == 0) { return(parent0); } var dataset = problemData.Dataset; // create symbols in order to improvize an ad-hoc tree so that the child can be evaluated var rootSymbol = new ProgramRootSymbol(); var startSymbol = new StartSymbol(); var tree0 = CreateTreeFromNode(random, crossoverPoint0.Child, rootSymbol, startSymbol); List <double> estimatedValues0 = interpreter.GetSymbolicExpressionTreeValues(tree0, dataset, rows).ToList(); crossoverPoint0.Child.Parent = crossoverPoint0.Parent; // restore parent ISymbolicExpressionTreeNode selectedBranch = null; // pick the first node that fulfills the semantic similarity conditions foreach (var node in allowedBranches) { var parent = node.Parent; var tree1 = CreateTreeFromNode(random, node, startSymbol, rootSymbol); // this will affect node.Parent List <double> estimatedValues1 = interpreter.GetSymbolicExpressionTreeValues(tree1, dataset, rows).ToList(); node.Parent = parent; // restore parent OnlineCalculatorError errorState; double ssd = OnlineMeanAbsoluteErrorCalculator.Calculate(estimatedValues0, estimatedValues1, out errorState); if (range.Start <= ssd && ssd <= range.End) { selectedBranch = node; break; } } // perform the actual swap if (selectedBranch != null) { Swap(crossoverPoint0, selectedBranch); } return(parent0); }
/// <summary> /// Genotype-to-Phenotype mapper (iterative random approach, where the next non-terminal /// symbol to expand is randomly determined). /// </summary> /// <param name="startNode">first node of the tree with arity 1</param> /// <param name="genotype">integer vector, which should be mapped to a tree</param> /// <param name="grammar">grammar to determine the allowed child symbols for each node</param> /// <param name="maxSubtreeCount">maximum allowed subtrees (= number of used genomes)</param> /// <param name="random">random number generator</param> private void MapRandomIteratively(ISymbolicExpressionTreeNode startNode, IntegerVector genotype, ISymbolicExpressionGrammar grammar, int maxSubtreeCount, IRandom random) { List<ISymbolicExpressionTreeNode> nonTerminals = new List<ISymbolicExpressionTreeNode>(); int genotypeIndex = 0; nonTerminals.Add(startNode); while (nonTerminals.Count > 0) { if (genotypeIndex >= maxSubtreeCount) { // if all genomes were used, only add terminal nodes to the remaining subtrees ISymbolicExpressionTreeNode current = nonTerminals[0]; nonTerminals.RemoveAt(0); current.AddSubtree(GetRandomTerminalNode(current, grammar, random)); } else { // similar to PIGEMapper, but here the current node is determined randomly ... ISymbolicExpressionTreeNode current = nonTerminals.SampleRandom(random); nonTerminals.Remove(current); ISymbolicExpressionTreeNode newNode = GetNewChildNode(current, genotype, grammar, genotypeIndex, random); int arity = SampleArity(random, newNode, grammar); current.AddSubtree(newNode); genotypeIndex++; // new node has subtrees, so add "arity" number of copies of this node to the nonTerminals list for (int i = 0; i < arity; ++i) { nonTerminals.Add(newNode); } } } }
public void AllArchitectureAlteringOperatorsDistributionTest() { var trees = new List <ISymbolicExpressionTree>(); var newTrees = new List <ISymbolicExpressionTree>(); var grammar = Grammars.CreateArithmeticAndAdfGrammar(); var random = new MersenneTwister(31415); SymbolicExpressionTreeStringFormatter formatter = new SymbolicExpressionTreeStringFormatter(); IntValue maxTreeSize = new IntValue(MAX_TREE_LENGTH); IntValue maxTreeHeigth = new IntValue(MAX_TREE_DEPTH); IntValue maxDefuns = new IntValue(3); IntValue maxArgs = new IntValue(3); for (int i = 0; i < POPULATION_SIZE; i++) { var tree = ProbabilisticTreeCreator.Create(random, grammar, MAX_TREE_LENGTH, MAX_TREE_DEPTH); Util.IsValid(tree); trees.Add(tree); } Stopwatch stopwatch = new Stopwatch(); int failedEvents = 0; for (int g = 0; g < N_ITERATIONS; g++) { for (int i = 0; i < POPULATION_SIZE; i++) { if (random.NextDouble() < 0.5) { // manipulate stopwatch.Start(); var selectedTree = (ISymbolicExpressionTree)trees.SampleRandom(random).Clone(); var oldTree = (ISymbolicExpressionTree)selectedTree.Clone(); bool success = false; int sw = random.Next(6); switch (sw) { case 0: success = ArgumentCreater.CreateNewArgument(random, selectedTree, MAX_TREE_LENGTH, MAX_TREE_DEPTH, 3, 3); break; case 1: success = ArgumentDeleter.DeleteArgument(random, selectedTree, 3, 3); break; case 2: success = ArgumentDuplicater.DuplicateArgument(random, selectedTree, 3, 3); break; case 3: success = SubroutineCreater.CreateSubroutine(random, selectedTree, MAX_TREE_LENGTH, MAX_TREE_DEPTH, 3, 3); break; case 4: success = SubroutineDuplicater.DuplicateSubroutine(random, selectedTree, 3, 3); break; case 5: success = SubroutineDeleter.DeleteSubroutine(random, selectedTree, 3, 3); break; } stopwatch.Stop(); if (!success) { failedEvents++; } Util.IsValid(selectedTree); newTrees.Add(selectedTree); } else { stopwatch.Start(); // crossover SymbolicExpressionTree par0 = null; SymbolicExpressionTree par1 = null; do { par0 = (SymbolicExpressionTree)trees.SampleRandom(random).Clone(); par1 = (SymbolicExpressionTree)trees.SampleRandom(random).Clone(); } while (par0.Length > MAX_TREE_LENGTH || par1.Length > MAX_TREE_LENGTH); var newTree = SubtreeCrossover.Cross(random, par0, par1, 0.9, MAX_TREE_LENGTH, MAX_TREE_DEPTH); stopwatch.Stop(); Util.IsValid(newTree); newTrees.Add(newTree); } } trees = new List <ISymbolicExpressionTree>(newTrees); newTrees.Clear(); } var msPerOperation = stopwatch.ElapsedMilliseconds / ((double)POPULATION_SIZE * (double)N_ITERATIONS); Console.WriteLine("AllArchitectureAlteringOperators: " + Environment.NewLine + "Operations / s: ~" + Math.Round(1000.0 / (msPerOperation)) + "operations / s)" + Environment.NewLine + "Failed events: " + failedEvents * 100.0 / (double)(POPULATION_SIZE * N_ITERATIONS / 2.0) + "%" + Environment.NewLine + Util.GetSizeDistributionString(trees, 200, 5) + Environment.NewLine + Util.GetFunctionDistributionString(trees) + Environment.NewLine + Util.GetNumberOfSubtreesDistributionString(trees) + Environment.NewLine + Util.GetTerminalDistributionString(trees) + Environment.NewLine ); Assert.IsTrue(failedEvents * 100.0 / (POPULATION_SIZE * N_ITERATIONS / 2.0) < 75.0); // 25% of architecture operations must succeed //mkommend: commented due to performance issues on the builder // Assert.IsTrue(Math.Round(1000.0 / (msPerOperation)) > 800); // must achieve more than 800 ops per second }