View Javadoc
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   * Wrapper around {@link net.bmahe.genetics4j.core.Fitness} for computing the fitness of a population
21   * <p>In order to leverage multi-core systems and speed up computations, the population will be split according to the
22   * number of partitions defined in
23   * {@link net.bmahe.genetics4j.core.EASystemnet.bmahe.genetics4j.core.spec.EAExecutionContext#numberOfPartitions()} and
24   * processed concurrently
25   * 
26   * @param <T>
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  }