001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.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.util.Objects.requireNonNull;
023
024import java.util.ArrayList;
025import java.util.Comparator;
026import java.util.List;
027import java.util.function.ToIntFunction;
028import java.util.stream.IntStream;
029
030import io.jenetics.Gene;
031import io.jenetics.Optimize;
032import io.jenetics.Phenotype;
033import io.jenetics.Selector;
034import io.jenetics.util.ISeq;
035import io.jenetics.util.ProxySorter;
036import io.jenetics.util.Seq;
037
038/**
039 * This selector selects the first {@code count} elements of the population,
040 * which has been sorted by the <em>Crowded-Comparison Operator</em>, as
041 * described in <a href="http://ieeexplore.ieee.org/document/996017/">
042 *     A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II</a>
043 * <p>
044 *  <b>Reference:</b><em>
045 *      K. Deb, A. Pratap, S. Agarwal, and T. Meyarivan. 2002. A fast and elitist
046 *      multiobjective genetic algorithm: NSGA-II. Trans. Evol. Comp 6, 2
047 *      (April 2002), 182-197. DOI=<a href="http://dx.doi.org/10.1109/4235.996017">
048 *          10.1109/4235.996017</a></em>
049 *
050 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
051 * @version 4.1
052 * @since 4.1
053 */
054public class NSGA2Selector<
055        G extends Gene<?, G>,
056        C extends Comparable<? super C>
057>
058        implements Selector<G, C>
059{
060
061        private final Comparator<Phenotype<G, C>> _dominance;
062        private final ElementComparator<Phenotype<G, C>> _comparator;
063        private final ElementDistance<Phenotype<G, C>> _distance;
064        private final ToIntFunction<Phenotype<G, C>> _dimension;
065
066        /**
067         * Creates a new {@code NSGA2Selector} with the functions needed for
068         * handling the multi-objective result type {@code C}. For the {@link Vec}
069         * classes, a selector is created like in the following example:
070         * {@snippet lang="java":
071         * new NSGA2Selector<>(
072         *     Vec<T>::dominance,
073         *     Vec<T>::compare,
074         *     Vec<T>::distance,
075         *     Vec<T>::length
076         * );
077         * }
078         *
079         * @see #ofVec()
080         *
081         * @param dominance the pareto dominance comparator
082         * @param comparator the vector element comparator
083         * @param distance the vector element distance
084         * @param dimension the dimensionality of vector type {@code C}
085         */
086        public NSGA2Selector(
087                final Comparator<? super C> dominance,
088                final ElementComparator<? super C> comparator,
089                final ElementDistance<? super C> distance,
090                final ToIntFunction<? super C> dimension
091        ) {
092                requireNonNull(dominance);
093                requireNonNull(comparator);
094                requireNonNull(distance);
095                requireNonNull(dimension);
096
097                _dominance = (a, b) -> dominance.compare(a.fitness(), b.fitness());
098                _comparator = comparator.map(Phenotype::fitness);
099                _distance = distance.map(Phenotype::fitness);
100                _dimension = v -> dimension.applyAsInt(v.fitness());
101        }
102
103        @Override
104        public ISeq<Phenotype<G, C>> select(
105                final Seq<Phenotype<G, C>> population,
106                final int count,
107                final Optimize opt
108        ) {
109                final CrowdedComparator<Phenotype<G, C>> cc = new CrowdedComparator<>(
110                        population,
111                        opt,
112                        _dominance,
113                        _comparator,
114                        _distance,
115                        _dimension
116                );
117
118                final int[] idx = ProxySorter.sort(
119                        init(new int[population.size()]),
120                        population.size(),
121                        (a, i, j) -> cc.compare(a[j], a[i])
122                );
123
124                final List<Phenotype<G, C>> result = new ArrayList<>();
125                while (result.size() < count) {
126                        IntStream.of(idx)
127                                .limit(count - result.size())
128                                .mapToObj(population)
129                                .forEach(result::add);
130                }
131
132                return ISeq.of(result);
133        }
134
135        private static int[] init(final int[] indexes) {
136                for (int i = 0; i < indexes.length; ++i) indexes[i] = i;
137                return indexes;
138        }
139
140        /**
141         * Return a new selector for the given result type {@code V}. This method is
142         * a shortcut for
143         * {@snippet lang="java":
144         * new NSGA2Selector<>(
145         *     Vec<T>::dominance,
146         *     Vec<T>::compare,
147         *     Vec<T>::distance,
148         *     Vec<T>::length
149         * );
150         * }
151         *
152         * @param <G> the gene type
153         * @param <T> the array type, e.g. {@code double[]}
154         * @param <V> the multi object result type vector
155         * @return a new selector for the given result type {@code V}
156         */
157        public static <G extends Gene<?, G>, T, V extends Vec<T>>
158        NSGA2Selector<G, V> ofVec() {
159                return new NSGA2Selector<>(
160                        Vec::dominance,
161                        Vec::compare,
162                        Vec::distance,
163                        Vec::length
164                );
165        }
166
167}