Skip to content

Commit

Permalink
Added unit tests for ParetoSet implementations (#270)
Browse files Browse the repository at this point in the history
* Changed type of parameter received in methods dedicated to generate lists of TestMove objects, since variable score is a double.

* Added two classes that are useful to implement unit tests in multi-objective contexts. TestSolutionWithMultipleObjectives is similar to TestSolution, but can have more than one objective. TestMoveWithMultipleObjectives is similar to TestMove, but can have more than one objective.

* Changed hashCode method to be consistent with equals.

* Added unit tests for ParetoSimpleList class.

* Removed unnecessary line.

* Added unit tests for ParetoByFirstObjective and NDTree.

* Added MOAlgorithm, needed to define a generic algorithm that returns a ParetoSet instead of a single solution.
  • Loading branch information
JavierYuste authored and rmartinsanta committed Nov 23, 2024
1 parent c199942 commit 75a904b
Show file tree
Hide file tree
Showing 7 changed files with 890 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package es.urjc.etsii.grafo.algorithms.mo;

import es.urjc.etsii.grafo.annotations.AlgorithmComponent;
import es.urjc.etsii.grafo.create.builder.SolutionBuilder;
import es.urjc.etsii.grafo.io.Instance;
import es.urjc.etsii.grafo.mo.pareto.ParetoSet;
import es.urjc.etsii.grafo.solution.Solution;

import java.util.Objects;


/**
* Base algorithm class, all algorithms should extend this class or any of its subclasses.
*
* @param <S> Solution class
* @param <I> Instance class
*/
@AlgorithmComponent
public abstract class MOAlgorithm<S extends Solution<S,I>, I extends Instance> {

private SolutionBuilder<S,I> builder;
private String algorithmName;

/**
* Initialize common algorithm fields
* @param algorithmName algorithm name. See {@link #setName(String)}
*/
protected MOAlgorithm(String algorithmName) {
this.algorithmName = algorithmName;
}

/**
* Algorithm name, uniquely identifies the algorithm inside an experiment
*
* @return algorithm name
*/
public String getName(){
return this.algorithmName;
}

/**
* Sets the algorithm name.
* This method can be called after the algorithm has been built,
* for example if we want to customize the name generated by the autoconfig module.
* This method CANNOT be called after the algorithm has started executing, as it would break the experiment results.
* The algorithm name is used to uniquely identify the algorithm inside an experiment.
*
* @param algorithmName must uniquely identify the algorithm inside an experiment
*/
public void setName(String algorithmName){
this.algorithmName = algorithmName;
}

/**
* Runs the algorithm
*
* @param instance Instance to solve
* @return Built solution
*/
public abstract ParetoSet<S, I> algorithm(I instance);

/**
* Create a new solution for the given instance. Solution is empty by default.
*
* @param instance Instance
* @return Empty solution, by default created calling the constructor Solution(Instance i)
*/
public S newSolution(I instance){
return this.builder.initializeSolution(instance);
}

/**
* Get solution builder
*
* @return solution builder
*/
protected SolutionBuilder<S, I> getBuilder() {
return builder;
}

/**
* Set solution builder, used by the framework.
* In case an algorithms contains another algorithms, this method should be overridden as follows:
* <pre>
* &#64;Override
* public void setBuilder(SolutionBuilder&#60;S, I&#62; builder) {
* super.setBuilder(builder);
* this.algorithm.setBuilder(builder);
* }
* </pre>
*
* @param builder solution builder
*/
public void setBuilder(SolutionBuilder<S, I> builder) {
this.builder = Objects.requireNonNull(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ public class TestMove extends Move<TestSolution, TestInstance> {
private final double score;
private final FMode fmode;

public static List<TestMove> generateSeq(int... data){
public static List<TestMove> generateSeq(double... data){
return generateSeq("TestInstance", data);
}

public static List<TestMove> generateSeq(String instanceName, int... data){
public static List<TestMove> generateSeq(String instanceName, double... data){
var testInstance = new TestInstance(instanceName);
var testSolution = new TestSolution(testInstance);
var moves = new ArrayList<TestMove>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package es.urjc.etsii.grafo.testutil;

import es.urjc.etsii.grafo.algorithms.FMode;
import es.urjc.etsii.grafo.solution.Move;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class TestMoveWithMultipleObjectives extends Move<TestSolutionWithMultipleObjectives, TestInstance> {
private final double[] score;
private final FMode[] fmode;

public static List<TestMoveWithMultipleObjectives> generateSeq(double[]... data){
return generateSeq("TestInstance", data);
}

public static List<TestMoveWithMultipleObjectives> generateSeq(String instanceName, double[]... data){
var testInstance = new TestInstance(instanceName);
var testSolution = new TestSolutionWithMultipleObjectives(testInstance);
var moves = new ArrayList<TestMoveWithMultipleObjectives>();
for(var i: data){
moves.add(new TestMoveWithMultipleObjectives(testSolution, i));
}
return moves;
}

public TestMoveWithMultipleObjectives(TestSolutionWithMultipleObjectives solution, double[] score, FMode[] fmode) {
super(solution);
this.score = score;
this.fmode = fmode;
}

public TestMoveWithMultipleObjectives(TestSolutionWithMultipleObjectives solution, double[] v) {
super(solution);
this.score = v;
this.fmode = new FMode[v.length];
for (int i = 0; i < v.length; i++) {
fmode[i] = FMode.MINIMIZE;
}
}

@Override
protected TestSolutionWithMultipleObjectives _execute(TestSolutionWithMultipleObjectives solution) {
int i = 0;
for (double v : score) {
solution.scores[i] += this.score[i];
i++;
}
return solution;
}

public double[] getScoreChanges() {
return score;
}

@Override
public String toString() {
return "TestMove{" +
"score=" + score +
", maximizing=" + fmode +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestMoveWithMultipleObjectives testMove = (TestMoveWithMultipleObjectives) o;
if (this.score.length != testMove.score.length) {
return false;
}
for (int i = 0; i < this.score.length; i++) {
if (Double.compare(testMove.score[i], score[i]) != 0 && fmode[i] != testMove.fmode[i]){
return false;
}
}
return true;
}

@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(score), Arrays.hashCode(fmode));
}

public boolean isMaximizing(int i) {
return fmode.length > i && fmode[i] == FMode.MAXIMIZE;
}

public boolean isMinimizing(int i) {
return fmode.length > i && fmode[i] == FMode.MINIMIZE;
}

public FMode[] getFmode() {
return fmode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package es.urjc.etsii.grafo.testutil;

import es.urjc.etsii.grafo.solution.Solution;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

public class TestSolutionWithMultipleObjectives extends Solution<TestSolutionWithMultipleObjectives, TestInstance>{

protected double[] scores;

public static TestSolutionWithMultipleObjectives[] from(double... scores) {
return from(new TestInstance("TestInstance"), scores);
}

public static TestSolutionWithMultipleObjectives[] from(TestInstance instance, double... scores) {
var solutions = new TestSolutionWithMultipleObjectives[scores.length];
for (int i = 0; i < scores.length; i++) {
solutions[i] = new TestSolutionWithMultipleObjectives(instance, scores);
}
return solutions;
}

Map<String, Function<TestSolutionWithMultipleObjectives, Object>> properties = new HashMap<>();


public TestSolutionWithMultipleObjectives(TestInstance ins) {
super(ins);
}

public TestSolutionWithMultipleObjectives(TestInstance ins, double[] scores) {
this(ins);
this.scores = scores;
}

public TestSolutionWithMultipleObjectives(TestInstance ins, double[] scores, Map<String, Function<TestSolutionWithMultipleObjectives, Object>> properties) {
this(ins);
this.scores = scores;
this.properties = properties;
}

public TestSolutionWithMultipleObjectives(TestSolutionWithMultipleObjectives sol) {
super(sol);
this.scores = sol.scores;
}

@Override
public TestSolutionWithMultipleObjectives cloneSolution() {
return new TestSolutionWithMultipleObjectives(this);
}

@Override
public String toString() {
return "TestSolutionWithMultipleObjectives{" +
"score=" + scores.toString() +
'}';
}

public void setTTB(long ttb) {
this.lastModifiedTime = ttb;
}

public void resetTTB() {
this.lastModifiedTime = Integer.MIN_VALUE;
}


@Override
public Map<String, Function<TestSolutionWithMultipleObjectives, Object>> customProperties() {
return this.properties;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestSolutionWithMultipleObjectives that = (TestSolutionWithMultipleObjectives) o;
if (this.scores.length != that.scores.length) {
return false;
}
for (int i = 0; i < this.scores.length; i++) {
if (Double.compare(that.scores[i], scores[i]) != 0){
return false;
}
}
return true;
}

@Override
public int hashCode() {
return Arrays.hashCode(this.scores);
}

public void setScores(double[] scores){
this.scores = scores;
}

public double getObjective(int index){
return this.scores[index];
}

public double[] getScores(){
return this.scores;
}

}
Loading

0 comments on commit 75a904b

Please sign in to comment.