Species.java

1
package net.bmahe.genetics4j.neat;
2
3
import java.util.ArrayList;
4
import java.util.Collection;
5
import java.util.List;
6
import java.util.Objects;
7
8
import org.apache.commons.lang3.Validate;
9
10
import net.bmahe.genetics4j.core.Individual;
11
12
/**
13
 * Represents a species in the NEAT (NeuroEvolution of Augmenting Topologies) algorithm.
14
 * 
15
 * <p>A Species groups together genetically similar individuals in the population, enabling
16
 * fitness sharing and diversity preservation in NEAT evolution. Species are formed based on
17
 * genetic compatibility distance, allowing individuals with similar network topologies to
18
 * compete within their own niche rather than with the entire population.
19
 * 
20
 * <p>Key characteristics:
21
 * <ul>
22
 * <li><strong>Genetic similarity</strong>: Members share similar network topologies and connection patterns</li>
23
 * <li><strong>Fitness sharing</strong>: Members compete primarily within their species for reproductive opportunities</li>
24
 * <li><strong>Diversity preservation</strong>: Protects innovative topologies from being eliminated by established forms</li>
25
 * <li><strong>Dynamic membership</strong>: Species composition changes as individuals evolve and compatibility shifts</li>
26
 * </ul>
27
 * 
28
 * <p>NEAT speciation process:
29
 * <ol>
30
 * <li><strong>Compatibility measurement</strong>: Calculate genetic distance between individuals</li>
31
 * <li><strong>Species assignment</strong>: Assign individuals to species based on distance thresholds</li>
32
 * <li><strong>Representative selection</strong>: Choose species representatives for compatibility testing</li>
33
 * <li><strong>Fitness sharing</strong>: Adjust individual fitness based on species membership size</li>
34
 * <li><strong>Reproduction allocation</strong>: Allocate offspring based on species average fitness</li>
35
 * </ol>
36
 * 
37
 * <p>Species lifecycle management:
38
 * <ul>
39
 * <li><strong>Formation</strong>: New species created when individuals exceed compatibility threshold</li>
40
 * <li><strong>Growth</strong>: Species gain members as similar individuals are assigned</li>
41
 * <li><strong>Stagnation</strong>: Species may stagnate if they fail to improve over generations</li>
42
 * <li><strong>Extinction</strong>: Species die out when they have no members or persistently poor performance</li>
43
 * </ul>
44
 * 
45
 * <p>Common usage patterns:
46
 * <pre>{@code
47
 * // Create new species with founding ancestors
48
 * List<Individual<Double>> founders = List.of(individual1, individual2);
49
 * Species<Double> species = new Species<>(42, founders);
50
 * 
51
 * // Add members during population assignment
52
 * species.addMember(similarIndividual1);
53
 * species.addMember(similarIndividual2);
54
 * species.addAllMembers(batchOfSimilarIndividuals);
55
 * 
56
 * // Access species information
57
 * int speciesId = species.getId();
58
 * int memberCount = species.getNumMembers();
59
 * List<Individual<Double>> allMembers = species.getMembers();
60
 * 
61
 * // Species-based fitness sharing
62
 * for (Individual<Double> member : species.getMembers()) {
63
 *     double sharedFitness = member.fitness() / species.getNumMembers();
64
 *     // Use shared fitness for selection
65
 * }
66
 * }</pre>
67
 * 
68
 * <p>Ancestor tracking:
69
 * <ul>
70
 * <li><strong>Species representatives</strong>: Ancestors serve as compatibility test references</li>
71
 * <li><strong>Historical continuity</strong>: Maintains connection to previous generations</li>
72
 * <li><strong>Stability</strong>: Prevents species boundaries from shifting too rapidly</li>
73
 * <li><strong>Representative selection</strong>: Best performers may become ancestors for next generation</li>
74
 * </ul>
75
 * 
76
 * <p>Fitness sharing mechanism:
77
 * <ul>
78
 * <li><strong>Within-species competition</strong>: Members primarily compete with each other</li>
79
 * <li><strong>Diversity protection</strong>: Prevents single topology from dominating population</li>
80
 * <li><strong>Innovation preservation</strong>: Allows new topologies time to optimize</li>
81
 * <li><strong>Niche exploitation</strong>: Different species can specialize for different aspects of the problem</li>
82
 * </ul>
83
 * 
84
 * <p>Integration with NEAT selection:
85
 * <ul>
86
 * <li><strong>Speciation</strong>: Used by NeatSelectionPolicyHandler for population organization</li>
87
 * <li><strong>Compatibility testing</strong>: Ancestors used as reference points for species assignment</li>
88
 * <li><strong>Reproduction allocation</strong>: Species size influences offspring distribution</li>
89
 * <li><strong>Population dynamics</strong>: Species creation, growth, and extinction drive population diversity</li>
90
 * </ul>
91
 * 
92
 * @param <T> the fitness value type (typically Double)
93
 * @see NeatSelectionPolicyHandler
94
 * @see SpeciesIdGenerator
95
 * @see NeatUtils#computeCompatibilityDistance
96
 * @see Individual
97
 */
98
public class Species<T extends Comparable<T>> {
99
100
	private final int id;
101 2 1. <init> : removed call to java/util/ArrayList::<init> → KILLED
2. <init> : Removed assignment to member variable ancestors → KILLED
	private final List<Individual<T>> ancestors = new ArrayList<>();
102 2 1. <init> : removed call to java/util/ArrayList::<init> → KILLED
2. <init> : Removed assignment to member variable members → KILLED
	private final List<Individual<T>> members = new ArrayList<>();
103
104
	/**
105
	 * Constructs a new species with the specified ID and founding ancestors.
106
	 * 
107
	 * <p>The ancestors serve as reference points for compatibility testing and represent
108
	 * the genetic heritage of the species. New individuals are tested against these
109
	 * ancestors to determine species membership.
110
	 * 
111
	 * @param _id unique identifier for this species
112
	 * @param _ancestors founding individuals that define the species genetic signature
113
	 * @throws IllegalArgumentException if ancestors is null
114
	 */
115
	public Species(final int _id, final List<Individual<T>> _ancestors) {
116
		Validate.notNull(_ancestors);
117
118 1 1. <init> : Removed assignment to member variable id → SURVIVED
		this.id = _id;
119 1 1. <init> : removed call to java/util/List::addAll → SURVIVED
		ancestors.addAll(_ancestors);
120
	}
121
122
	/**
123
	 * Adds an individual as an ancestor of this species.
124
	 * 
125
	 * <p>Ancestors serve as reference points for compatibility testing in subsequent
126
	 * generations. Typically, the best performers from a species may be promoted
127
	 * to ancestors to maintain species continuity.
128
	 * 
129
	 * @param individual the individual to add as an ancestor
130
	 * @throws IllegalArgumentException if individual is null
131
	 */
132
	public void addAncestor(final Individual<T> individual) {
133
		Validate.notNull(individual);
134
135 1 1. addAncestor : removed call to java/util/List::add → NO_COVERAGE
		ancestors.add(individual);
136
	}
137
138
	/**
139
	 * Adds an individual as a member of this species.
140
	 * 
141
	 * <p>Members are the current generation individuals that have been assigned to
142
	 * this species based on genetic compatibility. They participate in fitness
143
	 * sharing and species-based selection.
144
	 * 
145
	 * @param individual the individual to add as a member
146
	 * @throws IllegalArgumentException if individual is null
147
	 */
148
	public void addMember(final Individual<T> individual) {
149
		Validate.notNull(individual);
150 1 1. addMember : removed call to java/util/List::add → KILLED
		members.add(individual);
151
	}
152
153
	/**
154
	 * Adds multiple individuals as members of this species.
155
	 * 
156
	 * <p>This is a convenience method for bulk assignment of compatible individuals
157
	 * to the species. All individuals in the collection will participate in
158
	 * fitness sharing within this species.
159
	 * 
160
	 * @param individuals collection of individuals to add as members
161
	 * @throws IllegalArgumentException if individuals is null
162
	 */
163
	public void addAllMembers(final Collection<Individual<T>> individuals) {
164
		Validate.notNull(individuals);
165
166 1 1. addAllMembers : removed call to java/util/List::addAll → KILLED
		members.addAll(individuals);
167
	}
168
169
	/**
170
	 * Returns the number of ancestors in this species.
171
	 * 
172
	 * <p>Ancestors serve as reference points for compatibility testing and represent
173
	 * the species genetic heritage from previous generations.
174
	 * 
175
	 * @return the number of ancestors
176
	 */
177
	public int getNumAncestors() {
178 2 1. getNumAncestors : removed call to java/util/List::size → NO_COVERAGE
2. getNumAncestors : replaced int return with 0 for net/bmahe/genetics4j/neat/Species::getNumAncestors → NO_COVERAGE
		return ancestors.size();
179
	}
180
181
	/**
182
	 * Returns the number of current members in this species.
183
	 * 
184
	 * <p>The member count is used for fitness sharing calculations and reproduction
185
	 * allocation. Larger species will have their members' fitness values adjusted
186
	 * downward to prevent single species from dominating the population.
187
	 * 
188
	 * @return the number of current members
189
	 */
190
	public int getNumMembers() {
191 2 1. getNumMembers : removed call to java/util/List::size → KILLED
2. getNumMembers : replaced int return with 0 for net/bmahe/genetics4j/neat/Species::getNumMembers → KILLED
		return members.size();
192
	}
193
194
	/**
195
	 * Returns the unique identifier for this species.
196
	 * 
197
	 * <p>Species IDs are typically assigned by a SpeciesIdGenerator and remain
198
	 * constant throughout the species lifecycle.
199
	 * 
200
	 * @return the species unique identifier
201
	 */
202
	public int getId() {
203 1 1. getId : replaced int return with 0 for net/bmahe/genetics4j/neat/Species::getId → SURVIVED
		return id;
204
	}
205
206
	/**
207
	 * Returns the list of ancestors for this species.
208
	 * 
209
	 * <p>Ancestors are reference individuals used for compatibility testing
210
	 * when assigning new individuals to species. The returned list is mutable
211
	 * and modifications will affect the species behavior.
212
	 * 
213
	 * @return mutable list of ancestor individuals
214
	 */
215
	public List<Individual<T>> getAncestors() {
216 1 1. getAncestors : replaced return value with Collections.emptyList for net/bmahe/genetics4j/neat/Species::getAncestors → SURVIVED
		return ancestors;
217
	}
218
219
	/**
220
	 * Returns the list of current members in this species.
221
	 * 
222
	 * <p>Members are the current generation individuals that participate in
223
	 * fitness sharing and species-based selection. The returned list is mutable
224
	 * and modifications will affect species membership.
225
	 * 
226
	 * @return mutable list of member individuals
227
	 */
228
	public List<Individual<T>> getMembers() {
229 1 1. getMembers : replaced return value with Collections.emptyList for net/bmahe/genetics4j/neat/Species::getMembers → KILLED
		return members;
230
	}
231
232
	@Override
233
	public int hashCode() {
234
		return Objects.hash(ancestors, id, members);
235
	}
236
237
	// Should it be id only?
238
	@Override
239
	public boolean equals(Object obj) {
240 2 1. equals : removed conditional - replaced equality check with true → NO_COVERAGE
2. equals : negated conditional → NO_COVERAGE
		if (this == obj)
241 2 1. equals : Substituted 1 with 0 → NO_COVERAGE
2. equals : replaced boolean return with false for net/bmahe/genetics4j/neat/Species::equals → NO_COVERAGE
			return true;
242 3 1. equals : negated conditional → NO_COVERAGE
2. equals : removed conditional - replaced equality check with true → NO_COVERAGE
3. equals : removed conditional - replaced equality check with false → NO_COVERAGE
		if (obj == null)
243 2 1. equals : replaced boolean return with true for net/bmahe/genetics4j/neat/Species::equals → NO_COVERAGE
2. equals : Substituted 0 with 1 → NO_COVERAGE
			return false;
244 5 1. equals : negated conditional → NO_COVERAGE
2. equals : removed call to java/lang/Object::getClass → NO_COVERAGE
3. equals : removed call to java/lang/Object::getClass → NO_COVERAGE
4. equals : removed conditional - replaced equality check with false → NO_COVERAGE
5. equals : removed conditional - replaced equality check with true → NO_COVERAGE
		if (getClass() != obj.getClass())
245 2 1. equals : Substituted 0 with 1 → NO_COVERAGE
2. equals : replaced boolean return with true for net/bmahe/genetics4j/neat/Species::equals → NO_COVERAGE
			return false;
246
		Species other = (Species) obj;
247
		return Objects.equals(ancestors, other.ancestors) && id == other.id && Objects.equals(members, other.members);
248
	}
249
250
	@Override
251
	public String toString() {
252 3 1. toString : removed call to java/lang/String::valueOf → SURVIVED
2. toString : replaced return value with "" for net/bmahe/genetics4j/neat/Species::toString → SURVIVED
3. toString : removed call to java/lang/String::valueOf → SURVIVED
		return "Species [id=" + id + ", ancestors=" + ancestors + ", members=" + members + "]";
253
	}
254
}

Mutations

101

1.1
Location : <init>
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
removed call to java/util/ArrayList::<init> → KILLED

2.2
Location : <init>
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
Removed assignment to member variable ancestors → KILLED

102

1.1
Location : <init>
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
removed call to java/util/ArrayList::<init> → KILLED

2.2
Location : <init>
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
Removed assignment to member variable members → KILLED

118

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

119

1.1
Location : <init>
Killed by : none
removed call to java/util/List::addAll → SURVIVED
Covering tests

135

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

150

1.1
Location : addMember
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:select()]
removed call to java/util/List::add → KILLED

166

1.1
Location : addAllMembers
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
removed call to java/util/List::addAll → KILLED

178

1.1
Location : getNumAncestors
Killed by : none
removed call to java/util/List::size → NO_COVERAGE

2.2
Location : getNumAncestors
Killed by : none
replaced int return with 0 for net/bmahe/genetics4j/neat/Species::getNumAncestors → NO_COVERAGE

191

1.1
Location : getNumMembers
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
removed call to java/util/List::size → KILLED

2.2
Location : getNumMembers
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
replaced int return with 0 for net/bmahe/genetics4j/neat/Species::getNumMembers → KILLED

203

1.1
Location : getId
Killed by : none
replaced int return with 0 for net/bmahe/genetics4j/neat/Species::getId → SURVIVED
Covering tests

216

1.1
Location : getAncestors
Killed by : none
replaced return value with Collections.emptyList for net/bmahe/genetics4j/neat/Species::getAncestors → SURVIVED
Covering tests

229

1.1
Location : getMembers
Killed by : net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest.[engine:junit-jupiter]/[class:net.bmahe.genetics4j.neat.selection.NeatSelectorImplTest]/[method:eliminateLowestPerformers()]
replaced return value with Collections.emptyList for net/bmahe/genetics4j/neat/Species::getMembers → KILLED

240

1.1
Location : equals
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

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

241

1.1
Location : equals
Killed by : none
Substituted 1 with 0 → NO_COVERAGE

2.2
Location : equals
Killed by : none
replaced boolean return with false for net/bmahe/genetics4j/neat/Species::equals → NO_COVERAGE

242

1.1
Location : equals
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : equals
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

3.3
Location : equals
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

243

1.1
Location : equals
Killed by : none
replaced boolean return with true for net/bmahe/genetics4j/neat/Species::equals → NO_COVERAGE

2.2
Location : equals
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

244

1.1
Location : equals
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : equals
Killed by : none
removed call to java/lang/Object::getClass → NO_COVERAGE

3.3
Location : equals
Killed by : none
removed call to java/lang/Object::getClass → NO_COVERAGE

4.4
Location : equals
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

5.5
Location : equals
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

245

1.1
Location : equals
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

2.2
Location : equals
Killed by : none
replaced boolean return with true for net/bmahe/genetics4j/neat/Species::equals → NO_COVERAGE

252

1.1
Location : toString
Killed by : none
removed call to java/lang/String::valueOf → SURVIVED
Covering tests

2.2
Location : toString
Killed by : none
replaced return value with "" for net/bmahe/genetics4j/neat/Species::toString → SURVIVED Covering tests

3.3
Location : toString
Killed by : none
removed call to java/lang/String::valueOf → SURVIVED Covering tests

Active mutators

Tests examined


Report generated by PIT 1.19.6