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

Mutations

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 ancestors → KILLED

103

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

134

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

148

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

163

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

175

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

187

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

199

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

211

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

223

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

234

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

235

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

237

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

238

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

240

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

241

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

249

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.25.7 support