001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.0). 003 * Copyright (c) 2007-2024 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 */ 020package io.jenetics.ext.moea; 021 022import static java.lang.Math.min; 023import static java.util.Objects.requireNonNull; 024 025import java.util.ArrayList; 026import java.util.Comparator; 027import java.util.List; 028import java.util.function.ToIntFunction; 029 030import io.jenetics.Gene; 031import io.jenetics.Optimize; 032import io.jenetics.Phenotype; 033import io.jenetics.Selector; 034import io.jenetics.internal.math.Subsets; 035import io.jenetics.util.ISeq; 036import io.jenetics.util.RandomRegistry; 037import 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 */ 062public class UFTournamentSelector< 063 G extends Gene<?, G>, 064 C 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 * {@snippet lang="java": 078 * new UFTournamentSelector<>( 079 * Vec<T>::dominance, 080 * Vec<T>::compare, 081 * Vec<T>::distance, 082 * Vec<T>::length 083 * ); 084 * } 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 = Subsets.next(random, population.size(), k); 131 132 for (int j = 0; j < G.length - 1 && 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 * {@snippet lang="java": 159 * new UFTournamentSelector<>( 160 * Vec<T>::dominance, 161 * Vec<T>::compare, 162 * Vec<T>::distance, 163 * Vec<T>::length 164 * ); 165 * } 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}