DoubleTournament.java
package net.bmahe.genetics4j.gp.spec.selection;
import java.util.Comparator;
import org.apache.commons.lang3.Validate;
import org.immutables.value.Value;
import net.bmahe.genetics4j.core.Individual;
import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy;
import net.bmahe.genetics4j.core.spec.selection.Tournament;
/**
* Double tournament selection strategy that combines fitness-based and parsimony-based selection to control bloat in
* genetic programming and other evolutionary algorithms.
*
* <p>Double tournament selection operates in two modes:
* <ul>
* <li><b>Fitness First Mode</b> (default): Performs two independent fitness tournaments, then applies parsimony
* selection between the winners to select the less complex individual</li>
* <li><b>Parsimony First Mode</b>: For each tournament candidate, performs parsimony selection on random pairs first,
* then selects the fittest among the parsimony winners</li>
* </ul>
*
* <p>The parsimony tournament uses a probabilistic approach where the parsimony tournament size parameter controls the
* selection pressure toward less complex individuals:
* <ul>
* <li>Size 0.0: Always selects randomly (no parsimony pressure)</li>
* <li>Size 1.0: Balanced selection between complexity preferences</li>
* <li>Size 2.0: Always selects the less complex individual</li>
* </ul>
*
* <p>This selection method is particularly effective in genetic programming where it helps prevent code bloat while
* maintaining competitive fitness levels.
*
* @param <T> the fitness type, must be Comparable
*
* @see DoubleTournamentSelector
* @see Tournament
*/
@Value.Immutable
public abstract class DoubleTournament<T extends Comparable<T>> implements SelectionPolicy {
/**
* The fitness tournament configuration used for selecting individuals based on fitness. This tournament determines
* how many candidates compete in each fitness-based selection round.
*
* @return the tournament configuration for fitness-based selection
*/
@Value.Parameter
public abstract Tournament<T> fitnessTournament();
/**
* Comparator used to evaluate parsimony (complexity) between individuals. Typically compares individuals based on
* size, depth, or other complexity metrics. The comparator should return:
* <ul>
* <li>Negative value if first individual is less complex than second</li>
* <li>Zero if both individuals have equal complexity</li>
* <li>Positive value if first individual is more complex than second</li>
* </ul>
*
* @return the comparator for evaluating individual complexity
*/
@Value.Parameter
public abstract Comparator<Individual<T>> parsimonyComparator();
/**
* Controls the selection pressure toward less complex individuals in parsimony tournaments. Must be between 0.0 and
* 2.0 inclusive.
* <ul>
* <li>0.0: No parsimony pressure, selection is random</li>
* <li>1.0: Balanced parsimony pressure</li>
* <li>2.0: Maximum parsimony pressure, always selects less complex individual</li>
* </ul>
*
* @return the parsimony tournament size parameter
*/
@Value.Parameter
public abstract double parsimonyTournamentSize();
/**
* Determines the order of selection operations.
* <ul>
* <li>{@code true} (default): Fitness first mode - perform fitness tournaments first, then apply parsimony selection
* between winners</li>
* <li>{@code false}: Parsimony first mode - apply parsimony selection to random pairs first, then perform fitness
* tournament among parsimony winners</li>
* </ul>
*
* @return true for fitness-first mode, false for parsimony-first mode
*/
@Value.Default
public boolean doFitnessFirst() {
return true;
}
/**
* Validates that the parsimony tournament size is within the allowed range [0.0, 2.0].
*
* @throws IllegalArgumentException if parsimony tournament size is outside valid range
*/
@Value.Check
public void check() {
Validate.inclusiveBetween(0.0d, 2.0d, parsimonyTournamentSize());
}
/**
* Creates a new DoubleTournament selection policy with fitness-first mode enabled.
*
* @param <U> the fitness type, must be Comparable
* @param fitnessTournament the tournament configuration for fitness-based selection
* @param parsimonyComparator comparator for evaluating individual complexity
* @param parsimonyTournamentSize selection pressure parameter, must be between 0.0 and 2.0
* @return a new DoubleTournament instance with the specified parameters
* @throws IllegalArgumentException if parsimony tournament size is outside valid range
*/
public static <U extends Comparable<U>> DoubleTournament<U> of(final Tournament<U> fitnessTournament,
final Comparator<Individual<U>> parsimonyComparator, final double parsimonyTournamentSize) {
return ImmutableDoubleTournament.of(fitnessTournament, parsimonyComparator, parsimonyTournamentSize);
}
}