1 package net.bmahe.genetics4j.core.evaluation;
2
3 import java.util.ArrayList;
4 import java.util.Comparator;
5 import java.util.List;
6 import java.util.Objects;
7 import java.util.concurrent.CompletableFuture;
8 import java.util.concurrent.ExecutorService;
9
10 import org.apache.commons.lang3.Validate;
11 import org.apache.logging.log4j.LogManager;
12 import org.apache.logging.log4j.Logger;
13
14 import net.bmahe.genetics4j.core.Fitness;
15 import net.bmahe.genetics4j.core.Genotype;
16 import net.bmahe.genetics4j.core.spec.EAConfiguration;
17 import net.bmahe.genetics4j.core.spec.EAExecutionContext;
18
19
20
21
22
23
24
25
26
27
28 public class FitnessEvaluatorSync<T extends Comparable<T>> implements FitnessEvaluator<T> {
29 public static final Logger logger = LogManager.getLogger(FitnessEvaluatorSync.class);
30
31 private final EAExecutionContext<T> eaExecutionContext;
32 private final EAConfiguration<T> eaConfigurationSync;
33
34 private final ExecutorService executorService;
35
36 public FitnessEvaluatorSync(final EAExecutionContext<T> _eaExecutionContext,
37 final EAConfiguration<T> _eaConfigurationSync,
38 final ExecutorService _executorService) {
39 Objects.requireNonNull(_eaExecutionContext);
40 Objects.requireNonNull(_eaConfigurationSync);
41 Objects.requireNonNull(_executorService);
42
43 this.eaExecutionContext = _eaExecutionContext;
44 this.eaConfigurationSync = _eaConfigurationSync;
45 this.executorService = _executorService;
46 }
47
48 @Override
49 public List<T> evaluate(final long generation, final List<Genotype> population) {
50 Validate.isTrue(generation >= 0);
51 Objects.requireNonNull(population);
52 Validate.isTrue(population.isEmpty() == false, "population cannot be empty");
53
54 final Fitness<T> fitness = eaConfigurationSync.fitness();
55 final int numPartitions = eaExecutionContext.numberOfPartitions();
56 final int partitionSize = Math.max(1, (int) Math.ceil(population.size() / numPartitions));
57
58 final List<CompletableFuture<TaskResult<T>>> tasks = new ArrayList<>();
59 for (int i = 0; i < population.size();) {
60 final int numSubPopulation = population.size() - i > partitionSize ? partitionSize : population.size() - i;
61 final int partitionStart = i;
62 final int partitionEnd = partitionStart + numSubPopulation;
63 final List<Genotype> partition = population.subList(partitionStart, partitionEnd);
64 final CompletableFuture<TaskResult<T>> asyncFitnessCompute = CompletableFuture.supplyAsync(() -> {
65 final List<T> fitnessPartition = new ArrayList<>(numSubPopulation);
66 for (int j = 0; j < partition.size(); j++) {
67 final T fitnessIndividual = fitness.compute(partition.get(j));
68 fitnessPartition.add(fitnessIndividual);
69 }
70 return new TaskResult<>(partitionStart, fitnessPartition);
71 }, executorService);
72 tasks.add(asyncFitnessCompute);
73
74 i += numSubPopulation;
75 }
76
77 CompletableFuture.allOf(tasks.toArray(new CompletableFuture[tasks.size()]));
78
79 final List<T> fitnessScores = new ArrayList<>(population.size());
80 tasks.stream()
81 .map(CompletableFuture::join)
82 .sorted(Comparator.comparingInt(TaskResult::from))
83 .forEach(taskResult -> fitnessScores.addAll(taskResult.fitness()));
84
85 return fitnessScores;
86 }
87 }