/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package hh.creditassignment.fitnessindicator; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import org.moeaframework.core.NondominatedPopulation; import org.moeaframework.core.Population; import org.moeaframework.core.Problem; import org.moeaframework.core.Solution; /** * * @author nozomihitomi */ public class R2Indicator implements IIndicator { protected ArrayList<WtVector> wtVecs; /** * Index of the solution that minimizes the population utility */ private int[] minInd1; /** * Value of the smallest population utility */ private double[] minPopUtil; /** * Index of the solution that achieves the 2nd smallest the population * utility */ private int[] minInd2; /** * Value of the 2nd smallest population utility */ private double[] minPopUtil2; private Solution referencePoint; /** * Constructor to initialize the weight vectors * * @param problem * @param numVecs number of vectors * @param referencePoint */ public R2Indicator(Problem problem, int numVecs, Solution referencePoint){ wtVecs = new ArrayList<>(); initializeWts(problem.getNumberOfObjectives(), numVecs); this.referencePoint = referencePoint; } public double computeR2(Population population){ double value = 0.0; for(WtVector vec : wtVecs){ value += popUtility(vec, population); } return value; } @Override public List<Double> computeContributions(Population pop) { for(Solution solution : pop){ solution.setAttribute("contribution", 0.0); } computeContributors(pop, referencePoint); Double[] contributionsWPt = new Double[pop.size()]; Double[] contributionsWoPt = new Double[pop.size()]; Arrays.fill(contributionsWPt, 0.0); Arrays.fill(contributionsWoPt, 0.0); for(int i=0; i<pop.size();i++){ for(int j=0; j<minInd1.length;j++){ contributionsWPt[i]+=minPopUtil[j]; if(minInd1[j]!=i) contributionsWoPt[i]+=minPopUtil[j]; else contributionsWoPt[i]+=minPopUtil2[j]; } } ArrayList<Double> out = new ArrayList(pop.size()); for (int i=0; i<pop.size();i++) { out.add(i, (contributionsWoPt[i] - contributionsWPt[i]) / wtVecs.size()); pop.get(i).setAttribute("contribution", out.get(i)); } return out; } /** * This method is a fast R2 contributor computation based on a combiniation * of Díaz-Manríquez, Alan, Gregorio Toscano-Pulido, Carlos A Coello Coello, * and Ricardo Landa-Becerra. 2013. “A Ranking Method Based on the R2 * Indicator for Many-Objective Optimization.” 2013 IEEE Congress on * Evolutionary Computation, CEC 2013: 1523–1530. * doi:10.1109/CEC.2013.6557743. and Naujoks, B., N. Beume, and M. Emmerich. * 2005. “Multi-Objective Optimisation Using S-Metric Selection: Application * to Three-Dimensional Solution Spaces.” 2005 IEEE Congress on Evolutionary * Computation 2: 1282–1289. doi:10.1109/CEC.2005.1554838. * * Computes the R2 contribution by keeping the minimum and the 2nd minimum * values of the population utility for R2 * * @param pop * @param refPt */ private void computeContributors(Population pop, Solution refPt) { minInd1 = new int[wtVecs.size()]; Arrays.fill(minInd1, -1); minInd2 = new int[wtVecs.size()]; Arrays.fill(minInd2, -1); minPopUtil = new double[wtVecs.size()]; Arrays.fill(minPopUtil, Double.POSITIVE_INFINITY); minPopUtil2 = new double[wtVecs.size()]; Arrays.fill(minPopUtil2, Double.POSITIVE_INFINITY); int vecInd = 0; for (WtVector vec : wtVecs) { for (int i = 0; i < pop.size(); i++) { double solnUtil = solnUtility(vec, pop.get(i), refPt); if (solnUtil < minPopUtil[vecInd]) { minPopUtil2[vecInd] = minPopUtil[vecInd]; minPopUtil[vecInd] = solnUtil; minInd2[vecInd] = minInd1[vecInd]; minInd1[vecInd] = i; } else if (solnUtil < minPopUtil2[vecInd]) { minPopUtil2[vecInd] = solnUtil; minInd2[vecInd] = i; } } vecInd++; } } @Override public double computeContribution(Population pop, Solution offspring) { //Create a nondominated popualtion without the offspring int offspringInd = -1; for (int i = 0; i < pop.size(); i++) { if (offspring.equals(pop.get(i))) { offspringInd=i; } } computeContributors(pop, referencePoint); double contributionsWPt = 0; double contributionsWoPt = 0; for(int j=0; j<minInd1.length;j++){ contributionsWPt+=minPopUtil[j]; if(minInd1[j]!=offspringInd) contributionsWoPt+=minPopUtil[j]; else contributionsWoPt+=minPopUtil2[j]; } double out = (contributionsWoPt - contributionsWPt)/wtVecs.size(); if(out<0){ throw new IllegalStateException("Negative reward even though solution added"); } return out; } /** * In this implementation the order of the inputs * matter. formula based on Phan, D. H., & Suzuki, J. (2013). R2-IBEA: R2 * indicator based evolutionary algorithm for multiobjective optimization. * IEEE Congress on Evolutionary Computation, 1836–1845. * doi:10.1109/CEC.2013.6557783 * * @param solnA * @param solnB * @return */ @Override public double compute(Solution solnA, Solution solnB) { double valA = 0.0; double valB = 0.0; for (WtVector vec : wtVecs) { double solnAUtil = solnUtility(vec, solnA, referencePoint); valA += solnAUtil; valB += Math.min(solnAUtil, solnUtility(vec, solnB, referencePoint)); } return (valA / wtVecs.size()) - (valB / wtVecs.size()); } /** * Returns the minimum value over all the solution utilities wrt to a weight * vector * * @param vec weight vector * @param pop * @param refPt reference point * @return the utility of the nondominated population */ protected double popUtility(WtVector vec, Population pop) { double popUtil = Double.POSITIVE_INFINITY; for (Solution solution : pop) { popUtil = Math.min(popUtil, solnUtility(vec, solution, referencePoint)); } return popUtil; } /** * Computes the utility of a solution wrt to a weight vector using a * Tchebycheff function. Tchebycheff function: u_w(z) = -max{w_j*|z'_j - * z_j|} where w_j is the jth component of the weight vector, z' is the * reference point and z is the objective value. * * @param vec weight vector * @param solution * @param refPt reference point * @return utility of a solution wrt to a weight vectorq */ private double solnUtility(WtVector vec, Solution solution, Solution refPt) { double solnUtil = Double.NEGATIVE_INFINITY; for (int i = 0; i < solution.getNumberOfObjectives(); i++) { solnUtil = Math.max(solnUtil, vec.get(i) * Math.abs(solution.getObjective(i) - refPt.getObjective(i))); } return solnUtil; } /** * Method from jmetal to load the weights for problems meeting certain * criteria such as number of objectives and population size. Returns true * if the weights can be loaded and false if the weights data is * unavailable. * * @param numObj * @param numVecs */ private void initializeWts(int numObj,int numVecs) { String dataFileName; dataFileName = "W" + numObj + "D_" + numVecs + ".dat"; try { // Open the file FileInputStream fis = new FileInputStream("weight" + File.separator + dataFileName); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); wtVecs = new ArrayList<>(numVecs); int j = 0; String aux = br.readLine(); while (aux != null) { StringTokenizer st = new StringTokenizer(aux); j = 0; double[] wts = new double[numObj]; while (st.hasMoreTokens()) { double value = (new Double(st.nextToken())).doubleValue(); wts[j] = value; j++; } wtVecs.add(new WtVector(wts)); aux = br.readLine(); } br.close(); } catch (IOException | NumberFormatException e) { System.out .println("initUniformWeight: failed when reading for file: " + "weight" + File.separator + dataFileName); Logger.getLogger(R2Indicator.class.getName()).log(Level.SEVERE, null, e); } } @Override public String toString() { return "R2"; } @Override public Solution getReferencePoint() { return referencePoint; } @Override public void setReferencePoint(Solution solution) { referencePoint = solution; } protected class WtVector { /** * Weights for vector */ private final double[] weights; public WtVector(double[] weights) { this.weights = weights; } public double get(int i) { return weights[i]; } } }