View Javadoc
1   package net.bmahe.genetics4j.gp.spec.selection;
2   
3   import java.util.Comparator;
4   
5   import org.apache.commons.lang3.Validate;
6   import org.immutables.value.Value;
7   
8   import net.bmahe.genetics4j.core.Individual;
9   import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy;
10  import net.bmahe.genetics4j.core.spec.selection.Tournament;
11  
12  /**
13   * Double tournament selection strategy that combines fitness-based and parsimony-based selection to control bloat in
14   * genetic programming and other evolutionary algorithms.
15   *
16   * <p>Double tournament selection operates in two modes:
17   * <ul>
18   * <li><b>Fitness First Mode</b> (default): Performs two independent fitness tournaments, then applies parsimony
19   * selection between the winners to select the less complex individual</li>
20   * <li><b>Parsimony First Mode</b>: For each tournament candidate, performs parsimony selection on random pairs first,
21   * then selects the fittest among the parsimony winners</li>
22   * </ul>
23   *
24   * <p>The parsimony tournament uses a probabilistic approach where the parsimony tournament size parameter controls the
25   * selection pressure toward less complex individuals:
26   * <ul>
27   * <li>Size 0.0: Always selects randomly (no parsimony pressure)</li>
28   * <li>Size 1.0: Balanced selection between complexity preferences</li>
29   * <li>Size 2.0: Always selects the less complex individual</li>
30   * </ul>
31   *
32   * <p>This selection method is particularly effective in genetic programming where it helps prevent code bloat while
33   * maintaining competitive fitness levels.
34   *
35   * @param <T> the fitness type, must be Comparable
36   *
37   * @see DoubleTournamentSelector
38   * @see Tournament
39   */
40  @Value.Immutable
41  public abstract class DoubleTournament<T extends Comparable<T>> implements SelectionPolicy {
42  
43  	/**
44  	 * The fitness tournament configuration used for selecting individuals based on fitness. This tournament determines
45  	 * how many candidates compete in each fitness-based selection round.
46  	 *
47  	 * @return the tournament configuration for fitness-based selection
48  	 */
49  	@Value.Parameter
50  	public abstract Tournament<T> fitnessTournament();
51  
52  	/**
53  	 * Comparator used to evaluate parsimony (complexity) between individuals. Typically compares individuals based on
54  	 * size, depth, or other complexity metrics. The comparator should return:
55  	 * <ul>
56  	 * <li>Negative value if first individual is less complex than second</li>
57  	 * <li>Zero if both individuals have equal complexity</li>
58  	 * <li>Positive value if first individual is more complex than second</li>
59  	 * </ul>
60  	 *
61  	 * @return the comparator for evaluating individual complexity
62  	 */
63  	@Value.Parameter
64  	public abstract Comparator<Individual<T>> parsimonyComparator();
65  
66  	/**
67  	 * Controls the selection pressure toward less complex individuals in parsimony tournaments. Must be between 0.0 and
68  	 * 2.0 inclusive.
69  	 * <ul>
70  	 * <li>0.0: No parsimony pressure, selection is random</li>
71  	 * <li>1.0: Balanced parsimony pressure</li>
72  	 * <li>2.0: Maximum parsimony pressure, always selects less complex individual</li>
73  	 * </ul>
74  	 *
75  	 * @return the parsimony tournament size parameter
76  	 */
77  	@Value.Parameter
78  	public abstract double parsimonyTournamentSize();
79  
80  	/**
81  	 * Determines the order of selection operations.
82  	 * <ul>
83  	 * <li>{@code true} (default): Fitness first mode - perform fitness tournaments first, then apply parsimony selection
84  	 * between winners</li>
85  	 * <li>{@code false}: Parsimony first mode - apply parsimony selection to random pairs first, then perform fitness
86  	 * tournament among parsimony winners</li>
87  	 * </ul>
88  	 *
89  	 * @return true for fitness-first mode, false for parsimony-first mode
90  	 */
91  	@Value.Default
92  	public boolean doFitnessFirst() {
93  		return true;
94  	}
95  
96  	/**
97  	 * Validates that the parsimony tournament size is within the allowed range [0.0, 2.0].
98  	 *
99  	 * @throws IllegalArgumentException if parsimony tournament size is outside valid range
100 	 */
101 	@Value.Check
102 	public void check() {
103 		Validate.inclusiveBetween(0.0d, 2.0d, parsimonyTournamentSize());
104 	}
105 
106 	/**
107 	 * Creates a new DoubleTournament selection policy with fitness-first mode enabled.
108 	 *
109 	 * @param <U>                     the fitness type, must be Comparable
110 	 * @param fitnessTournament       the tournament configuration for fitness-based selection
111 	 * @param parsimonyComparator     comparator for evaluating individual complexity
112 	 * @param parsimonyTournamentSize selection pressure parameter, must be between 0.0 and 2.0
113 	 * @return a new DoubleTournament instance with the specified parameters
114 	 * @throws IllegalArgumentException if parsimony tournament size is outside valid range
115 	 */
116 	public static <U extends Comparable<U>> DoubleTournament<U> of(final Tournament<U> fitnessTournament,
117 			final Comparator<Individual<U>> parsimonyComparator, final double parsimonyTournamentSize) {
118 		return ImmutableDoubleTournament.of(fitnessTournament, parsimonyComparator, parsimonyTournamentSize);
119 	}
120 }