001 /*
002 * Java Genetic Algorithm Library (jenetics-8.0.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 */
020 package io.jenetics.engine;
021
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 import static io.jenetics.internal.util.Requires.probability;
025 import static io.jenetics.internal.util.SerialIO.readInt;
026 import static io.jenetics.internal.util.SerialIO.readLong;
027 import static io.jenetics.internal.util.SerialIO.writeInt;
028 import static io.jenetics.internal.util.SerialIO.writeLong;
029
030 import java.io.IOException;
031 import java.io.InvalidObjectException;
032 import java.io.ObjectInput;
033 import java.io.ObjectInputStream;
034 import java.io.ObjectOutput;
035 import java.io.Serial;
036 import java.io.Serializable;
037 import java.util.Objects;
038 import java.util.stream.Stream;
039
040 import io.jenetics.Alterer;
041 import io.jenetics.Gene;
042 import io.jenetics.Mutator;
043 import io.jenetics.Selector;
044 import io.jenetics.SinglePointCrossover;
045 import io.jenetics.TournamentSelector;
046 import 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 */
063 public 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 }
|