-
Notifications
You must be signed in to change notification settings - Fork 150
/
Engine.java
1264 lines (1136 loc) · 35.8 KB
/
Engine.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Java Genetic Algorithm Library (@__identifier__@).
* Copyright (c) @__year__@ Franz Wilhelmstötter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author:
* Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
*/
package io.jenetics.engine;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.CompletableFuture.supplyAsync;
import static java.util.concurrent.ForkJoinPool.commonPool;
import java.time.InstantSource;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import io.jenetics.Alterer;
import io.jenetics.AltererResult;
import io.jenetics.Chromosome;
import io.jenetics.Gene;
import io.jenetics.Genotype;
import io.jenetics.Optimize;
import io.jenetics.Phenotype;
import io.jenetics.Selector;
import io.jenetics.internal.concurrent.ExecutorEvaluator;
import io.jenetics.util.Copyable;
import io.jenetics.util.Factory;
import io.jenetics.util.ISeq;
import io.jenetics.util.MSeq;
import io.jenetics.util.NanoClock;
import io.jenetics.util.Seq;
/**
* Genetic algorithm <em>engine</em> which is the main class. The following
* example shows the main steps in initializing and executing the GA.
*
* <pre>{@code
* public class RealFunction {
* // Definition of the fitness function.
* private static Double eval(final Genotype<DoubleGene> gt) {
* final double x = gt.gene().doubleValue();
* return cos(0.5 + sin(x))*cos(x);
* }
*
* public static void main(String[] args) {
* // Create/configuring the engine via its builder.
* final Engine<DoubleGene, Double> engine = Engine
* .builder(
* RealFunction::eval,
* DoubleChromosome.of(0.0, 2.0*PI))
* .populationSize(500)
* .optimize(Optimize.MINIMUM)
* .alterers(
* new Mutator<>(0.03),
* new MeanAlterer<>(0.6))
* .build();
*
* // Execute the GA (engine).
* final Phenotype<DoubleGene, Double> result = engine.stream()
* // Truncate the evolution stream if no better individual could
* // be found after 5 consecutive generations.
* .limit(bySteadyFitness(5))
* // Terminate the evolution after maximal 100 generations.
* .limit(100)
* .collect(toBestPhenotype());
* }
* }
* }</pre>
*
* The architecture allows to decouple the configuration of the engine from the
* execution. The {@code Engine} is configured via the {@code Engine.Builder}
* class and can't be changed after creation. The actual <i>evolution</i> is
* performed by the {@link EvolutionStream}, which is created by the
* {@code Engine}.
*
* @implNote
* This class is thread safe: The engine maintains no mutable state.
* Therefore, it is safe to create multiple evolution streams with one
* engine, which may be actually used in different threads.
*
* @see Engine.Builder
* @see EvolutionStart
* @see EvolutionResult
* @see EvolutionStream
* @see EvolutionStatistics
* @see Codec
* @see Constraint
*
* @param <G> the gene type
* @param <C> the fitness result type
*
* @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
* @since 3.0
* @version 7.0
*/
public final class Engine<
G extends Gene<?, G>,
C extends Comparable<? super C>
>
implements
Evolution<G, C>,
EvolutionStreamable<G, C>,
Evaluator<G, C>
{
// Problem definition.
private final Evaluator<G, C> _evaluator;
private final Factory<Genotype<G>> _genotypeFactory;
private final Constraint<G, C> _constraint;
private final Optimize _optimize;
// Evolution parameters.
private final EvolutionParams<G, C> _evolutionParams;
// Execution context for concurrent execution of evolving steps.
private final Executor _executor;
private final InstantSource _clock;
private final EvolutionInterceptor<G, C> _interceptor;
/**
* Create a new GA engine with the given parameters.
*
* @param evaluator the population fitness evaluator
* @param genotypeFactory the genotype factory this GA is working with.
* @param constraint phenotype constraint which can override the default
* implementation the {@link Phenotype#isValid()} method and repairs
* invalid phenotypes when needed.
* @param optimize the kind of optimization (minimize or maximize)
* @param evolutionParams the evolution parameters, which influences the
* evolution process
* @param executor the executor used for executing the single evolve steps
* @param clock the clock used for calculating the timing results
* @param interceptor the evolution interceptor, which gives additional
* possibilities to influence the actual evolution
* @throws NullPointerException if one of the arguments is {@code null}
* @throws IllegalArgumentException if the given integer values are smaller
* than one.
*/
Engine(
final Evaluator<G, C> evaluator,
final Factory<Genotype<G>> genotypeFactory,
final Constraint<G, C> constraint,
final Optimize optimize,
final EvolutionParams<G, C> evolutionParams,
final Executor executor,
final InstantSource clock,
final EvolutionInterceptor<G, C> interceptor
) {
_evaluator = requireNonNull(evaluator);
_genotypeFactory = requireNonNull(genotypeFactory);
_constraint = requireNonNull(constraint);
_optimize = requireNonNull(optimize);
_evolutionParams = requireNonNull(evolutionParams);
_executor = requireNonNull(executor);
_clock = requireNonNull(clock);
_interceptor = requireNonNull(interceptor);
}
@Override
public EvolutionResult<G, C> evolve(final EvolutionStart<G, C> start) {
final EvolutionTiming timing = new EvolutionTiming(_clock);
timing.evolve.start();
final EvolutionStart<G, C> interceptedStart = _interceptor.before(start);
// Create initial population if `start` is empty.
final EvolutionStart<G, C> es = interceptedStart.population().isEmpty()
? evolutionStart(interceptedStart)
: interceptedStart;
// Initial evaluation of the population.
final ISeq<Phenotype<G, C>> population = es.isDirty()
? timing.evaluation.timing(() -> eval(es.population()))
: es.population();
// Select the offspring population.
final CompletableFuture<ISeq<Phenotype<G, C>>> offspring =
supplyAsync(() ->
timing.offspringSelection.timing(() ->
selectOffspring(population)
),
_executor
);
// Select the survivor population.
final CompletableFuture<ISeq<Phenotype<G, C>>> survivors =
supplyAsync(() ->
timing.survivorsSelection.timing(() ->
selectSurvivors(population)
),
_executor
);
// Altering the offspring population.
final CompletableFuture<AltererResult<G, C>> alteredOffspring =
offspring.thenApplyAsync(off ->
timing.offspringAlter.timing(() ->
_evolutionParams.alterer().alter(off, es.generation())
),
_executor
);
// Filter and replace invalid and old survivor individuals.
final CompletableFuture<FilterResult<G, C>> filteredSurvivors =
survivors.thenApplyAsync(sur ->
timing.survivorFilter.timing(() ->
filter(sur, es.generation())
),
_executor
);
// Filter and replace invalid and old offspring individuals.
final CompletableFuture<FilterResult<G, C>> filteredOffspring =
alteredOffspring.thenApplyAsync(off ->
timing.offspringFilter.timing(() ->
filter(off.population(), es.generation())
),
_executor
);
// Combining survivors and offspring to the new population.
final CompletableFuture<ISeq<Phenotype<G, C>>> nextPopulation =
filteredSurvivors.thenCombineAsync(
filteredOffspring,
(s, o) -> ISeq.of(s.population().append(o.population())),
_executor
);
// Evaluate the fitness-function and wait for result.
final ISeq<Phenotype<G, C>> pop = nextPopulation.join();
final ISeq<Phenotype<G, C>> result = timing.evaluation.timing(() ->
eval(pop)
);
final int killCount =
filteredOffspring.join().killCount() +
filteredSurvivors.join().killCount();
final int invalidCount =
filteredOffspring.join().invalidCount() +
filteredSurvivors.join().invalidCount();
final int alterationCount = alteredOffspring.join().alterations();
EvolutionResult<G, C> er = EvolutionResult.of(
_optimize,
result,
es.generation(),
timing.toDurations(),
killCount,
invalidCount,
alterationCount
);
final EvolutionResult<G, C> interceptedResult = _interceptor.after(er);
if (er != interceptedResult) {
er = interceptedResult.withPopulation(
timing.evaluation.timing(() ->
eval(interceptedResult.population())
));
}
timing.evolve.stop();
return er
.withDurations(timing.toDurations())
.clean();
}
// Selects the survivors population. A new population object is returned.
private ISeq<Phenotype<G, C>>
selectSurvivors(final ISeq<Phenotype<G, C>> population) {
return _evolutionParams.survivorsSize() > 0
? _evolutionParams.survivorsSelector()
.select(population, _evolutionParams.survivorsSize(), _optimize)
: ISeq.empty();
}
// Selects the offspring population. A new population object is returned.
private ISeq<Phenotype<G, C>>
selectOffspring(final ISeq<Phenotype<G, C>> population) {
return _evolutionParams.offspringSize() > 0
? _evolutionParams.offspringSelector()
.select(population, _evolutionParams.offspringSize(), _optimize)
: ISeq.empty();
}
// Filters out invalid and old individuals. Filtering is done in place.
private FilterResult<G, C> filter(
final Seq<Phenotype<G, C>> population,
final long generation
) {
int killCount = 0;
int invalidCount = 0;
final MSeq<Phenotype<G, C>> pop = MSeq.of(population);
for (int i = 0, n = pop.size(); i < n; ++i) {
final Phenotype<G, C> individual = pop.get(i);
if (!_constraint.test(individual)) {
pop.set(i, _constraint.repair(individual, generation));
++invalidCount;
} else if (individual.age(generation) >
_evolutionParams.maximalPhenotypeAge())
{
pop.set(i, Phenotype.of(_genotypeFactory.newInstance(), generation));
++killCount;
}
}
return new FilterResult<>(pop.toISeq(), killCount, invalidCount);
}
/* *************************************************************************
* Evaluation methods.
**************************************************************************/
/**
* Evaluates the fitness function of the given population with the configured
* {@link Evaluator} of this engine and returns a new population
* with its fitness value assigned.
*
* @since 5.0
*
* @see Evaluator
* @see Evaluator#eval(Seq)
*
* @param population the population to evaluate
* @return a new population with assigned fitness values
* @throws IllegalStateException if the configured fitness function doesn't
* return a population with the same size as the input population.
* This exception is also thrown if one of the populations
* phenotype has no fitness value assigned.
*/
@Override
public ISeq<Phenotype<G, C>> eval(final Seq<Phenotype<G, C>> population) {
final ISeq<Phenotype<G, C>> evaluated = _evaluator.eval(population);
if (population.size() != evaluated.size()) {
throw new IllegalStateException(format(
"Expected %d individuals, but got %d. " +
"Check your evaluator function.",
population.size(), evaluated.size()
));
}
if (!evaluated.forAll(Phenotype::isEvaluated)) {
throw new IllegalStateException(
"Some phenotypes have no assigned fitness value. " +
"Check your evaluator function."
);
}
return evaluated;
}
/* *************************************************************************
* Evolution Stream creation.
**************************************************************************/
@Override
public EvolutionStream<G, C>
stream(final Supplier<EvolutionStart<G, C>> start) {
return EvolutionStream.ofEvolution(
() -> evolutionStart(start.get()),
this
);
}
@Override
public EvolutionStream<G, C> stream(final EvolutionInit<G> init) {
return stream(evolutionStart(init));
}
private EvolutionStart<G, C>
evolutionStart(final EvolutionStart<G, C> start) {
final ISeq<Phenotype<G, C>> population = start.population();
final long gen = start.generation();
final Stream<Phenotype<G, C>> stream = Stream.concat(
population.stream(),
_genotypeFactory.instances()
.map(gt -> Phenotype.of(gt, gen))
);
final ISeq<Phenotype<G, C>> pop = stream
.limit(populationSize())
.collect(ISeq.toISeq());
return EvolutionStart.of(pop, gen);
}
private EvolutionStart<G, C>
evolutionStart(final EvolutionInit<G> init) {
final ISeq<Genotype<G>> pop = init.population();
final long gen = init.generation();
return evolutionStart(
EvolutionStart.of(
pop.map(gt -> Phenotype.of(gt, gen)),
gen
)
);
}
/* *************************************************************************
* Property access methods.
**************************************************************************/
/**
* Return the used genotype {@link Factory} of the GA. The genotype factory
* is used for creating the initial population and new, random individuals
* when needed (as replacement for invalid and/or died genotypes).
*
* @return the used genotype {@link Factory} of the GA.
*/
public Factory<Genotype<G>> genotypeFactory() {
return _genotypeFactory;
}
/**
* Return the constraint of the evolution problem.
*
* @since 5.0
*
* @return the constraint of the evolution problem
*/
public Constraint<G, C> constraint() {
return _constraint;
}
/**
* Return the used survivor {@link Selector} of the GA.
*
* @return the used survivor {@link Selector} of the GA.
*/
public Selector<G, C> survivorsSelector() {
return _evolutionParams.survivorsSelector();
}
/**
* Return the used offspring {@link Selector} of the GA.
*
* @return the used offspring {@link Selector} of the GA.
*/
public Selector<G, C> offspringSelector() {
return _evolutionParams.offspringSelector();
}
/**
* Return the used {@link Alterer} of the GA.
*
* @return the used {@link Alterer} of the GA.
*/
public Alterer<G, C> alterer() {
return _evolutionParams.alterer();
}
/**
* Return the number of selected offspring.
*
* @return the number of selected offspring
*/
public int offspringSize() {
return _evolutionParams.offspringSize();
}
/**
* The number of selected survivors.
*
* @return the number of selected survivors
*/
public int survivorsSize() {
return _evolutionParams.survivorsSize();
}
/**
* Return the number of individuals of a population.
*
* @return the number of individuals of a population
*/
public int populationSize() {
return _evolutionParams.populationSize();
}
/**
* Return the maximal allowed phenotype age.
*
* @return the maximal allowed phenotype age
*/
public long maximalPhenotypeAge() {
return _evolutionParams.maximalPhenotypeAge();
}
/**
* Return the optimization strategy.
*
* @return the optimization strategy
*/
public Optimize optimize() {
return _optimize;
}
/**
* Return the {@link InstantSource} the engine is using for measuring the
* execution time.
*
* @return the clock used for measuring the execution time
*/
public InstantSource clock() {
return _clock;
}
/**
* Return the {@link Executor} the engine is using for executing the
* evolution steps.
*
* @return the executor used for performing the evolution steps
*/
public Executor executor() {
return _executor;
}
/**
* Return the evolution interceptor.
*
* @since 6.0
*
* @return the evolution result mapper
*/
public EvolutionInterceptor<G, C> interceptor() {
return _interceptor;
}
/**
* Create a new evolution {@code Engine.Builder} initialized with the values
* of the current evolution {@code Engine}. With this method, the evolution
* engine can serve as a template for a new one.
*
* @return a new engine builder
*/
public Builder<G, C> toBuilder() {
return new Builder<>(_evaluator, _genotypeFactory)
.clock(_clock)
.executor(_executor)
.optimize(_optimize)
.constraint(_constraint)
.evolutionParams(_evolutionParams)
.interceptor(_interceptor);
}
/* *************************************************************************
* Static Builder methods.
**************************************************************************/
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* function and genotype factory.
*
* @param ff the fitness function
* @param gtf the genotype factory
* @param <G> the gene type
* @param <C> the fitness function result type
* @return a new engine builder
* @throws java.lang.NullPointerException if one of the arguments is
* {@code null}.
*/
public static <G extends Gene<?, G>, C extends Comparable<? super C>>
Builder<G, C> builder(
final Function<? super Genotype<G>, ? extends C> ff,
final Factory<Genotype<G>> gtf
) {
//return new Builder<>(Evaluators.concurrent(ff, commonPool()), gtf);
return new Builder<>(Evaluators.virtual(ff), gtf);
}
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* function and problem {@code codec}.
*
* @since 3.2
*
* @param ff the fitness evaluator
* @param codec the problem codec
* @param <T> the fitness function input type
* @param <C> the fitness function result type
* @param <G> the gene type
* @return a new engine builder
* @throws java.lang.NullPointerException if one of the arguments is
* {@code null}.
*/
public static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
Builder<G, C> builder(
final Function<? super T, ? extends C> ff,
final Codec<T, G> codec
) {
return builder(ff.compose(codec.decoder()), codec.encoding());
}
/**
* Create a new evolution {@code Engine.Builder} for the given
* {@link Problem}.
*
* @since 3.4
*
* @param problem the problem to be solved by the evolution {@code Engine}
* @param <T> the (<i>native</i>) argument type of the problem fitness function
* @param <G> the gene type the evolution engine is working with
* @param <C> the result type of the fitness function
* @return Create a new evolution {@code Engine.Builder}
*/
public static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
Builder<G, C> builder(final Problem<T, G, C> problem) {
final var builder = builder(problem.fitness(), problem.codec());
problem.constraint().ifPresent(builder::constraint);
return builder;
}
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* function and chromosome templates.
*
* @param ff the fitness function
* @param chromosome the first chromosome
* @param chromosomes the chromosome templates
* @param <G> the gene type
* @param <C> the fitness function result type
* @return a new engine builder
* @throws java.lang.NullPointerException if one of the arguments is
* {@code null}.
*/
@SafeVarargs
public static <G extends Gene<?, G>, C extends Comparable<? super C>>
Builder<G, C> builder(
final Function<? super Genotype<G>, ? extends C> ff,
final Chromosome<G> chromosome,
final Chromosome<G>... chromosomes
) {
return builder(ff, Genotype.of(chromosome, chromosomes));
}
/* *************************************************************************
* Engine builder
**************************************************************************/
/**
* Builder class for building GA {@code Engine} instances.
*
* @see Engine
*
* @param <G> the gene type
* @param <C> the fitness function result type
*
* @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
* @since 3.0
* @version 6.0
*/
public static final class Builder<
G extends Gene<?, G>,
C extends Comparable<? super C>
>
implements Copyable<Builder<G, C>>
{
// No default values for this properties.
private final Evaluator<G, C> _evaluator;
private final Factory<Genotype<G>> _genotypeFactory;
private Constraint<G, C> _constraint;
private Optimize _optimize = Optimize.MAXIMUM;
// Evolution parameters.
private final EvolutionParams.Builder<G, C> _evolutionParams =
EvolutionParams.builder();
// Engine execution environment.
private Executor _executor = commonPool();
private InstantSource _clock = NanoClock.systemUTC();
private EvolutionInterceptor<G, C> _interceptor =
EvolutionInterceptor.identity();
/**
* Create a new evolution {@code Engine.Builder} with the given fitness
* evaluator and genotype factory. This is the most general way for
* creating an engine builder.
*
* @since 5.0
*
* @see Engine#builder(Function, Codec)
* @see Engine#builder(Function, Factory)
* @see Engine#builder(Problem)
* @see Engine#builder(Function, Chromosome, Chromosome[])
*
* @param evaluator the fitness evaluator
* @param genotypeFactory the genotype factory
* @throws NullPointerException if one of the arguments is {@code null}.
*/
public Builder(
final Evaluator<G, C> evaluator,
final Factory<Genotype<G>> genotypeFactory
) {
_genotypeFactory = requireNonNull(genotypeFactory);
_evaluator = requireNonNull(evaluator);
}
/**
* Applies the given {@code setup} recipe to {@code this} engine builder.
*
* @since 6.0
*
* @param setup the setup recipe applying to {@code this} builder
* @return {@code this} builder, for command chaining
* @throws NullPointerException if the {@code setup} is {@code null}.
*/
public Builder<G, C> setup(final Setup<G, C> setup) {
setup.apply(this);
return this;
}
/**
* Set the evolution parameters used by the engine.
*
* @since 5.2
*
* @param params the evolution parameter
* @return {@code this} builder, for command chaining
* @throws NullPointerException if the {@code params} is {@code null}.
*/
public Builder<G, C> evolutionParams(final EvolutionParams<G, C> params) {
_evolutionParams.evolutionParams(params);
return this;
}
/**
* The selector used for selecting the offspring population. <i>Default
* values is set to {@code TournamentSelector<>(3)}.</i>
*
* @param selector used for selecting the offspring population
* @return {@code this} builder, for command chaining
* @throws NullPointerException if one of the {@code selector} is
* {@code null}.
*/
public Builder<G, C> offspringSelector(final Selector<G, C> selector) {
_evolutionParams.offspringSelector(selector);
return this;
}
/**
* The selector used for selecting the survivors population. <i>Default
* values is set to {@code TournamentSelector<>(3)}.</i>
*
* @param selector used for selecting survivors population
* @return {@code this} builder, for command chaining
* @throws NullPointerException if one of the {@code selector} is
* {@code null}.
*/
public Builder<G, C> survivorsSelector(final Selector<G, C> selector) {
_evolutionParams.survivorsSelector(selector);
return this;
}
/**
* The selector used for selecting the survivors and offspring
* population. <i>Default values is set to
* {@code TournamentSelector<>(3)}.</i>
*
* @param selector used for selecting survivors and offspring population
* @return {@code this} builder, for command chaining
* @throws NullPointerException if one of the {@code selector} is
* {@code null}.
*/
public Builder<G, C> selector(final Selector<G, C> selector) {
_evolutionParams.selector(selector);
return this;
}
/**
* The alterers used for alter the offspring population. <i>Default
* values is set to {@code new SinglePointCrossover<>(0.2)} followed by
* {@code new Mutator<>(0.15)}.</i>
*
* @param first the first alterer used for alter the offspring
* population
* @param rest the rest of the alterers used for alter the offspring
* population
* @return {@code this} builder, for command chaining
* @throws NullPointerException if one of the alterers is {@code null}.
*/
@SafeVarargs
public final Builder<G, C> alterers(
final Alterer<G, C> first,
final Alterer<G, C>... rest
) {
_evolutionParams.alterers(first, rest);
return this;
}
/**
* The phenotype constraint is used for detecting invalid individuals
* and repairing them.
*
* <p><i>Default implementation uses {@code Phenotype::isValid} for
* validating the phenotype.</i></p>
*
* @since 5.0
*
* @param constraint phenotype constraint which can override the default
* implementation the {@link Phenotype#isValid()} method and repairs
* invalid phenotypes when needed.
* @return {@code this} builder, for command chaining
* @throws NullPointerException if one of the {@code constraint} is
* {@code null}.
*/
public Builder<G, C> constraint(final Constraint<G, C> constraint) {
_constraint = constraint;
return this;
}
/**
* The optimization strategy used by the engine. <i>Default values is
* set to {@code Optimize.MAXIMUM}.</i>
*
* @param optimize the optimization strategy used by the engine
* @return {@code this} builder, for command chaining
* @throws NullPointerException if one of the {@code optimize} is
* {@code null}.
*/
public Builder<G, C> optimize(final Optimize optimize) {
_optimize = requireNonNull(optimize);
return this;
}
/**
* Set to a fitness maximizing strategy.
*
* @since 3.4
*
* @return {@code this} builder, for command chaining
*/
public Builder<G, C> maximizing() {
return optimize(Optimize.MAXIMUM);
}
/**
* Set to a fitness minimizing strategy.
*
* @since 3.4
*
* @return {@code this} builder, for command chaining
*/
public Builder<G, C> minimizing() {
return optimize(Optimize.MINIMUM);
}
/**
* The offspring fraction. <i>Default values is set to {@code 0.6}.</i>
* This method call is equivalent to
* {@code survivorsFraction(1 - offspringFraction)} and will override
* any previously set survivors-fraction.
*
* @see #survivorsFraction(double)
*
* @param fraction the offspring fraction
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the fraction is not
* within the range [0, 1].
*/
public Builder<G, C> offspringFraction(final double fraction) {
_evolutionParams.offspringFraction(fraction);
return this;
}
/**
* The survivors fraction. <i>Default values is set to {@code 0.4}.</i>
* This method call is equivalent to
* {@code offspringFraction(1 - survivorsFraction)} and will override
* any previously set offspring-fraction.
*
* @since 3.8
*
* @see #offspringFraction(double)
*
* @param fraction the survivors fraction
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the fraction is not
* within the range [0, 1].
*/
public Builder<G, C> survivorsFraction(final double fraction) {
return offspringFraction(1 - fraction);
}
/**
* The number of offspring individuals.
*
* @since 3.8
*
* @param size the number of offspring individuals.
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the size is not
* within the range [0, population-size].
*/
public Builder<G, C> offspringSize(final int size) {
if (size < 0) {
throw new IllegalArgumentException(format(
"Offspring size must be greater or equal zero, but was %s.",
size
));
}
return offspringFraction(size/(double)_evolutionParams.populationSize());
}
/**
* The number of survivors.
*
* @since 3.8
*
* @param size the number of survivors.
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if the size is not
* within the range [0, population-size].
*/
public Builder<G, C> survivorsSize(final int size) {
if (size < 0) {
throw new IllegalArgumentException(format(
"Survivors must be greater or equal zero, but was %s.",
size
));
}
return survivorsFraction(size/(double)_evolutionParams.populationSize());
}
/**
* The number of individuals which form the population. <i>Default
* values is set to {@code 50}.</i>
*
* @param size the number of individuals of a population
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if {@code size < 1}
*/
public Builder<G, C> populationSize(final int size) {
_evolutionParams.populationSize(size);
return this;
}
/**
* The maximal allowed age of a phenotype. <i>Default values is set to
* {@code 70}.</i>
*
* @param age the maximal phenotype age
* @return {@code this} builder, for command chaining
* @throws java.lang.IllegalArgumentException if {@code age < 1}
*/
public Builder<G, C> maximalPhenotypeAge(final long age) {
_evolutionParams.maximalPhenotypeAge(age);
return this;
}
/**
* The executor used by the engine.
*
* @param executor the executor used by the engine
* @return {@code this} builder, for command chaining
*/
public Builder<G, C> executor(final Executor executor) {
_executor = requireNonNull(executor);
return this;
}
/**
* The clock used for calculating the execution durations.
*
* @param clock the clock used for calculating the execution durations
* @return {@code this} builder, for command chaining
*/
public Builder<G, C> clock(final InstantSource clock) {