science.aist.machinelearning.algorithm.crossover.GPCrossover Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2021 the original author or authors.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package science.aist.machinelearning.algorithm.crossover;
import science.aist.machinelearning.algorithm.ga.Crossover;
import science.aist.machinelearning.algorithm.ga.Selector;
import science.aist.machinelearning.algorithm.gp.FunctionalGPGraphNode;
import science.aist.machinelearning.algorithm.gp.GPGraphNode;
import science.aist.machinelearning.algorithm.gp.nodes.basic.ResultNode;
import science.aist.machinelearning.algorithm.gp.util.BasicNodeUtil;
import science.aist.machinelearning.algorithm.gp.util.GPValidator;
import science.aist.machinelearning.core.ProblemGene;
import science.aist.machinelearning.core.Solution;
import science.aist.machinelearning.core.SolutionGene;
import science.aist.machinelearning.problem.GPProblem;
import java.util.*;
/**
* Crossover for GP-graphs. Tries to find a node that can be traded and then exchanges them.
*
* @author Daniel Wilfing
* @since 1.0
*/
public class GPCrossover implements Crossover {
private final Random r = new Random();
@Override
public Solution breed(List> population, Selector selector) {
if (population != null && population.size() > 0 && selector != null) {
if (population.size() == 1) {
return population.get(0);
}
Solution a = selector.select(population);
Solution b = selector.select(population);
//create deep copy of the graphs, or the crossover would cause changes in both graphs
ResultNode aRoot = BasicNodeUtil.deepCopyForGraph(a.getSolutionGenes().get(0).getGene());
ResultNode bRoot = BasicNodeUtil.deepCopyForGraph(b.getSolutionGenes().get(0).getGene());
//find node that returns the same thing in both graphs (except ResultNode)
Map> typesOfA = BasicNodeUtil.nodesWithReturnType(aRoot);
Map> typesOfB = BasicNodeUtil.nodesWithReturnType(bRoot);
//if either graph has no returnType (graph is only a terminal), then we can't crossover
if (typesOfA.keySet().size() == 0 || typesOfB.keySet().size() == 0) {
return population.get(r.nextInt(2));
}
//pick a random class
//do this by first shuffling, then taking one class after another from the first set
ArrayList classesToShuffle = new ArrayList<>(typesOfA.keySet());
Collections.shuffle(classesToShuffle);
Class sharedClass = null;
for (Class clazz : classesToShuffle) {
if (typesOfB.containsKey(clazz)) {
sharedClass = clazz;
break;
}
}
//if the graphs don't share a class, return a random solution
if (sharedClass == null) {
return population.get(r.nextInt(2));
}
//replace a random node in a with a random node in b
ArrayList aNodes = typesOfA.get(sharedClass);
GPGraphNode aNode = aNodes.get(r.nextInt(aNodes.size()));
ArrayList bNodes = typesOfB.get(sharedClass);
GPGraphNode bNode = bNodes.get(r.nextInt(bNodes.size()));
replaceAllNodesWithOtherNode(aRoot, aNode, bNode, new ArrayList<>());
//if something still went wrong when combining (possible with some weird collection combinations)
//then we drop the crossover and return a random solution
if (!GPValidator.validateGraph(aRoot)) {
GPValidator.validateGraph(aRoot);
return population.get(r.nextInt(2));
}
//return the new solution
Solution crossoverSolution = new Solution<>();
List> problemGenes = a.getSolutionGenes().get(0).getProblemGenes();
crossoverSolution.addGene(new SolutionGene<>(aRoot, problemGenes));
return crossoverSolution;
}
return null;
}
/**
* Checks if one of the children is the node that should be replaced. Will then replace it with the substitute
* Node.
*
* @param currentNode check children of this node
* @param replaceNode node that should get replaced
* @param substituteNode new node for replacement
* @param visitedNodes nodes that have already been checked
*/
private void replaceAllNodesWithOtherNode(GPGraphNode currentNode, GPGraphNode replaceNode, GPGraphNode substituteNode, Collection visitedNodes) {
if (currentNode instanceof FunctionalGPGraphNode) {
FunctionalGPGraphNode castedNode = (FunctionalGPGraphNode) currentNode;
for (ListIterator iterator = ((ArrayList) castedNode.getChildNodes()).listIterator(); iterator.hasNext(); ) {
GPGraphNode child = iterator.next();
//if the child is equal the node to replace, then replace it with the substitute
if (child == replaceNode) {
iterator.set(substituteNode);
}
if (!visitedNodes.contains(child)) {
visitedNodes.add(child);
replaceAllNodesWithOtherNode(child, replaceNode, substituteNode, visitedNodes);
}
}
}
}
}