TournamentNSGA2Selection.java

package net.bmahe.genetics4j.moo.nsga2.spec;

import java.util.Comparator;
import java.util.Optional;
import java.util.function.Function;

import org.immutables.value.Value;

import net.bmahe.genetics4j.core.Genotype;
import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy;
import net.bmahe.genetics4j.moo.FitnessVector;
import net.bmahe.genetics4j.moo.ObjectiveDistance;

/**
 * Tournament based NSGA2 selection
 * <p>
 * This method of selection follows the method describe in the NSGA2 paper where
 * the individuals are sorted according to NSGA2 and then selected through
 * tournaments where they are compared based on their NSGA2 metric
 *
 * @param <T> Type of the fitness measurement
 */
@Value.Immutable
public abstract class TournamentNSGA2Selection<T extends Comparable<T>> implements SelectionPolicy {

	/**
	 * Describe how many objectives are embedded in T
	 * 
	 * @return Number of objectives embedded in T
	 */
	@Value.Parameter
	public abstract int numberObjectives();

	/**
	 * Override the dominance operator.
	 * <p>
	 * If not specified, it assumes the default comparator conforms to the Pareto
	 * dominance relation
	 * 
	 * @return
	 */
	@Value.Default
	public Comparator<T> dominance() {
		return (a, b) -> a.compareTo(b);
	}

	/**
	 * Comparator used for deduplication of solution prior to processing
	 * <p>
	 * If not specified, it defaults to not do any deduplication
	 * 
	 * @return
	 */
	@Value.Default
	public Optional<Comparator<Genotype>> deduplicate() {
		return Optional.empty();
	}

	/**
	 * Sort T based on the objective passed as a parameter
	 * 
	 * @return
	 */
	@Value.Parameter
	public abstract Function<Integer, Comparator<T>> objectiveComparator();

	/**
	 * Define how to compute distances between fitness scores along their objectives
	 * 
	 * @return Distance computation method
	 */
	@Value.Parameter
	public abstract ObjectiveDistance<T> distance();

	/**
	 * Number of candidates in each tournament
	 * 
	 * @return Number of candidates in each tournament
	 */
	@Value.Parameter
	public abstract int numCandidates();

	public static class Builder<T extends Comparable<T>> extends ImmutableTournamentNSGA2Selection.Builder<T> {
	}

	public static <U extends Comparable<U>> Builder<U> builder() {
		return new Builder<U>();
	}

	/**
	 * Factory method to instantiate a Tournament based NSGA2 selection when fitness
	 * is defined as a FitnessVector of a Number
	 * 
	 * @param <U>              Type of the fitness measurement
	 * @param numberObjectives Number of objectives and dimensions of the
	 *                         FitnessVector
	 * @param numberCandidates Number of candidates in each tournament
	 * @param deduplicate      Deduplicator comparator. Null value with disable
	 *                         deduplication
	 * @return A new instance of TournamentNSGA2Selection
	 */
	public static <U extends Number & Comparable<U>> TournamentNSGA2Selection<FitnessVector<U>> ofFitnessVector(
			final int numberObjectives, final int numberCandidates, final Comparator<Genotype> deduplicate) {

		final var builder = new Builder<FitnessVector<U>>();

		builder.objectiveComparator((m) -> (a, b) -> Double.compare(a.get(m).doubleValue(), b.get(m).doubleValue()))
				.distance((a, b, m) -> Math.abs(b.get(m).doubleValue() - a.get(m).doubleValue()))
				.numberObjectives(numberObjectives)
				.numCandidates(numberCandidates)
				.deduplicate(Optional.ofNullable(deduplicate));

		return builder.build();
	}

	/**
	 * Factory method to instantiate a Tournament based NSGA2 selection when fitness
	 * is defined as a FitnessVector of a Number
	 * 
	 * @param <U>              Type of the fitness measurement
	 * @param numberObjectives Number of objectives and dimensions of the
	 *                         FitnessVector
	 * @param numberCandidates Number of candidates in each tournament
	 * @return A new instance of TournamentNSGA2Selection
	 */
	public static <U extends Number & Comparable<U>> TournamentNSGA2Selection<FitnessVector<U>>
			ofFitnessVector(final int numberObjectives, final int numberCandidates) {

		return ofFitnessVector(numberObjectives, numberCandidates, null);
	}

}