Species.java
package net.bmahe.genetics4j.neat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.Validate;
import net.bmahe.genetics4j.core.Individual;
/**
* Represents a species in the NEAT (NeuroEvolution of Augmenting Topologies) algorithm.
*
* <p>A Species groups together genetically similar individuals in the population, enabling
* fitness sharing and diversity preservation in NEAT evolution. Species are formed based on
* genetic compatibility distance, allowing individuals with similar network topologies to
* compete within their own niche rather than with the entire population.
*
* <p>Key characteristics:
* <ul>
* <li><strong>Genetic similarity</strong>: Members share similar network topologies and connection patterns</li>
* <li><strong>Fitness sharing</strong>: Members compete primarily within their species for reproductive opportunities</li>
* <li><strong>Diversity preservation</strong>: Protects innovative topologies from being eliminated by established forms</li>
* <li><strong>Dynamic membership</strong>: Species composition changes as individuals evolve and compatibility shifts</li>
* </ul>
*
* <p>NEAT speciation process:
* <ol>
* <li><strong>Compatibility measurement</strong>: Calculate genetic distance between individuals</li>
* <li><strong>Species assignment</strong>: Assign individuals to species based on distance thresholds</li>
* <li><strong>Representative selection</strong>: Choose species representatives for compatibility testing</li>
* <li><strong>Fitness sharing</strong>: Adjust individual fitness based on species membership size</li>
* <li><strong>Reproduction allocation</strong>: Allocate offspring based on species average fitness</li>
* </ol>
*
* <p>Species lifecycle management:
* <ul>
* <li><strong>Formation</strong>: New species created when individuals exceed compatibility threshold</li>
* <li><strong>Growth</strong>: Species gain members as similar individuals are assigned</li>
* <li><strong>Stagnation</strong>: Species may stagnate if they fail to improve over generations</li>
* <li><strong>Extinction</strong>: Species die out when they have no members or persistently poor performance</li>
* </ul>
*
* <p>Common usage patterns:
* <pre>{@code
* // Create new species with founding ancestors
* List<Individual<Double>> founders = List.of(individual1, individual2);
* Species<Double> species = new Species<>(42, founders);
*
* // Add members during population assignment
* species.addMember(similarIndividual1);
* species.addMember(similarIndividual2);
* species.addAllMembers(batchOfSimilarIndividuals);
*
* // Access species information
* int speciesId = species.getId();
* int memberCount = species.getNumMembers();
* List<Individual<Double>> allMembers = species.getMembers();
*
* // Species-based fitness sharing
* for (Individual<Double> member : species.getMembers()) {
* double sharedFitness = member.fitness() / species.getNumMembers();
* // Use shared fitness for selection
* }
* }</pre>
*
* <p>Ancestor tracking:
* <ul>
* <li><strong>Species representatives</strong>: Ancestors serve as compatibility test references</li>
* <li><strong>Historical continuity</strong>: Maintains connection to previous generations</li>
* <li><strong>Stability</strong>: Prevents species boundaries from shifting too rapidly</li>
* <li><strong>Representative selection</strong>: Best performers may become ancestors for next generation</li>
* </ul>
*
* <p>Fitness sharing mechanism:
* <ul>
* <li><strong>Within-species competition</strong>: Members primarily compete with each other</li>
* <li><strong>Diversity protection</strong>: Prevents single topology from dominating population</li>
* <li><strong>Innovation preservation</strong>: Allows new topologies time to optimize</li>
* <li><strong>Niche exploitation</strong>: Different species can specialize for different aspects of the problem</li>
* </ul>
*
* <p>Integration with NEAT selection:
* <ul>
* <li><strong>Speciation</strong>: Used by NeatSelectionPolicyHandler for population organization</li>
* <li><strong>Compatibility testing</strong>: Ancestors used as reference points for species assignment</li>
* <li><strong>Reproduction allocation</strong>: Species size influences offspring distribution</li>
* <li><strong>Population dynamics</strong>: Species creation, growth, and extinction drive population diversity</li>
* </ul>
*
* @param <T> the fitness value type (typically Double)
* @see NeatSelectionPolicyHandler
* @see SpeciesIdGenerator
* @see NeatUtils#computeCompatibilityDistance
* @see Individual
*/
public class Species<T extends Comparable<T>> {
private final int id;
private final List<Individual<T>> ancestors = new ArrayList<>();
private final List<Individual<T>> members = new ArrayList<>();
/**
* Constructs a new species with the specified ID and founding ancestors.
*
* <p>The ancestors serve as reference points for compatibility testing and represent
* the genetic heritage of the species. New individuals are tested against these
* ancestors to determine species membership.
*
* @param _id unique identifier for this species
* @param _ancestors founding individuals that define the species genetic signature
* @throws IllegalArgumentException if ancestors is null
*/
public Species(final int _id, final List<Individual<T>> _ancestors) {
Validate.notNull(_ancestors);
this.id = _id;
ancestors.addAll(_ancestors);
}
/**
* Adds an individual as an ancestor of this species.
*
* <p>Ancestors serve as reference points for compatibility testing in subsequent
* generations. Typically, the best performers from a species may be promoted
* to ancestors to maintain species continuity.
*
* @param individual the individual to add as an ancestor
* @throws IllegalArgumentException if individual is null
*/
public void addAncestor(final Individual<T> individual) {
Validate.notNull(individual);
ancestors.add(individual);
}
/**
* Adds an individual as a member of this species.
*
* <p>Members are the current generation individuals that have been assigned to
* this species based on genetic compatibility. They participate in fitness
* sharing and species-based selection.
*
* @param individual the individual to add as a member
* @throws IllegalArgumentException if individual is null
*/
public void addMember(final Individual<T> individual) {
Validate.notNull(individual);
members.add(individual);
}
/**
* Adds multiple individuals as members of this species.
*
* <p>This is a convenience method for bulk assignment of compatible individuals
* to the species. All individuals in the collection will participate in
* fitness sharing within this species.
*
* @param individuals collection of individuals to add as members
* @throws IllegalArgumentException if individuals is null
*/
public void addAllMembers(final Collection<Individual<T>> individuals) {
Validate.notNull(individuals);
members.addAll(individuals);
}
/**
* Returns the number of ancestors in this species.
*
* <p>Ancestors serve as reference points for compatibility testing and represent
* the species genetic heritage from previous generations.
*
* @return the number of ancestors
*/
public int getNumAncestors() {
return ancestors.size();
}
/**
* Returns the number of current members in this species.
*
* <p>The member count is used for fitness sharing calculations and reproduction
* allocation. Larger species will have their members' fitness values adjusted
* downward to prevent single species from dominating the population.
*
* @return the number of current members
*/
public int getNumMembers() {
return members.size();
}
/**
* Returns the unique identifier for this species.
*
* <p>Species IDs are typically assigned by a SpeciesIdGenerator and remain
* constant throughout the species lifecycle.
*
* @return the species unique identifier
*/
public int getId() {
return id;
}
/**
* Returns the list of ancestors for this species.
*
* <p>Ancestors are reference individuals used for compatibility testing
* when assigning new individuals to species. The returned list is mutable
* and modifications will affect the species behavior.
*
* @return mutable list of ancestor individuals
*/
public List<Individual<T>> getAncestors() {
return ancestors;
}
/**
* Returns the list of current members in this species.
*
* <p>Members are the current generation individuals that participate in
* fitness sharing and species-based selection. The returned list is mutable
* and modifications will affect species membership.
*
* @return mutable list of member individuals
*/
public List<Individual<T>> getMembers() {
return members;
}
@Override
public int hashCode() {
return Objects.hash(ancestors, id, members);
}
// Should it be id only?
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Species other = (Species) obj;
return Objects.equals(ancestors, other.ancestors) && id == other.id && Objects.equals(members, other.members);
}
@Override
public String toString() {
return "Species [id=" + id + ", ancestors=" + ancestors + ", members=" + members + "]";
}
}