FitnessEvaluatorVirtualThread.java

1
package net.bmahe.genetics4j.core.evaluation;
2
3
import java.util.ArrayList;
4
import java.util.List;
5
import java.util.Objects;
6
import java.util.concurrent.CompletableFuture;
7
import java.util.concurrent.ExecutorService;
8
import java.util.concurrent.Executors;
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
 * Virtual thread-based fitness evaluator that creates one virtual thread per individual evaluation.
21
 *
22
 * <p>This evaluator leverages Java 21+ virtual threads to provide massive parallelism for fitness evaluation without
23
 * the overhead of traditional platform threads. Each genotype gets its own virtual thread, making this approach
24
 * particularly suitable for:
25
 *
26
 * <ul>
27
 * <li><strong>I/O-bound fitness functions</strong>: Database queries, web service calls, file operations</li>
28
 * <li><strong>Large populations</strong>: Thousands of individuals without thread pool limitations</li>
29
 * <li><strong>Complex simulations</strong>: Long-running evaluations that benefit from massive parallelism</li>
30
 * <li><strong>External service integration</strong>: Fitness evaluations requiring network calls</li>
31
 * </ul>
32
 *
33
 * <p>Unlike traditional thread pool-based evaluators, this implementation:
34
 * <ul>
35
 * <li>Creates one virtual thread per genotype (no partitioning)</li>
36
 * <li>Leverages virtual thread's lightweight nature for maximum concurrency</li>
37
 * <li>Automatically manages virtual thread lifecycle</li>
38
 * <li>Provides optimal resource utilization for I/O-intensive workloads</li>
39
 * </ul>
40
 *
41
 * <p>Performance characteristics:
42
 * <ul>
43
 * <li><strong>Memory overhead</strong>: ~200 bytes per virtual thread vs ~2MB per platform thread</li>
44
 * <li><strong>Creation cost</strong>: Nearly zero compared to platform threads</li>
45
 * <li><strong>Blocking behavior</strong>: Virtual threads don't block carrier threads during I/O</li>
46
 * <li><strong>Scalability</strong>: Can handle millions of concurrent virtual threads</li>
47
 * </ul>
48
 *
49
 * <p>This evaluator automatically creates and manages a virtual thread executor, eliminating the need for explicit
50
 * thread pool configuration and management.
51
 *
52
 * @param <T> the type of fitness values produced, must be comparable for selection operations
53
 * @see FitnessEvaluator
54
 * @see FitnessEvaluatorSync
55
 * @see net.bmahe.genetics4j.core.Fitness
56
 */
57
public class FitnessEvaluatorVirtualThread<T extends Comparable<T>> implements FitnessEvaluator<T> {
58
	public static final Logger logger = LogManager.getLogger(FitnessEvaluatorVirtualThread.class);
59
60
	private final EAExecutionContext<T> eaExecutionContext;
61
	private final EAConfiguration<T> eaConfiguration;
62
	private final ExecutorService virtualThreadExecutor;
63
64
	public FitnessEvaluatorVirtualThread(final EAExecutionContext<T> _eaExecutionContext,
65
			final EAConfiguration<T> _eaConfiguration) {
66
		Objects.requireNonNull(_eaExecutionContext);
67
		Objects.requireNonNull(_eaConfiguration);
68
69 1 1. <init> : Removed assignment to member variable eaExecutionContext → SURVIVED
		this.eaExecutionContext = _eaExecutionContext;
70 1 1. <init> : Removed assignment to member variable eaConfiguration → SURVIVED
		this.eaConfiguration = _eaConfiguration;
71 2 1. <init> : Removed assignment to member variable virtualThreadExecutor → SURVIVED
2. <init> : removed call to java/util/concurrent/Executors::newVirtualThreadPerTaskExecutor → SURVIVED
		this.virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
72
	}
73
74
	@Override
75
	public void preEvaluation() {
76
		logger.debug("Pre-evaluation setup for virtual thread fitness evaluator");
77
	}
78
79
	@Override
80
	public void postEvaluation() {
81
		logger.debug("Post-evaluation cleanup for virtual thread fitness evaluator");
82
	}
83
84
	@Override
85
	public List<T> evaluate(final long generation, final List<Genotype> population) {
86
		Validate.isTrue(generation >= 0);
87
		Objects.requireNonNull(population);
88
89 4 1. evaluate : removed conditional - replaced equality check with false → NO_COVERAGE
2. evaluate : negated conditional → NO_COVERAGE
3. evaluate : removed conditional - replaced equality check with true → NO_COVERAGE
4. evaluate : removed call to java/util/List::isEmpty → NO_COVERAGE
		if (population.isEmpty()) {
90 2 1. evaluate : removed call to java/util/ArrayList::<init> → NO_COVERAGE
2. evaluate : replaced return value with Collections.emptyList for net/bmahe/genetics4j/core/evaluation/FitnessEvaluatorVirtualThread::evaluate → NO_COVERAGE
			return new ArrayList<>();
91
		}
92
93
		logger.debug("Evaluating {} individuals using virtual threads", population.size());
94
95 1 1. evaluate : removed call to net/bmahe/genetics4j/core/spec/EAConfiguration::fitness → NO_COVERAGE
		final Fitness<T> fitness = eaConfiguration.fitness();
96 2 1. evaluate : removed call to java/util/ArrayList::<init> → NO_COVERAGE
2. evaluate : removed call to java/util/List::size → NO_COVERAGE
		final List<CompletableFuture<T>> evaluationTasks = new ArrayList<>(population.size());
97
98
		// Create one virtual thread task per genotype - no partitioning needed
99
		for (final Genotype genotype : population) {
100 1 1. evaluate : removed call to java/util/concurrent/CompletableFuture::supplyAsync → NO_COVERAGE
			final CompletableFuture<T> evaluationTask = CompletableFuture.supplyAsync(() -> {
101 2 1. lambda$evaluate$0 : replaced return value with null for net/bmahe/genetics4j/core/evaluation/FitnessEvaluatorVirtualThread::lambda$evaluate$0 → NO_COVERAGE
2. lambda$evaluate$0 : removed call to net/bmahe/genetics4j/core/Fitness::compute → NO_COVERAGE
				return fitness.compute(genotype);
102
			}, virtualThreadExecutor);
103
104 1 1. evaluate : removed call to java/util/List::add → NO_COVERAGE
			evaluationTasks.add(evaluationTask);
105
		}
106
107
		// Wait for all evaluations to complete and collect results in order
108 2 1. evaluate : removed call to java/util/ArrayList::<init> → NO_COVERAGE
2. evaluate : removed call to java/util/List::size → NO_COVERAGE
		final List<T> fitnessScores = new ArrayList<>(population.size());
109
		for (final CompletableFuture<T> task : evaluationTasks) {
110 2 1. evaluate : removed call to java/util/List::add → NO_COVERAGE
2. evaluate : removed call to java/util/concurrent/CompletableFuture::join → NO_COVERAGE
			fitnessScores.add(task.join());
111
		}
112
113
		logger.debug("Completed evaluation of {} individuals", population.size());
114 1 1. evaluate : replaced return value with Collections.emptyList for net/bmahe/genetics4j/core/evaluation/FitnessEvaluatorVirtualThread::evaluate → NO_COVERAGE
		return fitnessScores;
115
	}
116
}

Mutations

69

1.1
Location : <init>
Killed by : none
Removed assignment to member variable eaExecutionContext → SURVIVED
Covering tests

70

1.1
Location : <init>
Killed by : none
Removed assignment to member variable eaConfiguration → SURVIVED
Covering tests

71

1.1
Location : <init>
Killed by : none
Removed assignment to member variable virtualThreadExecutor → SURVIVED
Covering tests

2.2
Location : <init>
Killed by : none
removed call to java/util/concurrent/Executors::newVirtualThreadPerTaskExecutor → SURVIVED Covering tests

89

1.1
Location : evaluate
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

2.2
Location : evaluate
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : evaluate
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

4.4
Location : evaluate
Killed by : none
removed call to java/util/List::isEmpty → NO_COVERAGE

90

1.1
Location : evaluate
Killed by : none
removed call to java/util/ArrayList::<init> → NO_COVERAGE

2.2
Location : evaluate
Killed by : none
replaced return value with Collections.emptyList for net/bmahe/genetics4j/core/evaluation/FitnessEvaluatorVirtualThread::evaluate → NO_COVERAGE

95

1.1
Location : evaluate
Killed by : none
removed call to net/bmahe/genetics4j/core/spec/EAConfiguration::fitness → NO_COVERAGE

96

1.1
Location : evaluate
Killed by : none
removed call to java/util/ArrayList::<init> → NO_COVERAGE

2.2
Location : evaluate
Killed by : none
removed call to java/util/List::size → NO_COVERAGE

100

1.1
Location : evaluate
Killed by : none
removed call to java/util/concurrent/CompletableFuture::supplyAsync → NO_COVERAGE

101

1.1
Location : lambda$evaluate$0
Killed by : none
replaced return value with null for net/bmahe/genetics4j/core/evaluation/FitnessEvaluatorVirtualThread::lambda$evaluate$0 → NO_COVERAGE

2.2
Location : lambda$evaluate$0
Killed by : none
removed call to net/bmahe/genetics4j/core/Fitness::compute → NO_COVERAGE

104

1.1
Location : evaluate
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

108

1.1
Location : evaluate
Killed by : none
removed call to java/util/ArrayList::<init> → NO_COVERAGE

2.2
Location : evaluate
Killed by : none
removed call to java/util/List::size → NO_COVERAGE

110

1.1
Location : evaluate
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

2.2
Location : evaluate
Killed by : none
removed call to java/util/concurrent/CompletableFuture::join → NO_COVERAGE

114

1.1
Location : evaluate
Killed by : none
replaced return value with Collections.emptyList for net/bmahe/genetics4j/core/evaluation/FitnessEvaluatorVirtualThread::evaluate → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.20.3