001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 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 */ 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( 250 final Selector<G, C> selector 251 ) { 252 _offspringSelector = requireNonNull(selector); 253 return this; 254 } 255 256 /** 257 * The selector used for selecting the survivor population. <i>Default 258 * values is set to {@code TournamentSelector<>(3)}.</i> 259 * 260 * @param selector used for selecting survivor population 261 * @return {@code this} builder, for command chaining 262 */ 263 public Builder<G, C> survivorsSelector( 264 final Selector<G, C> selector 265 ) { 266 _survivorsSelector = requireNonNull(selector); 267 return this; 268 } 269 270 /** 271 * The selector used for selecting the survivors and offspring 272 * population. <i>Default values is set to 273 * {@code TournamentSelector<>(3)}.</i> 274 * 275 * @param selector used for selecting survivors and offspring population 276 * @return {@code this} builder, for command chaining 277 */ 278 public Builder<G, C> selector(final Selector<G, C> selector) { 279 _offspringSelector = requireNonNull(selector); 280 _survivorsSelector = requireNonNull(selector); 281 return this; 282 } 283 284 /** 285 * The alterers used for alter the offspring population. <i>Default 286 * values is set to {@code new SinglePointCrossover<>(0.2)} followed by 287 * {@code new Mutator<>(0.15)}.</i> 288 * 289 * @param first the first alterer used for alter the offspring 290 * population 291 * @param rest the rest of the alterers used for alter the offspring 292 * population 293 * @return {@code this} builder, for command chaining 294 * @throws java.lang.NullPointerException if one of the alterers is 295 * {@code null}. 296 */ 297 @SafeVarargs 298 public final Builder<G, C> alterers( 299 final Alterer<G, C> first, 300 final Alterer<G, C>... rest 301 ) { 302 requireNonNull(first); 303 Stream.of(rest).forEach(Objects::requireNonNull); 304 305 _alterer = rest.length == 0 306 ? first 307 : Alterer.of(rest).compose(first); 308 309 return this; 310 } 311 312 /** 313 * The number of individuals which form the population. <i>Default 314 * values is set to {@code 50}.</i> 315 * 316 * @param size the number of individuals of a population 317 * @return {@code this} builder, for command chaining 318 * @throws IllegalArgumentException if {@code size < 1} 319 */ 320 public Builder<G, C> populationSize(final int size) { 321 if (size < 1) { 322 throw new IllegalArgumentException(format( 323 "Population size must be greater than zero, but was %s.", 324 size 325 )); 326 } 327 328 _populationSize = size; 329 return this; 330 } 331 332 333 /** 334 * The offspring fraction. 335 * 336 * @param fraction the offspring fraction 337 * @return {@code this} builder, for command chaining 338 * @throws IllegalArgumentException if the fraction is not within the 339 * range [0, 1]. 340 */ 341 public Builder<G, C> offspringFraction(final double fraction) { 342 _offspringFraction = probability(fraction); 343 return this; 344 } 345 346 /** 347 * The maximal allowed age of a phenotype. <i>Default values is set to 348 * {@code 70}.</i> 349 * 350 * @param age the maximal phenotype age 351 * @return {@code this} builder, for command chaining 352 * @throws IllegalArgumentException if {@code age < 1} 353 */ 354 public Builder<G, C> maximalPhenotypeAge(final long age) { 355 if (age < 1) { 356 throw new IllegalArgumentException(format( 357 "Phenotype age must be greater than one, but was %s.", age 358 )); 359 } 360 _maximalPhenotypeAge = age; 361 return this; 362 } 363 364 /** 365 * Builds a new {@code EvolutionParams} instance from the set properties. 366 * 367 * @return a new {@code EvolutionParams} instance from the set properties 368 */ 369 public EvolutionParams<G, C> build() { 370 return new EvolutionParams<>( 371 _survivorsSelector, 372 _offspringSelector, 373 _alterer, 374 _populationSize, 375 _offspringFraction, 376 _maximalPhenotypeAge 377 ); 378 } 379 380 381 /* ********************************************************************* 382 * Current properties 383 ***********************************************************************/ 384 385 /** 386 * Return the used {@link Alterer} of the GA. 387 * 388 * @return the used {@link Alterer} of the GA. 389 */ 390 public Alterer<G, C> alterer() { 391 return _alterer; 392 } 393 394 395 /** 396 * Return the maximal allowed phenotype age. 397 * 398 * @return the maximal allowed phenotype age 399 */ 400 public long maximalPhenotypeAge() { 401 return _maximalPhenotypeAge; 402 } 403 404 /** 405 * Return the used offspring {@link Selector} of the GA. 406 * 407 * @return the used offspring {@link Selector} of the GA. 408 */ 409 public Selector<G, C> offspringSelector() { 410 return _offspringSelector; 411 } 412 413 /** 414 * Return the used survivor {@link Selector} of the GA. 415 * 416 * @return the used survivor {@link Selector} of the GA. 417 */ 418 public Selector<G, C> survivorsSelector() { 419 return _survivorsSelector; 420 } 421 422 /** 423 * Return the number of individuals of a population. 424 * 425 * @return the number of individuals of a population 426 */ 427 public int populationSize() { 428 return _populationSize; 429 } 430 431 /** 432 * Return the offspring fraction. 433 * 434 * @return the offspring fraction. 435 */ 436 public double offspringFraction() { 437 return _offspringFraction; 438 } 439 440 /* ************************************************************************* 441 * Derived properties. 442 **************************************************************************/ 443 444 /** 445 * Return the number of offspring. <em>This is a derived property.</em> 446 * 447 * @return the offspring count. 448 */ 449 public int offspringSize() { 450 return (int)Math.round(_populationSize*_offspringFraction); 451 } 452 453 /** 454 * Return the number of survivors. <em>This is a derived property.</em> 455 * 456 * @return the number of survivors 457 */ 458 public int survivorsSize() { 459 return _populationSize - offspringSize(); 460 } 461 462 } 463 464 /* ************************************************************************* 465 * Java object serialization 466 * ************************************************************************/ 467 468 @Serial 469 private Object writeReplace() { 470 return new SerialProxy(SerialProxy.EVOLUTION_PARAMS, this); 471 } 472 473 @Serial 474 private void readObject(final ObjectInputStream stream) 475 throws InvalidObjectException 476 { 477 throw new InvalidObjectException("Serialization proxy required."); 478 } 479 480 void write(final ObjectOutput out) throws IOException { 481 out.writeObject(survivorsSelector()); 482 out.writeObject(offspringSelector()); 483 out.writeObject(alterer()); 484 writeInt(populationSize(), out); 485 out.writeDouble(offspringFraction()); 486 writeLong(maximalPhenotypeAge(), out); 487 } 488 489 @SuppressWarnings({"unchecked", "rawtypes"}) 490 static EvolutionParams read(final ObjectInput in) 491 throws IOException, ClassNotFoundException 492 { 493 return new EvolutionParams( 494 (Selector)in.readObject(), 495 (Selector)in.readObject(), 496 (Alterer)in.readObject(), 497 readInt(in), 498 in.readDouble(), 499 readLong(in) 500 ); 501 } 502 503}