| 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 2.2 |
|
| 105 |
1.1 2.2 |
|
| 120 |
1.1 |
|
| 121 |
1.1 |
|
| 136 |
1.1 |
|
| 150 |
1.1 |
|
| 165 |
1.1 |
|
| 177 |
1.1 2.2 |
|
| 189 |
1.1 2.2 |
|
| 201 |
1.1 |
|
| 213 |
1.1 |
|
| 225 |
1.1 |
|
| 236 |
1.1 2.2 |
|
| 237 |
1.1 2.2 |
|
| 238 |
1.1 2.2 3.3 |
|
| 239 |
1.1 2.2 |
|
| 240 |
1.1 2.2 3.3 4.4 5.5 |
|
| 241 |
1.1 2.2 |
|
| 248 |
1.1 2.2 3.3 |