All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.lm.LanguageModelSearch Maven / Gradle / Ivy

/**
 * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * 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 .
 */
package org.evosuite.lm;

import org.evosuite.Properties;
import org.evosuite.testcase.ValueMinimizer;
import org.evosuite.testcase.variable.ConstantValue;
import org.evosuite.utils.Randomness;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by mat on 07/04/2014.
 */
public abstract class LanguageModelSearch implements Comparator {
    protected static final int GENERATIONS = 1000000;
    private static Logger logger = LoggerFactory.getLogger(LanguageModelSearch.class);
    protected final LangModel languageModel;
    protected final String startPoint;
    protected final ValueMinimizer.Minimization objective;
    protected final ConstantValue constantValue;

    protected static final int MAX_EVALUATIONS;

    static{
        MAX_EVALUATIONS = Properties.LM_ITERATIONS;
    }

    public int getEvaluations() {
        return evaluations;
    }

    private int evaluations = 0;


    public LanguageModelSearch(ValueMinimizer.Minimization objective, ConstantValue constantValue) {
        try {
            this.languageModel = new LangModel(Properties.LM_SRC);
        } catch (Exception e) {
            //FIXME: remove this garbage
            throw new RuntimeException("Couldn't create language model");
        }
        this.startPoint = (String)constantValue.getValue();
        this.objective = objective;
        // this.statement = statement;
        this.constantValue = constantValue;
    }

    public static String mutateRandom(final String input){

        if (input == null || input.length() == 0){
            return input;
        }

        int position = (int)(Math.abs(Randomness.nextDouble() * input.length()));

        String output = input.substring(0,position);

        int tailSize = input.length() - position;

        assert tailSize >= 0;

        int replacementSize = 1;
        if(tailSize > 1)
            replacementSize = Randomness.nextInt(1,tailSize);

        String replacementString = "";
        for(int i =0; i < replacementSize; i++)
            replacementString += (char) Randomness.nextInt(' ', '~' + 1);

        output += replacementString;

        output += input.substring(position+ replacementSize);


        return output;
    }

    public static String mutateEvoSuite(final String input){

        char[] array = input.toCharArray();


        for (int index = 0; index < input.length(); index++) {
            if (Randomness.nextDouble() < 1.0 / (0.5 * input.length() + 1)) {
                char oldValue = array[index];
                while (array[index] == oldValue) {
                    array[index] = (char) Randomness.nextInt(' ', '~' + 1);
                }
            }
        }
        assert String.valueOf(array).length() == input.length();
        return String.valueOf(array);
    }

    public String mutate(final String input){
        if(Properties.LM_MUTATION_TYPE == MutationType.LANGMODEL) {
            return mutateLangModel(input);
        }else if(Properties.LM_MUTATION_TYPE == MutationType.RANDOM){
            return mutateRandom(input);
        }else if(Properties.LM_MUTATION_TYPE == MutationType.EVOSUITE){
            return mutateEvoSuite(input);
        }else{
            throw new RuntimeException("Mutation type " + Properties.LM_MUTATION_TYPE + " not supported");
        }
    }

    /**
     * Build a random string that is at most maxStringLength characters long.
     */
    public String generateRandomStringFromModel(int maxStringLength, String previousChar){
        //Start the string with a random choice from the 10 most likely characters to start a string:


        String newString = "";


        do{

            Set choices = new HashSet();
            choices.addAll(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));

            boolean foundValidChar = false;
            String nextChar;

            do{
                int currentChoice = Randomness.choice(choices);

                choices.remove(currentChoice);

                if(previousChar == null){
                    nextChar = languageModel.predict_char(currentChoice);
                }else{
                    nextChar = languageModel.predict_char(previousChar, currentChoice);
                }

                foundValidChar = nextChar != null && !languageModel.isMagicChar(nextChar);

            }while(!choices.isEmpty() && !foundValidChar);
            //We keep trying until we get an actual char, or we run out of slots. After that, we give up and
            //reuse the input.

            if(!foundValidChar){
                logger.debug("Couldn't find any bigram or unigram for " + previousChar);
                nextChar = "a"; //TODO: this is a kludge
            }


            if(!languageModel.isEndOfSentence(nextChar))
                newString += nextChar;

            previousChar = nextChar;

        }while(newString.length() < maxStringLength && !languageModel.isEndOfSentence(previousChar));

        return newString;
    }

    public String generateRandomStringFromModelWithExactLength(int targetStringLength, String previousChar){
        String newString = generateRandomStringFromModel(targetStringLength,previousChar);

        while(newString.length() < targetStringLength){
            newString += generateRandomStringFromModel(targetStringLength - newString.length(), newString.charAt(newString.length()-1) + "");
        }

        assert newString.length() == targetStringLength;

        return newString;
    }

    public String generateRandomStringFromModelWithExactLength(int targetStringLength){
        return generateRandomStringFromModelWithExactLength(targetStringLength, null);
    }

    public String generateRandomStringFromModel(int maxStringLength){
        return generateRandomStringFromModel(maxStringLength, null);
    }


    /**
     * Produce a new string similar to input.
     * Selects a random substring of the string and for each character in that substring:
     *  gets the character preceeding it
     *  gets a character the language model predicts to follow that prefix
     *  replaces the character with the predicted character
     */
    public String mutateLangModel(final String input){

        if(input.length() == 0){
            //Mutation preserves length, so we can't do anything for empty string.
            return input;
        }

        //e.g. string is "abcde"
        // point is 0..4
        //Since nextDouble never returns 1.0, taking the floor gives us values in the range 0..strlen-1:
        int startPoint = (int)(Randomness.nextDouble() * (input.length()));

        //we preserve string lengths; how many chars do we have to mutate?
        int remainingLength = (int)Math.round(
                Randomness.nextDouble() * (input.length() - startPoint)
        );

        if(remainingLength == 0){
            //Mutation size is zero, so there's nothing to do.
            return input;
        }


        //substring is start (inclusive) to end (exclusive).
        //e.g. if startPoint is 3
        // output = "abc" at this point
        String output = input.substring(0,startPoint);

        String replacementChunk = generateRandomStringFromModelWithExactLength(remainingLength, ""+ input.charAt(startPoint));


        output += replacementChunk;
        output += input.substring(startPoint + remainingLength, input.length());

        return output;

    }


    /**
     * @param individual
     * @return the language model score of the string, plus 1 if it does not affect the coverage goal.
     */
    protected double evaluate(Chromosome individual) throws EvaluationBudgetExpendedException {
        if(isBudgetExpended()){
            throw new EvaluationBudgetExpendedException();
        }
        evaluations++;
        String oldValue = (String)constantValue.getValue();
        constantValue.setValue(individual.getValue());
        boolean isNotWorse = objective.isNotWorse();

        constantValue.setValue(oldValue);

        return (isNotWorse ? 1 : 0) + languageModel.score(individual.getValue());

    }

    protected boolean isBudgetExpended() {
        return evaluations >= MAX_EVALUATIONS;
    }

    protected Chromosome mutate(Chromosome individual){
        assert individual != null;
        return new Chromosome(mutate(individual.getValue()));
    }



    public abstract String optimise();

    @Override
    public int compare(Chromosome o1, Chromosome o2) {
        if (!o1.isEvaluated()) {
            o1.setFitness(evaluate(o1));
        }
        if (!o2.isEvaluated()) {
            o2.setFitness(evaluate(o2));
        }
        return o1.compareTo(o2);
    }


    protected void resetEvaluationCounter() {
        evaluations = 0;
    }
}

class Chromosome implements Comparable, Cloneable {
    private String value;
    private double fitness = -1;
    private boolean isEvaluated = false;

    public Chromosome(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        if (this.value.equals(value)) {
            return;
        }

        this.value = value;
        this.isEvaluated = false;
    }

    public double getFitness() {
        return fitness;
    }

    public void setFitness(double fitness) {
        this.fitness = fitness;
        this.isEvaluated = true;
    }

    public boolean isEvaluated() {
        return isEvaluated;
    }

    public Chromosome clone() {
        Chromosome other = new Chromosome(this.getValue());
        other.setFitness(this.getFitness());
        other.isEvaluated = this.isEvaluated();
        return other;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Chromosome that = (Chromosome) o;

        //Don't care about fitness or evaluation status:

        return value.equals(that.value);
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = value.hashCode();
        return result;
            /*temp = Double.doubleToLongBits(fitness);
            result = 31 * result + (int) (temp ^ (temp >>> 32));
            result = 31 * result + (isEvaluated ? 1 : 0);
            return result;*/
    }

    @Override
    public int compareTo(Chromosome o) {
        if (o == null) {
            return 1;
        }

        return Double.compare(this.getFitness(), o.getFitness());

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy