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.Double.POSITIVE_INFINITY; 023import static java.lang.String.format; 024import static java.util.Objects.requireNonNull; 025 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Comparator; 029import java.util.List; 030import java.util.function.ToIntFunction; 031 032import io.jenetics.util.BaseSeq; 033import io.jenetics.util.ISeq; 034import io.jenetics.util.MSeq; 035import io.jenetics.util.ProxySorter; 036 037import io.jenetics.ext.internal.util.IntList; 038 039/** 040 * Low-level utility methods for doing pareto-optimal calculations. These methods 041 * are mostly for users who want 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 */ 047public 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, 0) > 0) { 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][q] > 0) { 230 Sp.add(q); 231 232 // Increment the domination counter of p. 233 } else if (d[q][p] > 0) { 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 // The updates 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}