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.util.Objects.requireNonNull; 023import static io.jenetics.internal.util.Arrays.revert; 024 025import java.util.AbstractSet; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.Comparator; 029import java.util.Iterator; 030import java.util.List; 031import java.util.Objects; 032import java.util.Set; 033import java.util.function.BiPredicate; 034import java.util.function.ToIntFunction; 035import java.util.stream.Collector; 036import java.util.stream.IntStream; 037 038import io.jenetics.util.ISeq; 039import io.jenetics.util.ProxySorter; 040import 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 * {@snippet lang="java": 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 * } 056 * 057 * or directly for {@code double[]} array objects 058 * {@snippet lang="java": 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 * } 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 */ 078public 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 < 0 || _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(e) ? 1 : 0) 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 to its crowding distance, and the elements which have 185 * smaller distance to its neighbors are removed first. 186 * 187 * {@snippet lang="java": 188 * final ParetoFront<Vec<double[]>> front = new ParetoFront<>(Vec::dominance); 189 * front.trim(10, Vec::compare, Vec::distance, Vec::length); 190 * } 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}