NSGA2Selection.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;

/**
 * NSGA2 Selection specification
 * <p>
 * Select individuals based on their NSGA2 score, going from the most dominating
 * ones to the lesser ones
 * 
 * @param <T> Type of the fitness measurement
 */
@Value.Immutable
public abstract class NSGA2Selection<T extends Comparable<T>> implements SelectionPolicy {

	/**
	 * Number of objectives
	 * 
	 * @return
	 */
	@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();

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

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

	/**
	 * Factory method to instantiate a NSGA2Selection 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 deduplicate      Deduplicator comparator. Null value with disable
	 *                         deduplication
	 * @return A new instance of NSGA2Selection
	 */
	public static <U extends Number & Comparable<U>> NSGA2Selection<FitnessVector<U>>
			ofFitnessVector(final int numberObjectives, 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)
				.deduplicate(Optional.ofNullable(deduplicate));

		return builder.build();
	}

	/**
	 * Factory method to instantiate a NSGA2Selection 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
	 * @return A new instance of NSGA2Selection
	 */
	public static <U extends Number & Comparable<U>> NSGA2Selection<FitnessVector<U>>
			ofFitnessVector(final int numberObjectives) {

		return ofFitnessVector(numberObjectives, null);
	}
}