View Javadoc
1   package net.bmahe.genetics4j.core.selection;
2   
3   import java.util.Comparator;
4   import java.util.List;
5   import java.util.Objects;
6   import java.util.random.RandomGenerator;
7   
8   import org.apache.commons.lang3.Validate;
9   
10  import net.bmahe.genetics4j.core.Genotype;
11  import net.bmahe.genetics4j.core.Population;
12  import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
13  import net.bmahe.genetics4j.core.spec.AbstractEAExecutionContext;
14  import net.bmahe.genetics4j.core.spec.selection.RouletteWheel;
15  import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy;
16  
17  public class RouletteWheelSelectionPolicyHandler<T extends Number & Comparable<T>>
18  		implements SelectionPolicyHandler<T>
19  {
20  
21  	private final RandomGenerator randomGenerator;
22  
23  	public RouletteWheelSelectionPolicyHandler(final RandomGenerator _randomGenerator) {
24  		Objects.requireNonNull(_randomGenerator);
25  
26  		this.randomGenerator = _randomGenerator;
27  	}
28  
29  	@Override
30  	public boolean canHandle(final SelectionPolicy selectionPolicy) {
31  		Objects.requireNonNull(selectionPolicy);
32  		return selectionPolicy instanceof RouletteWheel;
33  	}
34  
35  	@Override
36  	public Selector<T> resolve(AbstractEAExecutionContext<T> eaExecutionContext,
37  			AbstractEAConfiguration<T> eaConfiguration, SelectionPolicyHandlerResolver<T> selectionPolicyHandlerResolver,
38  			SelectionPolicy selectionPolicy) {
39  		Objects.requireNonNull(selectionPolicy);
40  		Validate.isInstanceOf(RouletteWheel.class, selectionPolicy);
41  
42  		return new Selector<>() {
43  
44  			@Override
45  			public Population<T> select(final AbstractEAConfiguration<T> eaConfiguration, final long generation,
46  					final int numIndividuals, final List<Genotype> population, final List<T> fitnessScore) {
47  				Objects.requireNonNull(eaConfiguration);
48  				Objects.requireNonNull(population);
49  				Objects.requireNonNull(fitnessScore);
50  				Validate.isTrue(numIndividuals > 0);
51  				Validate.isTrue(population.size() == fitnessScore.size());
52  
53  				final Population<T> selectedIndividuals = new Population<>();
54  
55  				final double minFitness = fitnessScore.stream()
56  						.map(Number::doubleValue)
57  						.min(Comparator.naturalOrder())
58  						.orElseThrow();
59  				final double maxFitness = fitnessScore.stream()
60  						.map(Number::doubleValue)
61  						.max(Comparator.naturalOrder())
62  						.orElseThrow();
63  				final double reversedBase = minFitness + maxFitness; // Used as a base when minimizing
64  
65  				double sumFitness = 0.0;
66  				final double[] probabilities = new double[population.size()];
67  
68  				for (int i = 0; i < population.size(); i++) {
69  					final double score = switch (eaConfiguration.optimization()) {
70  						case MAXIMIZE -> fitnessScore.get(i).doubleValue();
71  						case MINIMIZE -> reversedBase - fitnessScore.get(i).doubleValue();
72  					};
73  
74  					sumFitness += score;
75  					probabilities[i] = sumFitness;
76  				}
77  
78  				for (int i = 0; i < numIndividuals; i++) {
79  					final double targetScore = randomGenerator.nextDouble() * sumFitness;
80  
81  					int index = 0;
82  					while (probabilities[index] < targetScore) {
83  						index++;
84  					}
85  
86  					selectedIndividuals.add(population.get(index), fitnessScore.get(index));
87  				}
88  
89  				return selectedIndividuals;
90  			}
91  		};
92  	}
93  }