View Javadoc
1   package net.bmahe.genetics4j.gp.selection;
2   
3   import java.util.ArrayList;
4   import java.util.Comparator;
5   import java.util.List;
6   import java.util.Objects;
7   import java.util.random.RandomGenerator;
8   import java.util.stream.IntStream;
9   
10  import org.apache.commons.lang3.Validate;
11  import org.apache.logging.log4j.LogManager;
12  import org.apache.logging.log4j.Logger;
13  
14  import net.bmahe.genetics4j.core.Genotype;
15  import net.bmahe.genetics4j.core.Individual;
16  import net.bmahe.genetics4j.core.Population;
17  import net.bmahe.genetics4j.core.selection.Selector;
18  import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
19  import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy;
20  import net.bmahe.genetics4j.core.spec.selection.Tournament;
21  import net.bmahe.genetics4j.core.util.IndividualUtils;
22  import net.bmahe.genetics4j.gp.spec.selection.DoubleTournament;
23  
24  public class DoubleTournamentSelector<T extends Comparable<T>> implements Selector<T> {
25  	final static public Logger logger = LogManager.getLogger(DoubleTournamentSelector.class);
26  
27  	private final SelectionPolicy selectionPolicy;
28  	private final RandomGenerator randomGenerator;
29  
30  	public DoubleTournamentSelector(final SelectionPolicy _selectionPolicy, final RandomGenerator _randomGenerator) {
31  		Objects.requireNonNull(_selectionPolicy);
32  		Validate.isInstanceOf(DoubleTournament.class, _selectionPolicy);
33  		Objects.requireNonNull(_randomGenerator);
34  
35  		this.selectionPolicy = _selectionPolicy;
36  		this.randomGenerator = _randomGenerator;
37  	}
38  
39  	protected Individual<T> randomIndividual(final List<Genotype> population, final List<T> fitnessScore) {
40  		Objects.requireNonNull(population);
41  		Objects.requireNonNull(fitnessScore);
42  		Validate.isTrue(fitnessScore.size() > 0);
43  		Validate.isTrue(population.size() == fitnessScore.size());
44  
45  		final int candidateIndex = randomGenerator.nextInt(fitnessScore.size());
46  		return Individual.of(population.get(candidateIndex), fitnessScore.get(candidateIndex));
47  
48  	}
49  
50  	protected Individual<T> selectForFitness(final AbstractEAConfiguration<T> eaConfiguration,
51  			final Comparator<Individual<T>> fitnessComparator, final int numCandidates, final List<Genotype> population,
52  			final List<T> fitnessScore) {
53  		Objects.requireNonNull(population);
54  		Objects.requireNonNull(fitnessScore);
55  		Validate.isTrue(fitnessScore.isEmpty() == false);
56  
57  		return IntStream.range(0, numCandidates)
58  				.boxed()
59  				.map(i -> randomIndividual(population, fitnessScore))
60  				.max((a, b) -> fitnessComparator.compare(a, b))
61  				.get();
62  
63  	}
64  
65  	protected Individual<T> parsimonyPick(final Comparator<Individual<T>> parsimonyComparator,
66  			final double parsimonyTournamentSize, final Individual<T> first, final Individual<T> second) {
67  		Objects.requireNonNull(parsimonyComparator);
68  		Validate.inclusiveBetween(0.0, 2.0, parsimonyTournamentSize);
69  		Objects.requireNonNull(first);
70  		Objects.requireNonNull(second);
71  
72  		final int parsimonyCompared = parsimonyComparator.compare(first, second);
73  
74  		Individual<T> selected = first;
75  		if (randomGenerator.nextDouble() < parsimonyTournamentSize / 2.0) {
76  			if (parsimonyCompared > 0) {
77  				selected = second;
78  			}
79  		} else {
80  			if (parsimonyCompared < 0) {
81  				selected = second;
82  			}
83  		}
84  
85  		return selected;
86  	}
87  
88  	@Override
89  	public Population<T> select(final AbstractEAConfiguration<T> eaConfiguration, final long generation,
90  			final int numIndividuals, final List<Genotype> population, final List<T> fitnessScore) {
91  		Objects.requireNonNull(eaConfiguration);
92  		Objects.requireNonNull(population);
93  		Objects.requireNonNull(fitnessScore);
94  		Validate.isTrue(generation >= 0);
95  		Validate.isTrue(numIndividuals > 0);
96  		Validate.isTrue(population.size() == fitnessScore.size());
97  
98  		@SuppressWarnings("unchecked")
99  		final DoubleTournament<T> doubleTournament = (DoubleTournament<T>) selectionPolicy;
100 		final boolean doFitnessFirst = doubleTournament.doFitnessFirst();
101 		final Tournament<T> fitnessTournament = doubleTournament.fitnessTournament();
102 		final Comparator<Individual<T>> parsimonyComparator = doubleTournament.parsimonyComparator();
103 		final double parsimonyTournamentSize = doubleTournament.parsimonyTournamentSize();
104 
105 		Validate.isTrue((doFitnessFirst && parsimonyTournamentSize <= 2.0 && parsimonyTournamentSize >= 0.0)
106 				|| doFitnessFirst == false);
107 
108 		final Comparator<Individual<T>> fitnessComparator = IndividualUtils.fitnessBasedComparator(eaConfiguration);
109 
110 		logger.debug("Selecting {} individuals", numIndividuals);
111 
112 		final Population<T> selectedIndividuals = new Population<>();
113 
114 		while (selectedIndividuals.size() < numIndividuals) {
115 
116 			if (doFitnessFirst) {
117 				final Individual<T> first = selectForFitness(eaConfiguration,
118 						fitnessComparator,
119 						fitnessTournament.numCandidates(),
120 						population,
121 						fitnessScore);
122 				final Individual<T> second = selectForFitness(eaConfiguration,
123 						fitnessComparator,
124 						fitnessTournament.numCandidates(),
125 						population,
126 						fitnessScore);
127 
128 				final Individual<T> selected = parsimonyPick(parsimonyComparator, parsimonyTournamentSize, first, second);
129 
130 				selectedIndividuals.add(selected.genotype(), selected.fitness());
131 			} else {
132 
133 				final int numberCandidatesFitness = fitnessTournament.numCandidates();
134 				final List<Individual<T>> candidatesFitness = new ArrayList<>(numberCandidatesFitness);
135 
136 				for (int i = 0; i < numberCandidatesFitness; i++) {
137 					final Individual<T> first = randomIndividual(population, fitnessScore);
138 					final Individual<T> second = randomIndividual(population, fitnessScore);
139 
140 					final Individual<T> selected = parsimonyPick(parsimonyComparator,
141 							parsimonyTournamentSize,
142 							first,
143 							second);
144 
145 					candidatesFitness.add(selected);
146 				}
147 
148 				final Individual<T> selected = candidatesFitness.stream()
149 						.max((a, b) -> fitnessComparator.compare(a, b))
150 						.get();
151 
152 				selectedIndividuals.add(selected.genotype(), selected.fitness());
153 			}
154 
155 		}
156 
157 		return selectedIndividuals;
158 	}
159 }