/**
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
*/
package org.evosuite.ga.metaheuristics.mosa;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.evosuite.ga.Chromosome;
import org.evosuite.ga.ChromosomeFactory;
import org.evosuite.ga.FitnessFunction;
import org.evosuite.ga.metaheuristics.GeneticAlgorithm;
import org.evosuite.ga.metaheuristics.mosa.comparators.OnlyCrowdingComparator;
import org.evosuite.testcase.TestChromosome;
import org.evosuite.testcase.TestFitnessFunction;
import org.evosuite.testsuite.TestSuiteChromosome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of the MOSA (Many-Objective Sorting Algorithm) described in the ICST'15 paper ...
*
* @author Annibale Panichella, Fitsum M. Kifetew
*
* @param <T>
*/
public class MOSA<T extends Chromosome> extends AbstractMOSA<T> {
private static final long serialVersionUID = 146182080947267628L;
private static final Logger logger = LoggerFactory.getLogger(MOSA.class);
/** Map used to store the covered test goals (keys of the map) and the corresponding covering test cases (values of the map) **/
protected Map<FitnessFunction<T>, T> archive = new HashMap<FitnessFunction<T>, T>();
/** Boolean vector to indicate whether each test goal is covered or not. **/
protected Set<FitnessFunction<T>> uncoveredGoals = new HashSet<FitnessFunction<T>>();
protected CrowdingDistance<T> distance = new CrowdingDistance<T>();
/**
* Constructor based on the abstract class {@link AbstractMOSA}
* @param factory
*/
public MOSA(ChromosomeFactory<T> factory) {
super(factory);
}
/** {@inheritDoc} */
@Override
protected void evolve() {
List<T> offspringPopulation = breedNextGeneration();
// Create the union of parents and offSpring
List<T> union = new ArrayList<T>();
union.addAll(population);
union.addAll(offspringPopulation);
// Ranking the union
logger.debug("Union Size =" + union.size());
// Ranking the union using the best rank algorithm (modified version of the non dominated sorting algorithm
ranking.computeRankingAssignment(union, uncoveredGoals);
// add to the archive the new covered goals (and the corresponding test cases)
//this.archive.putAll(ranking.getNewCoveredGoals());
int remain = population.size();
int index = 0;
List<T> front = null;
population.clear();
// Obtain the next front
front = ranking.getSubfront(index);
while ((remain > 0) && (remain >= front.size())) {
// Assign crowding distance to individuals
distance.fastEpsilonDominanceAssignment(front, uncoveredGoals);
// Add the individuals of this front
population.addAll(front);
// Decrement remain
remain = remain - front.size();
// Obtain the next front
index++;
if (remain > 0) {
front = ranking.getSubfront(index);
} // if
} // while
// Remain is less than front(index).size, insert only the best one
if (remain > 0) { // front contains individuals to insert
distance.fastEpsilonDominanceAssignment(front, uncoveredGoals);
Collections.sort(front, new OnlyCrowdingComparator());
for (int k = 0; k < remain; k++) {
population.add(front.get(k));
} // for
remain = 0;
} // if
currentIteration++;
//logger.error("");
//logger.error("N. fronts = "+ranking.getNumberOfSubfronts());
//logger.debug("1* front size = "+ranking.getSubfront(0).size());
//logger.debug("2* front size = "+ranking.getSubfront(1).size());
//logger.error("Covered goals = "+this.archive.size());
//logger.error("Uncovered goals = "+uncoveredGoals.size());
//logger.debug("Generation=" + currentIteration + " Population Size=" + population.size() + " Archive size=" + archive.size());
}
/** {@inheritDoc} */
@Override
protected void calculateFitness(T c) {
for (FitnessFunction<T> fitnessFunction : this.fitnessFunctions) {
double value = fitnessFunction.getFitness(c);
if (value == 0.0) {
//((TestChromosome)c).addCoveredGoals(fitnessFunction);
updateArchive(c, fitnessFunction);
}
}
notifyEvaluation(c);
}
/** {@inheritDoc} */
@Override
public void generateSolution() {
logger.info("executing generateSolution function");
// keep track of covered goals
for (FitnessFunction<T> goal : fitnessFunctions) {
uncoveredGoals.add(goal);
}
//initialize population
if (population.isEmpty())
initializePopulation();
// Calculate dominance ranks and crowding distance
ranking.computeRankingAssignment(population, this.uncoveredGoals);
for (int i = 0; i<ranking.getNumberOfSubfronts(); i++){
distance.fastEpsilonDominanceAssignment(ranking.getSubfront(i), this.uncoveredGoals);
}
// TODO add here dynamic stopping condition
while (!isFinished() && this.getNumberOfCoveredGoals()<this.fitnessFunctions.size()) {
evolve();
notifyIteration();
}
notifySearchFinished();
}
/** This method is used to print the number of test goals covered by the test cases stored in the current archive **/
private int getNumberOfCoveredGoals() {
int n_covered_goals = this.archive.keySet().size();
logger.debug("# Covered Goals = " + n_covered_goals);
return n_covered_goals;
}
/** This method return the test goals covered by the test cases stored in the current archive **/
public Set<FitnessFunction<T>> getCoveredGoals() {
return this.archive.keySet();
}
/**
* This method update the archive by adding test cases that cover new test goals, or replacing the
* old tests if the new ones are smaller (at the same level of coverage).
*
* @param solutionSet is the list of Chromosomes (population)
*/
private void updateArchive(T solution, FitnessFunction<T> covered) {
// the next two lines are needed since that coverage information are used
// during EvoSuite post-processing
TestChromosome tch = (TestChromosome) solution;
tch.getTestCase().getCoveredGoals().add((TestFitnessFunction) covered);
// store the test cases that are optimal for the test goal in the
// archive
if (archive.containsKey(covered)){
int bestSize = this.archive.get(covered).size();
int size = solution.size();
if (size < bestSize)
this.archive.put(covered, solution);
} else {
archive.put(covered, solution);
this.uncoveredGoals.remove(covered);
}
}
protected List<T> getArchive() {
Set<T> set = new HashSet<T>();
set.addAll(archive.values());
List<T> arch = new ArrayList<T>();
arch.addAll(set);
return arch;
}
protected List<T> getFinalTestSuite() {
// trivial case where there are no branches to cover or the archive is empty
if (this.getNumberOfCoveredGoals()==0) {
return getArchive();
}
if (archive.size() == 0)
if (population.size() > 0) {
ArrayList<T> list = new ArrayList<T>();
list.add(population.get(population.size() - 1));
return list;
} else
return getArchive();
List<T> final_tests = getArchive();
List<T> tests = this.getNonDominatedSolutions(final_tests);
return tests;
}
/**
* This method is used by the Progress Monitor at the and of each generation to show the totol coverage reached by the algorithm.
* Since the Progress Monitor need a "Suite", this method artificially creates a "SuiteChromosome" (see {@link MOSA#suiteFitness})
* as the union of all test cases stored in {@link MOSA#archive}.
*
* The coverage score of the "SuiteChromosome" is given by the percentage of test goals covered (goals in {@link MOSA#archive})
* onto the total number of goals <code> this.fitnessFunctions</code> (see {@link GeneticAlgorithm}).
*
* @return "SuiteChromosome" directly consumable by the Progress Monitor.
*/
@Override @SuppressWarnings("unchecked")
public T getBestIndividual() {
TestSuiteChromosome best = new TestSuiteChromosome();
for (T test : getArchive()) {
best.addTest((TestChromosome) test);
}
// compute overall fitness and coverage
double coverage = ((double) this.getNumberOfCoveredGoals()) / ((double) this.fitnessFunctions.size());
best.setCoverage(suiteFitness, coverage);
best.setFitness(suiteFitness, this.fitnessFunctions.size() - this.getNumberOfCoveredGoals());
//suiteFitness.getFitness(best);
return (T) best;
}
protected double numberOfCoveredTargets(){
return this.archive.keySet().size();
}
}