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.engine; 021 022import static java.lang.String.format; 023import static java.util.Objects.requireNonNull; 024import static io.jenetics.internal.util.Requires.probability; 025import static io.jenetics.internal.util.SerialIO.readInt; 026import static io.jenetics.internal.util.SerialIO.readLong; 027import static io.jenetics.internal.util.SerialIO.writeInt; 028import static io.jenetics.internal.util.SerialIO.writeLong; 029 030import java.io.IOException; 031import java.io.InvalidObjectException; 032import java.io.ObjectInput; 033import java.io.ObjectInputStream; 034import java.io.ObjectOutput; 035import java.io.Serial; 036import java.io.Serializable; 037import java.util.Objects; 038import java.util.stream.Stream; 039 040import io.jenetics.Alterer; 041import io.jenetics.Gene; 042import io.jenetics.Mutator; 043import io.jenetics.Selector; 044import io.jenetics.SinglePointCrossover; 045import io.jenetics.TournamentSelector; 046import io.jenetics.internal.util.Requires; 047 048/** 049 * This class collects the parameters which control the behavior of the 050 * evolution process. This doesn't include the parameters for the 051 * <em>technical</em> execution like the used execution service. 052 * 053 * @see Engine 054 * @see Engine.Builder 055 * 056 * @param <G> the gene type 057 * @param <C> the fitness function result type 058 * 059 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 060 * @version 5.2 061 * @since 5.2 062 */ 063public final class EvolutionParams< 064 G extends Gene<?, G>, 065 C extends Comparable<? super C> 066> 067 implements Serializable 068{ 069 070 @Serial 071 private static final long serialVersionUID = 1L; 072 073 private final Selector<G, C> _survivorsSelector; 074 private final Selector<G, C> _offspringSelector; 075 private final Alterer<G, C> _alterer; 076 private final int _populationSize; 077 private final double _offspringFraction; 078 private final long _maximalPhenotypeAge; 079 080 private EvolutionParams( 081 final Selector<G, C> survivorsSelector, 082 final Selector<G, C> offspringSelector, 083 final Alterer<G, C> alterer, 084 final int populationSize, 085 final double offspringFraction, 086 final long maximalPhenotypeAge 087 ) { 088 _survivorsSelector = requireNonNull(survivorsSelector); 089 _offspringSelector = requireNonNull(offspringSelector); 090 _alterer = requireNonNull(alterer); 091 _populationSize = Requires.positive(populationSize); 092 _offspringFraction = Requires.probability(offspringFraction); 093 _maximalPhenotypeAge = Requires.positive(maximalPhenotypeAge); 094 } 095 096 097 /** 098 * Return the used survivor {@link Selector} of the GA. 099 * 100 * @return the used survivor {@link Selector} of the GA. 101 */ 102 public Selector<G, C> survivorsSelector() { 103 return _survivorsSelector; 104 } 105 106 /** 107 * Return the used offspring {@link Selector} of the GA. 108 * 109 * @return the used offspring {@link Selector} of the GA. 110 */ 111 public Selector<G, C> offspringSelector() { 112 return _offspringSelector; 113 } 114 115 /** 116 * Return the used {@link Alterer} of the GA. 117 * 118 * @return the used {@link Alterer} of the GA. 119 */ 120 public Alterer<G, C> alterer() { 121 return _alterer; 122 } 123 124 125 /** 126 * Return the population size. 127 * 128 * @return the population size 129 */ 130 public int populationSize() { 131 return _populationSize; 132 } 133 134 /** 135 * Return the offspring fraction. 136 * 137 * @return the offspring fraction. 138 */ 139 public double offspringFraction() { 140 return _offspringFraction; 141 } 142 143 /** 144 * Return the maximal allowed phenotype age. 145 * 146 * @return the maximal allowed phenotype age 147 */ 148 public long maximalPhenotypeAge() { 149 return _maximalPhenotypeAge; 150 } 151 152 153 /* ************************************************************************* 154 * Derived properties. 155 **************************************************************************/ 156 157 /** 158 * Return the number of offspring. <em>This is a derived property.</em> 159 * 160 * @return the offspring count. 161 */ 162 public int offspringSize() { 163 return (int)Math.rint(_populationSize*_offspringFraction); 164 } 165 166 /** 167 * Return the number of survivors. <em>This is a derived property.</em> 168 * 169 * @return the number of survivors 170 */ 171 public int survivorsSize() { 172 return _populationSize - offspringSize(); 173 } 174 175 /** 176 * Return a new builder object, initialized with {@code this} parameters. 177 * 178 * @return a new pre-filled builder object 179 */ 180 public EvolutionParams.Builder<G, C> toBuilder() { 181 return EvolutionParams.<G, C>builder() 182 .survivorsSelector(survivorsSelector()) 183 .offspringSelector(offspringSelector()) 184 .alterers(alterer()) 185 .populationSize(populationSize()) 186 .offspringFraction(offspringFraction()) 187 .maximalPhenotypeAge(maximalPhenotypeAge()); 188 } 189 190 /** 191 * Create a new evolution parameter builder. 192 * 193 * @param <G> the gene type 194 * @param <C> the fitness function result type 195 * @return a new parameter builder 196 */ 197 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 198 Builder<G, C> builder() { 199 return new Builder<>(); 200 } 201 202 203 /* ************************************************************************* 204 * Params builder 205 **************************************************************************/ 206 207 /** 208 * Builder class for the evolution parameter. 209 * 210 * @param <G> the gene type 211 * @param <C> the fitness function result type 212 */ 213 public static final class Builder< 214 G extends Gene<?, G>, 215 C extends Comparable<? super C> 216 > { 217 218 private Selector<G, C> _survivorsSelector = new TournamentSelector<>(3); 219 private Selector<G, C> _offspringSelector = new TournamentSelector<>(3); 220 private Alterer<G, C> _alterer = Alterer.of( 221 new SinglePointCrossover<G, C>(0.2), 222 new Mutator<>(0.15) 223 ); 224 private int _populationSize = 50; 225 private double _offspringFraction = 0.6; 226 private long _maximalPhenotypeAge = 70; 227 228 229 private Builder() { 230 } 231 232 public Builder<G, C> evolutionParams(final EvolutionParams<G, C> params) { 233 survivorsSelector(params.survivorsSelector()); 234 offspringSelector(params.offspringSelector()); 235 alterers(params.alterer()); 236 populationSize(params.populationSize()); 237 offspringFraction(params.offspringFraction()); 238 maximalPhenotypeAge(params.maximalPhenotypeAge()); 239 return this; 240 } 241 242 /** 243 * The selector used for selecting the offspring population. <i>Default 244 * values is set to {@code TournamentSelector<>(3)}.</i> 245 * 246 * @param selector used for selecting the offspring population 247 * @return {@code this} builder, for command chaining 248 */ 249 public Builder<G, C> offspringSelector(final Selector<G, C> selector) { 250 _offspringSelector = requireNonNull(selector); 251 return this; 252 } 253 254 /** 255 * The selector used for selecting the survivor population. <i>Default 256 * values is set to {@code TournamentSelector<>(3)}.</i> 257 * 258 * @param selector used for selecting survivor population 259 * @return {@code this} builder, for command chaining 260 */ 261 public Builder<G, C> survivorsSelector(final Selector<G, C> selector) { 262 _survivorsSelector = requireNonNull(selector); 263 return this; 264 } 265 266 /** 267 * The selector used for selecting the survivors and offspring 268 * population. <i>Default values is set to 269 * {@code TournamentSelector<>(3)}.</i> 270 * 271 * @param selector used for selecting survivors and offspring population 272 * @return {@code this} builder, for command chaining 273 */ 274 public Builder<G, C> selector(final Selector<G, C> selector) { 275 _offspringSelector = requireNonNull(selector); 276 _survivorsSelector = requireNonNull(selector); 277 return this; 278 } 279 280 /** 281 * The alterers used for alter the offspring population. <i>Default 282 * values is set to {@code new SinglePointCrossover<>(0.2)} followed by 283 * {@code new Mutator<>(0.15)}.</i> 284 * 285 * @param first the first alterer used for alter the offspring 286 * population 287 * @param rest the rest of the alterers used for alter the offspring 288 * population 289 * @return {@code this} builder, for command chaining 290 * @throws java.lang.NullPointerException if one of the alterers is 291 * {@code null}. 292 */ 293 @SafeVarargs 294 public final Builder<G, C> alterers( 295 final Alterer<G, C> first, 296 final Alterer<G, C>... rest 297 ) { 298 requireNonNull(first); 299 Stream.of(rest).forEach(Objects::requireNonNull); 300 301 _alterer = rest.length == 0 302 ? first 303 : Alterer.of(rest).compose(first); 304 305 return this; 306 } 307 308 /** 309 * The number of individuals which form the population. <i>Default 310 * values is set to {@code 50}.</i> 311 * 312 * @param size the number of individuals of a population 313 * @return {@code this} builder, for command chaining 314 * @throws IllegalArgumentException if {@code size < 1} 315 */ 316 public Builder<G, C> populationSize(final int size) { 317 if (size < 1) { 318 throw new IllegalArgumentException(format( 319 "Population size must be greater than zero, but was %s.", 320 size 321 )); 322 } 323 324 _populationSize = size; 325 return this; 326 } 327 328 329 /** 330 * The offspring fraction. 331 * 332 * @param fraction the offspring fraction 333 * @return {@code this} builder, for command chaining 334 * @throws IllegalArgumentException if the fraction is not within the 335 * range [0, 1]. 336 */ 337 public Builder<G, C> offspringFraction(final double fraction) { 338 _offspringFraction = probability(fraction); 339 return this; 340 } 341 342 /** 343 * The maximal allowed age of a phenotype. <i>Default values is set to 344 * {@code 70}.</i> 345 * 346 * @param age the maximal phenotype age 347 * @return {@code this} builder, for command chaining 348 * @throws IllegalArgumentException if {@code age < 1} 349 */ 350 public Builder<G, C> maximalPhenotypeAge(final long age) { 351 if (age < 1) { 352 throw new IllegalArgumentException(format( 353 "Phenotype age must be greater than one, but was %s.", age 354 )); 355 } 356 _maximalPhenotypeAge = age; 357 return this; 358 } 359 360 /** 361 * Builds a new {@code EvolutionParams} instance from the set properties. 362 * 363 * @return a new {@code EvolutionParams} instance from the set properties 364 */ 365 public EvolutionParams<G, C> build() { 366 return new EvolutionParams<>( 367 _survivorsSelector, 368 _offspringSelector, 369 _alterer, 370 _populationSize, 371 _offspringFraction, 372 _maximalPhenotypeAge 373 ); 374 } 375 376 377 /* ********************************************************************* 378 * Current properties 379 ***********************************************************************/ 380 381 /** 382 * Return the used {@link Alterer} of the GA. 383 * 384 * @return the used {@link Alterer} of the GA. 385 */ 386 public Alterer<G, C> alterer() { 387 return _alterer; 388 } 389 390 391 /** 392 * Return the maximal allowed phenotype age. 393 * 394 * @return the maximal allowed phenotype age 395 */ 396 public long maximalPhenotypeAge() { 397 return _maximalPhenotypeAge; 398 } 399 400 /** 401 * Return the used offspring {@link Selector} of the GA. 402 * 403 * @return the used offspring {@link Selector} of the GA. 404 */ 405 public Selector<G, C> offspringSelector() { 406 return _offspringSelector; 407 } 408 409 /** 410 * Return the used survivor {@link Selector} of the GA. 411 * 412 * @return the used survivor {@link Selector} of the GA. 413 */ 414 public Selector<G, C> survivorsSelector() { 415 return _survivorsSelector; 416 } 417 418 /** 419 * Return the number of individuals of a population. 420 * 421 * @return the number of individuals of a population 422 */ 423 public int populationSize() { 424 return _populationSize; 425 } 426 427 /** 428 * Return the offspring fraction. 429 * 430 * @return the offspring fraction. 431 */ 432 public double offspringFraction() { 433 return _offspringFraction; 434 } 435 436 /* ************************************************************************* 437 * Derived properties. 438 **************************************************************************/ 439 440 /** 441 * Return the number of offspring. <em>This is a derived property.</em> 442 * 443 * @return the offspring count. 444 */ 445 public int offspringSize() { 446 return (int)Math.round(_populationSize*_offspringFraction); 447 } 448 449 /** 450 * Return the number of survivors. <em>This is a derived property.</em> 451 * 452 * @return the number of survivors 453 */ 454 public int survivorsSize() { 455 return _populationSize - offspringSize(); 456 } 457 458 } 459 460 /* ************************************************************************* 461 * Java object serialization 462 * ************************************************************************/ 463 464 @Serial 465 private Object writeReplace() { 466 return new SerialProxy(SerialProxy.EVOLUTION_PARAMS, this); 467 } 468 469 @Serial 470 private void readObject(final ObjectInputStream stream) 471 throws InvalidObjectException 472 { 473 throw new InvalidObjectException("Serialization proxy required."); 474 } 475 476 void write(final ObjectOutput out) throws IOException { 477 out.writeObject(survivorsSelector()); 478 out.writeObject(offspringSelector()); 479 out.writeObject(alterer()); 480 writeInt(populationSize(), out); 481 out.writeDouble(offspringFraction()); 482 writeLong(maximalPhenotypeAge(), out); 483 } 484 485 @SuppressWarnings({"unchecked", "rawtypes"}) 486 static EvolutionParams read(final ObjectInput in) 487 throws IOException, ClassNotFoundException 488 { 489 return new EvolutionParams( 490 (Selector)in.readObject(), 491 (Selector)in.readObject(), 492 (Alterer)in.readObject(), 493 readInt(in), 494 in.readDouble(), 495 readLong(in) 496 ); 497 } 498 499}