ParetoFront.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.2).
003  * Copyright (c) 2007-2023 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.util.Objects.requireNonNull;
023 import static io.jenetics.internal.util.Arrays.revert;
024 
025 import java.util.AbstractSet;
026 import java.util.ArrayList;
027 import java.util.Collection;
028 import java.util.Comparator;
029 import java.util.Iterator;
030 import java.util.List;
031 import java.util.Objects;
032 import java.util.Set;
033 import java.util.function.BiPredicate;
034 import java.util.function.ToIntFunction;
035 import java.util.stream.Collector;
036 import java.util.stream.IntStream;
037 
038 import io.jenetics.util.ISeq;
039 import io.jenetics.util.ProxySorter;
040 import io.jenetics.util.Seq;
041 
042 /**
043  * This class only contains non-dominate (Pareto-optimal) elements according to
044  * a given <em>dominance</em> measure. Like a {@link Set}, it only contains no
045  * duplicate entries. Unlike the usual set implementation, the iteration order
046  * is deterministic.
047  <p>
048  * You can create a new {@code ParetoFront} for {@link Vec} objects
049  <pre>{@code
050  * final ParetoFront<Vec<double[]>> front = new ParetoFront<>(Vec::dominance);
051  * front.add(Vec.of(1.0, 2.0));
052  * front.add(Vec.of(1.1, 2.5));
053  * front.add(Vec.of(0.9, 2.1));
054  * front.add(Vec.of(0.0, 2.9));
055  * }</pre>
056  *
057  * or directly for {@code double[]} array objects
058  <pre>{@code
059  * final ParetoFront<double[]> front = new ParetoFront<>(Pareto::dominance);
060  * front.add(new double[]{1.0, 2.0});
061  * front.add(new double[]{1.1, 2.5});
062  * front.add(new double[]{0.9, 2.1});
063  * front.add(new double[]{0.0, 2.9});
064  * }</pre>
065  *
066  * You only have to specify the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
067  *     Pareto dominance/efficiency</a> measure.
068  *
069  @see Pareto
070  *
071  * @apiNote
072  * Inserting a new element has a time complexity of {@code O(n)}.
073  *
074  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
075  @version 5.1
076  @since 4.1
077  */
078 public final class ParetoFront<T> extends AbstractSet<T> {
079 
080     private final List<T> _population = new ArrayList<>();
081 
082     private final Comparator<? super T> _dominance;
083     private final BiPredicate<? super T, ? super T> _equals;
084 
085     /**
086      * Create a new {@code ParetoSet} with the given {@code dominance} measure.
087      *
088      @since 5.1
089      *
090      @param dominance the <em>Pareto</em> dominance measure
091      @param equals the equals predicate used for keeping the set distinct
092      @throws NullPointerException if the given {@code dominance} measure is
093      *         {@code null}
094      */
095     public ParetoFront(
096         final Comparator<? super T> dominance,
097         final BiPredicate<? super T, ? super T> equals
098     ) {
099         _dominance = requireNonNull(dominance);
100         _equals = requireNonNull(equals);
101     }
102 
103     /**
104      * Create a new {@code ParetoSet} with the given {@code dominance} measure.
105      *
106      @param dominance the <em>Pareto</em> dominance measure
107      @throws NullPointerException if the given {@code dominance} measure is
108      *         {@code null}
109      */
110     public ParetoFront(final Comparator<? super T> dominance) {
111         this(dominance, Objects::equals);
112     }
113 
114     /**
115      * Inserts an {@code element} to this pareto front.
116      *
117      * @implNote
118      * Inserting a new element has a time complexity of {@code O(this.size())},
119      * where <em>n</em> is the number of elements of {@code this} pareto-front.
120      *
121      @param element the element to add
122      @return {@code true} if this set did not already contain the specified
123      *         element
124      */
125     @Override
126     public boolean add(final T element) {
127         requireNonNull(element);
128 
129         boolean updated = false;
130         final Iterator<T> iterator = _population.iterator();
131         while (iterator.hasNext()) {
132             final T existing = iterator.next();
133 
134             int cmp = _dominance.compare(element, existing);
135             if (cmp > 0) {
136                 iterator.remove();
137                 updated = true;
138             else if (cmp < || _equals.test(element, existing)) {
139                 return updated;
140             }
141         }
142 
143         _population.add(element);
144         return true;
145     }
146 
147     /**
148      * Adds all elements of the given collection to {@code this} pareto front.
149      *
150      * @implNote
151      * The runtime complexity of this operation is
152      * {@code O(elements.size()*this.size())}.
153      *
154      @param elements the elements to add to {@code this} pareto front
155      @return {@code true} if {@code this} pareto front has been changed,
156      *         {@code false} otherwise
157      */
158     @Override
159     public boolean addAll(final Collection<? extends T> elements) {
160         final int sum = elements.stream()
161             .mapToInt(e -> add(e0)
162             .sum();
163         return sum > 0;
164     }
165 
166     /**
167      * Add the all {@code elements} to {@code this} pareto-set.
168      *
169      * @implNote
170      * Merging two pareto fronts has a time complexity of
171      * {@code O(elements.size()*this.size())}.
172      *
173      @param elements the elements to add
174      @return {@code this} pareto-set
175      @throws NullPointerException if the given parameter is {@code null}
176      */
177     public ParetoFront<T> merge(final ParetoFront<? extends T> elements) {
178         addAll(elements);
179         return this;
180     }
181 
182     /**
183      * Trims {@code this} pareto front to the given size. The front elements are
184      * sorted according its crowding distance and the elements which have smaller
185      * distance to its neighbors are removed first.
186      *
187      <pre>{@code
188      * final ParetoFront<Vec<double[]>> front = new ParetoFront<>(Vec::dominance);
189      * front.trim(10, Vec::compare, Vec::distance, Vec::length);
190      * }</pre>
191      * The example above reduces the given front to 10 elements.
192      *
193      @param size the number of front elements after the trim. If
194      *        {@code size() <= size}, nothing is trimmed.
195      @param comparator the element comparator used for calculating the
196      *        crowded distance
197      @param distance the element distance measure
198      @param dimension the number of vector elements of {@code T}
199      @return {@code this} trimmed pareto front
200      @throws NullPointerException if one of the objects is {@code null}
201      */
202     public ParetoFront<T> trim(
203         final int size,
204         final ElementComparator<? super T> comparator,
205         final ElementDistance<? super T> distance,
206         final ToIntFunction<? super T> dimension
207     ) {
208         requireNonNull(comparator);
209         requireNonNull(distance);
210         requireNonNull(dimension);
211 
212         if (size() > size) {
213             final double[] distances = Pareto.crowdingDistance(
214                 Seq.viewOf(_population),
215                 comparator,
216                 distance,
217                 dimension
218             );
219             final int[] indexes = ProxySorter.sort(distances);
220             revert(indexes);
221 
222             final List<T> list = IntStream.of(indexes)
223                 .limit(size)
224                 .mapToObj(_population::get)
225                 .toList();
226 
227             _population.clear();
228             _population.addAll(list);
229         }
230 
231         return this;
232     }
233 
234     @Override
235     public Iterator<T> iterator() {
236         return _population.iterator();
237     }
238 
239     @Override
240     public int size() {
241         return _population.size();
242     }
243 
244     @Override
245     public boolean isEmpty() {
246         return _population.isEmpty();
247     }
248 
249     /**
250      * Return the elements of {@code this} pareto-front as {@link ISeq}.
251      *
252      @return the elements of {@code this} pareto-front as {@link ISeq}
253      */
254     public ISeq<T> toISeq() {
255         return ISeq.of(_population);
256     }
257 
258     /**
259      * Return a pareto-front collector. The natural order of the elements is
260      * used as pareto-dominance order.
261      *
262      @param <C> the element type
263      @return a new pareto-front collector
264      */
265     public static <C extends Comparable<? super C>>
266     Collector<C, ?, ParetoFront<C>> toParetoFront() {
267         return toParetoFront(Comparator.naturalOrder());
268     }
269 
270     /**
271      * Return a pareto-front collector with the given pareto {@code dominance}
272      * measure.
273      *
274      @param dominance the pareto dominance comparator
275      @param <T> the element type
276      @return a new pareto-front collector
277      @throws NullPointerException if the given {@code dominance} collector is
278      *         {@code null}
279      */
280     public static <T> Collector<T, ?, ParetoFront<T>>
281     toParetoFront(final Comparator<? super T> dominance) {
282         requireNonNull(dominance);
283 
284         return Collector.of(
285             () -> new ParetoFront<>(dominance),
286             ParetoFront::add,
287             ParetoFront::merge
288         );
289     }
290 
291 }