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

Mutations

104

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

105

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

120

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

121

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

136

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

165

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

177

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

189

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

201

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

213

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

225

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

236

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

237

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

238

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

239

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

248

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.20.3