| 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 2.2 |
|
| 103 |
1.1 2.2 |
|
| 118 |
1.1 |
|
| 119 |
1.1 |
|
| 134 |
1.1 |
|
| 148 |
1.1 |
|
| 163 |
1.1 |
|
| 175 |
1.1 2.2 |
|
| 187 |
1.1 2.2 |
|
| 199 |
1.1 |
|
| 211 |
1.1 |
|
| 223 |
1.1 |
|
| 234 |
1.1 2.2 |
|
| 235 |
1.1 2.2 |
|
| 237 |
1.1 2.2 3.3 |
|
| 238 |
1.1 2.2 |
|
| 240 |
1.1 2.2 3.3 4.4 5.5 |
|
| 241 |
1.1 2.2 |
|
| 249 |
1.1 2.2 3.3 |