Pareto.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-6.3.0).
003  * Copyright (c) 2007-2021 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.Double.POSITIVE_INFINITY;
023 import static java.lang.String.format;
024 import static java.util.Objects.requireNonNull;
025 
026 import java.util.ArrayList;
027 import java.util.Arrays;
028 import java.util.Comparator;
029 import java.util.List;
030 import java.util.function.ToIntFunction;
031 
032 import io.jenetics.util.BaseSeq;
033 import io.jenetics.util.ISeq;
034 import io.jenetics.util.MSeq;
035 import io.jenetics.util.ProxySorter;
036 
037 import io.jenetics.ext.internal.IntList;
038 
039 /**
040  * Low-level utility methods for doing pareto-optimal calculations. This methods
041  * are mostly for users who wants to extend the existing <em>MOEA</em> classes.
042  *
043  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
044  @version 4.1
045  @since 4.1
046  */
047 public final class Pareto {
048 
049     private Pareto() {
050     }
051 
052     /* *************************************************************************
053      * Crowding distance methods.
054      * ************************************************************************/
055 
056     /**
057      * The crowding distance value of a solution provides an estimate of the
058      * density of solutions surrounding that solution. The <em>crowding
059      * distance</em> value of a particular solution is the average distance of
060      * its two neighboring solutions.
061      *
062      * @apiNote
063      * Calculating the crowding distance has a time complexity of
064      * {@code O(d*n*log(n))}, where {@code d} is the number of dimensions and
065      * {@code n} the {@code set} size.
066      *
067      @see #crowdingDistance(BaseSeq, ElementComparator, ElementDistance, ToIntFunction)
068      *
069      @param set the point set used for calculating the <em>crowding distance</em>
070      @param <T> the vector type
071      @return the crowded distances of the {@code set} points
072      @throws NullPointerException if the input {@code set} is {@code null}
073      @throws IllegalArgumentException if {@code set.get(0).length() < 2}
074      */
075     public static <T> double[]
076     crowdingDistance(final BaseSeq<? extends Vec<T>> set) {
077         return crowdingDistance(
078             set,
079             Vec::compare,
080             Vec::distance,
081             Vec::length
082         );
083     }
084 
085     /**
086      * The crowding distance value of a solution provides an estimate of the
087      * density of solutions surrounding that solution. The <em>crowding
088      * distance</em> value of a particular solution is the average distance of
089      * its two neighboring solutions.
090      *
091      * @apiNote
092      * Calculating the crowding distance has a time complexity of
093      * {@code O(d*n*log(n))}, where {@code d} is the number of dimensions and
094      * {@code n} the {@code set} size.
095      *
096      @see #crowdingDistance(BaseSeq)
097      *
098      @param set the point set used for calculating the <em>crowding distance</em>
099      @param comparator the comparator which defines the (total) order of the
100      *        vector elements of {@code T}
101      @param distance the distance of two vector elements
102      @param dimension the dimension of vector type {@code T}
103      @param <T> the vector type
104      @return the crowded distances of the {@code set} points
105      @throws NullPointerException if one of the arguments is {@code null}
106      */
107     public static <T> double[] crowdingDistance(
108         final BaseSeq<? extends T> set,
109         final ElementComparator<? super T> comparator,
110         final ElementDistance<? super T> distance,
111         final ToIntFunction<? super T> dimension
112     ) {
113         requireNonNull(set);
114         requireNonNull(comparator);
115         requireNonNull(distance);
116 
117         final double[] result = new double[set.length()];
118         if (set.length() 3) {
119             Arrays.fill(result, POSITIVE_INFINITY);
120         else {
121             for (int m = 0, d = dimension.applyAsInt(set.get(0)); m < d; ++m) {
122                 final int[] idx = ProxySorter.sort(
123                     set,
124                     comparator.ofIndex(m).reversed()
125                 );
126 
127                 result[idx[0]] = POSITIVE_INFINITY;
128                 result[idx[set.length() 1]] = POSITIVE_INFINITY;
129 
130                 final T max = set.get(idx[0]);
131                 final T min = set.get(idx[set.length() 1]);
132                 final double dm = distance.distance(max, min, m);
133 
134                 if (Double.compare(dm, 00) {
135                     for (int i = 1, n = set.length() 1; i < n; ++i) {
136                         final double dist = distance.distance(
137                             set.get(idx[i - 1]),
138                             set.get(idx[i + 1]),
139                             m
140                         );
141 
142                         result[idx[i]] += dist/dm;
143                     }
144                 }
145             }
146         }
147 
148         return result;
149     }
150 
151     /* *************************************************************************
152      * Pareto ranks methods.
153      * ************************************************************************/
154 
155     /**
156      * Calculates the <em>non-domination</em> rank of the given input {@code set},
157      * using the <em>natural</em> order of the elements as <em>dominance</em>
158      * measure.
159      *
160      * @apiNote
161      * Calculating the rank has a time complexity of {@code O(n^2}, where
162      * {@code n} the {@code set} size.
163      *
164      <p>
165      *  <b>Reference:</b><em>
166      *      Kalyanmoy Deb, Associate Member, IEEE, Amrit Pratap,
167      *      Sameer Agarwal, and T. Meyarivan.
168      *      A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II,
169      *      IEEE TRANSACTIONS ON EVOLUTIONARY COMPUTATION, VOL. 6, NO. 2,
170      *      APRIL 2002.</em>
171      *
172      @param set the input set
173      @param <T> the element type
174      @return the <em>non-domination</em> rank of the given input {@code set}
175      */
176     public static <T> int[] rank(final BaseSeq<? extends Vec<T>> set) {
177         return rank(set, Vec::dominance);
178     }
179 
180     /**
181      * Calculates the <em>non-domination</em> rank of the given input {@code set},
182      * using the given {@code dominance} comparator.
183      *
184      * @apiNote
185      * Calculating the rank has a time and space complexity of {@code O(n^2},
186      * where {@code n} the {@code set} size.
187      *
188      <p>
189      *  <b>Reference:</b><em>
190      *      Kalyanmoy Deb, Associate Member, IEEE, Amrit Pratap,
191      *      Sameer Agarwal, and T. Meyarivan.
192      *      A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II,
193      *      IEEE TRANSACTIONS ON EVOLUTIONARY COMPUTATION, VOL. 6, NO. 2,
194      *      APRIL 2002.</em>
195      *
196      @param set the input set
197      @param dominance the dominance comparator used
198      @param <T> the element type
199      @return the <em>non-domination</em> rank of the given input {@code set}
200      */
201     public static <T> int[] rank(
202         final BaseSeq<? extends T> set,
203         final Comparator<? super T> dominance
204     ) {
205         // Pre-compute the dominance relations.
206         final int[][] d = new int[set.length()][set.length()];
207         for (int i = 0; i < set.length(); ++i) {
208             for (int j = i + 1; j < set.length(); ++j) {
209                 d[i][j= dominance.compare(set.get(i), set.get(j));
210                 d[j][i= -d[i][j];
211             }
212         }
213 
214         // Compute for each element p the element q that it dominates and the
215         // number of times it is dominated. Using the names as defined in the
216         // referenced paper.
217         final int[] nq = new int[set.length()];
218         final List<IntList> fronts = new ArrayList<>();
219         IntList Fi = new IntList();
220 
221         for (int p = 0; p < set.length(); ++p) {
222             final IntList Sp = new IntList();
223             int np = 0;
224 
225             for (int q = 0; q < set.length(); ++q) {
226                 if (p != q) {
227                     // If p dominates q, add q to the set of solutions
228                     // dominated by p.
229                     if (d[p][q0) {
230                         Sp.add(q);
231 
232                     // Increment the domination counter of p.
233                     else if (d[q][p0) {
234                         np += 1;
235                     }
236                 }
237             }
238 
239             // p belongs to the first front.
240             if (np == 0) {
241                 Fi.add(p);
242             }
243 
244             fronts.add(Sp);
245             nq[p= np;
246         }
247 
248         // Initialize the front counter.
249         int i = 0;
250         final int[] ranks = new int[set.length()];
251         while (!Fi.isEmpty()) {
252             // Used to store the members of the next front.
253             final IntList Q = new IntList();
254 
255             for (int p = 0; p < Fi.size(); ++p) {
256                 final int fi = Fi.get(p);
257                 ranks[fi= i;
258 
259                 // Update the dominated counts as compute next front.
260                 for (int k = 0, n = fronts.get(fi).size(); k < n; ++k) {
261                     final int q = fronts.get(fi).get(k);
262                     nq[q-= 1;
263 
264                     // q belongs to the next front.
265                     if (nq[q== 0) {
266                         Q.add(q);
267                     }
268                 }
269             }
270 
271             ++i;
272             Fi = Q;
273         }
274 
275         return ranks;
276     }
277 
278     /* *************************************************************************
279      * 'front'
280      * ************************************************************************/
281 
282     /**
283      * Return the elements, from the given input {@code set}, which are part of
284      * the pareto front. The {@link Comparable} interface defines the dominance
285      * measure of the elements, used for calculating the pareto front.
286      <p>
287      *  <b>Reference:</b><em>
288      *      E. Zitzler and L. Thiele.
289      *      Multiobjective Evolutionary Algorithms: A Comparative Case Study
290      *      and the Strength Pareto Approach,
291      *      IEEE Transactions on Evolutionary Computation, vol. 3, no. 4,
292      *      pp. 257-271, 1999.</em>
293      *
294      @param set the input set
295      @param <T> the element type
296      @return the elements which are part of the pareto set
297      @throws NullPointerException if one of the arguments is {@code null}
298      */
299     public static <T> ISeq<Vec<T>> front(final BaseSeq<? extends Vec<T>> set) {
300         return front(set, Vec::dominance);
301     }
302 
303     /**
304      * Return the elements, from the given input {@code set}, which are part of
305      * the pareto front.
306      <p>
307      *  <b>Reference:</b><em>
308      *      E. Zitzler and L. Thiele.
309      *      Multiobjective Evolutionary Algorithms: A Comparative Case Study
310      *      and the Strength Pareto Approach,
311      *      IEEE Transactions on Evolutionary Computation, vol. 3, no. 4,
312      *      pp. 257-271, 1999.</em>
313      *
314      @param set the input set
315      @param dominance the dominance comparator used
316      @param <T> the element type
317      @return the elements which are part of the pareto set
318      @throws NullPointerException if one of the arguments is {@code null}
319      */
320     public static <T> ISeq<T> front(
321         final BaseSeq<? extends T> set,
322         final Comparator<? super T> dominance
323     ) {
324         final MSeq<T> front = MSeq.of(set);
325 
326         int n = front.size();
327         int i = 0;
328         while (i < n) {
329             int j = i + 1;
330             while (j < n) {
331                 if (dominance.compare(front.get(i), front.get(j)) 0) {
332                     --n;
333                     front.swap(j, n);
334                 else if (dominance.compare(front.get(j), front.get(i)) 0) {
335                     --n;
336                     front.swap(i, n);
337                     --i;
338                     break;
339                 else {
340                     ++j;
341                 }
342             }
343             ++i;
344         }
345 
346         return front.subSeq(0, n).copy().toISeq();
347     }
348 
349 
350     /* *************************************************************************
351      * Common 'dominance' methods.
352      * ************************************************************************/
353 
354     /**
355      * Calculates the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
356      *     <b>Pareto Dominance</b></a> of the two vectors <b>u</b> and <b>v</b>.
357      *
358      @see Vec#dominance(Comparable[], Comparable[])
359      *
360      @param u the first vector
361      @param v the second vector
362      @param <C> the element type of vector <b>u</b> and <b>v</b>
363      @return {@code 1} if <b>u</b> ≻ <b>v</b>, {@code -1} if <b>v</b> 
364      *         <b>u</b> and {@code 0} otherwise
365      @throws NullPointerException if one of the arguments is {@code null}
366      @throws IllegalArgumentException if {@code u.length != v.length}
367      */
368     public static <C extends Comparable<? super C>> int
369     dominance(final C[] u, final C[] v) {
370         return dominance(u, v, Comparator.naturalOrder());
371     }
372 
373     /**
374      * Calculates the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
375      *     <b>Pareto Dominance</b></a> of the two vectors <b>u</b> and <b>v</b>.
376      *
377      @see Vec#dominance(Object[], Object[], Comparator)
378      *
379      @param u the first vector
380      @param v the second vector
381      @param comparator the element comparator which is used for calculating
382      *        the dominance
383      @param <T> the element type of vector <b>u</b> and <b>v</b>
384      @return {@code 1} if <b>u</b> ≻ <b>v</b>, {@code -1} if <b>v</b> 
385      *         <b>u</b> and {@code 0} otherwise
386      @throws NullPointerException if one of the arguments is {@code null}
387      @throws IllegalArgumentException if {@code u.length != v.length}
388      */
389     public static <T> int
390     dominance(final T[] u, final T[] v, final Comparator<? super T> comparator) {
391         requireNonNull(comparator);
392         checkLength(u.length, v.length);
393 
394         return dominance(
395             u, v, u.length, (a, b, i-> comparator.compare(a[i], b[i])
396         );
397     }
398 
399     /**
400      * Calculates the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
401      *     <b>Pareto Dominance</b></a> of the two vectors <b>u</b> and <b>v</b>.
402      *
403      @see Vec#dominance(int[], int[])
404      *
405      @param u the first vector
406      @param v the second vector
407      @return {@code 1} if <b>u</b> ≻ <b>v</b>, {@code -1} if <b>v</b> 
408      *         <b>u</b> and {@code 0} otherwise
409      @throws NullPointerException if one of the arguments is {@code null}
410      @throws IllegalArgumentException if {@code u.length != v.length}
411      */
412     public static int dominance(final int[] u, final int[] v) {
413         checkLength(u.length, v.length);
414 
415         return dominance(
416             u, v, u.length, (a, b, i-> Integer.compare(a[i], b[i])
417         );
418     }
419 
420     /**
421      * Calculates the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
422      *     <b>Pareto Dominance</b></a> of the two vectors <b>u</b> and <b>v</b>.
423      *
424      @see Vec#dominance(long[], long[])
425      *
426      @param u the first vector
427      @param v the second vector
428      @return {@code 1} if <b>u</b> ≻ <b>v</b>, {@code -1} if <b>v</b> 
429      *         <b>u</b> and {@code 0} otherwise
430      @throws NullPointerException if one of the arguments is {@code null}
431      @throws IllegalArgumentException if {@code u.length != v.length}
432      */
433     public static int dominance(final long[] u, final long[] v) {
434         checkLength(u.length, v.length);
435 
436         return dominance(
437             u, v, u.length, (a, b, i-> Long.compare(a[i], b[i])
438         );
439     }
440 
441     /**
442      * Calculates the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
443      *     <b>Pareto Dominance</b></a> of the two vectors <b>u</b> and <b>v</b>.
444      *
445      @see Vec#dominance(double[], double[])
446      *
447      @param u the first vector
448      @param v the second vector
449      @return {@code 1} if <b>u</b> ≻ <b>v</b>, {@code -1} if <b>v</b> 
450      *         <b>u</b> and {@code 0} otherwise
451      @throws NullPointerException if one of the arguments is {@code null}
452      @throws IllegalArgumentException if {@code u.length != v.length}
453      */
454     public static int dominance(final double[] u, final double[] v) {
455         checkLength(u.length, v.length);
456 
457         return dominance(
458             u, v, u.length, (a, b, i-> Double.compare(a[i], b[i])
459         );
460     }
461 
462     private static void checkLength(final int i, final int j) {
463         if (i != j) {
464             throw new IllegalArgumentException(format(
465                 "Length are not equals: %d != %d.", i, j
466             ));
467         }
468     }
469 
470     /**
471      * Calculates the <a href="https://en.wikipedia.org/wiki/Pareto_efficiency">
472      *     <b>Pareto Dominance</b></a> of the two vectors <b>u</b> and <b>v</b>.
473      *
474      @since 5.2
475      *
476      @param u the first vector
477      @param v the second vector
478      @param dimensions the number of vector elements
479      @param comparator the comparator used for comparing the vector elements
480      @param <V> the vector type
481      @return {@code 1} if <b>u</b> ≻ <b>v</b>, {@code -1} if <b>v</b> 
482      *         <b>u</b> and {@code 0} otherwise
483      @throws NullPointerException if one of the arguments is {@code null}
484      */
485     public static <V> int dominance(
486         final V u,
487         final V v,
488         final int dimensions,
489         final ElementComparator<? super V> comparator
490     ) {
491         boolean udominated = false;
492         boolean vdominated = false;
493 
494         for (int i = 0; i < dimensions; ++i) {
495             final int cmp = comparator.compare(u, v, i);
496 
497             if (cmp > 0) {
498                 if (vdominated) {
499                     return 0;
500                 else {
501                     udominated = true;
502                 }
503             else if (cmp < 0) {
504                 if (udominated) {
505                     return 0;
506                 else {
507                     vdominated = true;
508                 }
509             }
510         }
511 
512         if (udominated == vdominated) {
513             return 0;
514         else if (udominated) {
515             return 1;
516         else {
517             return -1;
518         }
519     }
520 
521 }