EliteSelector.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.4.0).
003  * Copyright (c) 2007-2019 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics;
021 
022 import static java.lang.Math.max;
023 import static java.lang.Math.min;
024 import static java.lang.String.format;
025 import static java.util.Objects.requireNonNull;
026 import static io.jenetics.internal.util.Hashes.hash;
027 
028 import io.jenetics.internal.util.require;
029 import io.jenetics.util.ISeq;
030 import io.jenetics.util.Seq;
031 
032 /**
033  * The {@code EliteSelector} copies a small proportion of the fittest candidates,
034  * without changes, into the next generation. This may have a dramatic impact on
035  * performance by ensuring that the GA doesn't waste time re-discovering
036  * previously refused partial solutions. Individuals that are preserved through
037  * elitism remain eligible for selection as parents of the next generation.
038  * Elitism is also related with memory: remember the best solution found so far.
039  * A problem with elitism is that it may causes the GA to converge to a local
040  * optimum, so pure elitism is a race to the nearest local optimum.
041  *
042  <pre>{@code
043  * final Selector<DoubleGene, Double> selector = new EliteSelector<>(
044  *     // Number of best individuals preserved for next generation: elites
045  *     3,
046  *     // Selector used for selecting rest of population.
047  *     new RouletteWheelSelector<>()
048  * );
049  * }</pre>
050  *
051  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
052  @version 4.0
053  @since 4.0
054  */
055 public class EliteSelector<
056     extends Gene<?, G>,
057     extends Comparable<? super C>
058 >
059     implements Selector<G, C>
060 {
061     private final TruncationSelector<G, C>
062     ELITE_SELECTOR = new TruncationSelector<>();
063 
064     private final Selector<G, C> _nonEliteSelector;
065     private final int _eliteCount;
066 
067     /**
068      * Create a new elite selector with the desired number of elites to be
069      * selected and the selector used for selecting the rest of the population.
070      *
071      @param eliteCount the desired number of elite individual to be selected
072      @param nonEliteSelector the selector used for selecting the rest of the
073      *        population
074      @throws IllegalArgumentException if {@code eliteCount < 1}
075      @throws NullPointerException if the {@code nonEliteSelector} is
076      *         {@code null}
077      */
078     public EliteSelector(
079         final int eliteCount,
080         final Selector<G, C> nonEliteSelector
081     ) {
082         _eliteCount = require.positive(eliteCount);
083         _nonEliteSelector = requireNonNull(nonEliteSelector);
084     }
085 
086     /**
087      * Create a new elite selector with the desired number of elites to be
088      * selected. The selector for selecting the rest of the population is
089      * initialized with {@code TournamentSelector<>(3)}.
090      *
091      @see TournamentSelector
092      *
093      @param eliteCount the desired number of elite individual to be selected
094      @throws IllegalArgumentException if {@code eliteCount < 1}
095      */
096     public EliteSelector(final int eliteCount) {
097         this(eliteCount, new TournamentSelector<>(3));
098     }
099 
100     /**
101      * Create a new elite selector with selector used for selecting the rest of
102      * the population. The elite count is set to 1.
103      *
104      @see TournamentSelector
105      *
106      @param nonEliteSelector the selector used for selecting the rest of the
107      *        population
108      @throws NullPointerException if the {@code nonEliteSelector} is
109      *         {@code null}
110      */
111     public EliteSelector(final Selector<G, C> nonEliteSelector) {
112         this(1, nonEliteSelector);
113     }
114 
115     /**
116      * Create a new elite selector with elite count 1 and the selector for
117      * selecting the rest of the population is initialized with
118      * {@code TournamentSelector<>(3)}
119      */
120     public EliteSelector() {
121         this(1new TournamentSelector<>(3));
122     }
123 
124     @Override
125     public ISeq<Phenotype<G, C>> select(
126         final Seq<Phenotype<G, C>> population,
127         final int count,
128         final Optimize opt
129     ) {
130         if (count < 0) {
131             throw new IllegalArgumentException(format(
132                 "Selection count must be greater or equal then zero, but was %s.",
133                 count
134             ));
135         }
136 
137         ISeq<Phenotype<G, C>> result;
138         if (population.isEmpty() || count <= 0) {
139             result = ISeq.empty();
140         else {
141             final int ec = min(count, _eliteCount);
142             result = ELITE_SELECTOR.select(population, ec, opt);
143             result = result.append(
144                 _nonEliteSelector.select(population, max(0, count - ec), opt)
145             );
146         }
147 
148         return result;
149     }
150 
151     @Override
152     public int hashCode() {
153         return hash(_eliteCount, hash(_nonEliteSelector));
154     }
155 
156     @Override
157     public boolean equals(final Object obj) {
158         return obj == this ||
159             obj != null &&
160             getClass() == obj.getClass() &&
161             ((EliteSelector)obj)._eliteCount == _eliteCount &&
162             ((EliteSelector)obj)._nonEliteSelector.equals(_nonEliteSelector);
163     }
164 
165     @Override
166     public String toString() {
167         return format("EliteSelector[%d, %s]", _eliteCount, _nonEliteSelector);
168     }
169 
170 }