UFTournamentSelector.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.1).
003  * Copyright (c) 2007-2022 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.ext.moea;
021 
022 import static java.lang.Math.min;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.util.ArrayList;
026 import java.util.Comparator;
027 import java.util.List;
028 import java.util.function.ToIntFunction;
029 
030 import io.jenetics.Gene;
031 import io.jenetics.Optimize;
032 import io.jenetics.Phenotype;
033 import io.jenetics.Selector;
034 import io.jenetics.internal.math.Subset;
035 import io.jenetics.util.ISeq;
036 import io.jenetics.util.RandomRegistry;
037 import io.jenetics.util.Seq;
038 
039 /**
040  * Unique fitness based tournament selection.
041  <p>
042  <em>The selection of unique fitnesses lifts the selection bias towards
043  * over-represented fitnesses by reducing multiple solutions sharing the same
044  * fitness to a single point in the objective space. It is therefore no longer
045  * required to assign a crowding distance of zero to individual of equal fitness
046  * as the selection operator correctly enforces diversity preservation by
047  * picking unique points in the objective space.</em>
048  <p>
049  *  <b>Reference:</b><em>
050  *      Félix-Antoine Fortin and Marc Parizeau. 2013. Revisiting the NSGA-II
051  *      crowding-distance computation. In Proceedings of the 15th annual
052  *      conference on Genetic and evolutionary computation (GECCO '13),
053  *      Christian Blum (Ed.). ACM, New York, NY, USA, 623-630.
054  *      DOI=<a href="http://dx.doi.org/10.1145/2463372.2463456">
055  *          10.1145/2463372.2463456</a></em>
056  *
057  *
058  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
059  @version 4.1
060  @since 4.1
061  */
062 public class UFTournamentSelector<
063     extends Gene<?, G>,
064     extends Comparable<? super C>
065 >
066     implements Selector<G, C>
067 {
068     private final Comparator<Phenotype<G, C>> _dominance;
069     private final ElementComparator<Phenotype<G, C>> _comparator;
070     private final ElementDistance<Phenotype<G, C>> _distance;
071     private final ToIntFunction<Phenotype<G, C>> _dimension;
072 
073     /**
074      * Creates a new {@code UFTournamentSelector} with the functions needed for
075      * handling the multi-objective result type {@code C}. For the {@link Vec}
076      * classes, a selector is created like in the following example:
077      <pre>{@code
078      * new UFTournamentSelector<>(
079      *     Vec<T>::dominance,
080      *     Vec<T>::compare,
081      *     Vec<T>::distance,
082      *     Vec<T>::length
083      * );
084      * }</pre>
085      *
086      @see #ofVec()
087      *
088      @param dominance the pareto dominance comparator
089      @param comparator the vector element comparator
090      @param distance the vector element distance
091      @param dimension the dimensionality of vector type {@code C}
092      */
093     public UFTournamentSelector(
094         final Comparator<? super C> dominance,
095         final ElementComparator<? super C> comparator,
096         final ElementDistance<? super C> distance,
097         final ToIntFunction<? super C> dimension
098     ) {
099         requireNonNull(dominance);
100         requireNonNull(comparator);
101         requireNonNull(distance);
102         requireNonNull(dimension);
103 
104         _dominance = (a, b-> dominance.compare(a.fitness(), b.fitness());
105         _comparator = comparator.map(Phenotype::fitness);
106         _distance = distance.map(Phenotype::fitness);
107         _dimension = v -> dimension.applyAsInt(v.fitness());
108     }
109 
110     @Override
111     public ISeq<Phenotype<G, C>> select(
112         final Seq<Phenotype<G, C>> population,
113         final int count,
114         final Optimize opt
115     ) {
116         final var random = RandomRegistry.random();
117 
118         final CrowdedComparator<Phenotype<G, C>> cc = new CrowdedComparator<>(
119             population,
120             opt,
121             _dominance,
122             _comparator,
123             _distance,
124             _dimension
125         );
126 
127         final List<Phenotype<G, C>> S = new ArrayList<>();
128         while (S.size() < count) {
129             final int k = min(2*count - S.size(), population.size());
130             final int[] G = Subset.next(population.size(), k, random);
131 
132             for (int j = 0; j < G.length - && S.size() < count; j += 2) {
133                 final int cmp = cc.compare(G[j], G[j + 1]);
134                 final int p;
135                 if (cmp > 0) {
136                     p = G[j];
137                 else if (cmp < 0) {
138                     p = G[j + 1];
139                 else {
140                     p = random.nextBoolean() ? G[j: G[j + 1];
141                 }
142 
143                 final C fitness = population.get(p).fitness();
144                 final List<Phenotype<G, C>> list = population.stream()
145                     .filter(pt -> pt.fitness().equals(fitness))
146                     .toList();
147 
148                 S.add(list.get(random.nextInt(list.size())));
149             }
150         }
151 
152         return ISeq.of(S);
153     }
154 
155     /**
156      * Return a new selector for the given result type {@code V}. This method is
157      * a shortcut for
158      <pre>{@code
159      * new UFTournamentSelector<>(
160      *     Vec<T>::dominance,
161      *     Vec<T>::compare,
162      *     Vec<T>::distance,
163      *     Vec<T>::length
164      * );
165      * }</pre>
166      *
167      @param <G> the gene type
168      @param <T> the array type, e.g. {@code double[]}
169      @param <V> the multi object result type vector
170      @return a new selector for the given result type {@code V}
171      */
172     public static <G extends Gene<?, G>, T, V extends Vec<T>>
173     UFTournamentSelector<G, V> ofVec() {
174         return new UFTournamentSelector<>(
175             Vec::dominance,
176             Vec::compare,
177             Vec::distance,
178             Vec::length
179         );
180     }
181 
182 }