<commit_before>package edacc.configurator.ga;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import edacc.api.API;
import edacc.model.ExperimentResult;
import edacc.model.Instance;
import edacc.parameterspace.ParameterConfiguration;
import edacc.parameterspace.graph.ParameterGraph;

/** Parcour entries */
class InstanceSeedPair {
    protected int idInstance;
    protected BigInteger seed;
    
    public InstanceSeedPair(int idInstance, BigInteger seed) {
        this.idInstance = idInstance;
        this.seed = seed;
    }
}

/**
 * Genetic algorithm configurator using the EDACC API.
 * 
 * Algorithm:
 * initialize & evaluate population
 * while not termination criterion reached maxTerminationCriterionHits times do:
 *    do #populationSize of times:
 *        with a crossover probability select 2 parents, recombine them, add child to new generation
 *        otherwise copy one individual into new generation
 *    
 *    for each individual of the new population do:
 *      mutate each gene with a mutation probability
 *    
 *    replace old generation with new generation
 *    evaluate population
 *    
 * ----
 * 
 * Termination criterion: generation average cost has to be better than 0.95 times the average
 * of the prevrious generation in order to continue
 * 
 * Parent selection is done via tournament selection of size 5.
 * 5 individuals of the current population are picked at random and the best
 * is selected.
 * 
 * ----
 * 
 * Evaluation of each individual is done on all instances of the EDACC experiment.
 * The cost of an inidividual is its par10 runtime of all runs.
 */
public class GAConfigurator {
    final int populationSize = 40;
    final int tournamentSize = 3;
    final float crossoverProbability = 0.8f;
    final float mutationProbability = 0.05f;
    final float mutationStandardDeviationFactor = 0.05f;
    private final int maxTerminationCriterionHits = 5;
    
    private int terminationCriterionHits = 0;
    private int idExperiment;
    private API api;
    private List<InstanceSeedPair> parcour;
    private Random rng;
    private ParameterGraph pspace;
    private int jobCPUTimeLimit;

    public static void main(String... args) throws Exception {
        if (args.length < 8) {
            System.out.println("arguments: <hostname> <port> <username> <password> <database> <experiment id> <runs per instance> <job CPU time limit>");
            return;
        }
        GAConfigurator ga = new GAConfigurator(args[0], Integer.valueOf(args[1]), args[2],
                args[3], args[4], Integer.valueOf(args[5]), Integer.valueOf(args[6]), Integer.valueOf(args[7]));
        ga.evolve();
        ga.shutdown();
    }

    public GAConfigurator(String hostname, int port, String username,
            String password, String database, int idExperiment,
            int numRunsPerInstance, int jobCPUTimeLimit) throws Exception {
        api = new API();
        api.connect(hostname, port, database, username, password);
        this.idExperiment = idExperiment;
        rng = new edacc.util.MersenneTwister();
        List<Instance> expInstances = api.getExperimentInstances(idExperiment);
        // generate parcour, eventually this should come from the database
        parcour = new ArrayList<InstanceSeedPair>();
        for (int i = 0; i < numRunsPerInstance; i++) {
            for (Instance instance : expInstances) {
                parcour.add(new InstanceSeedPair(instance.getId(),BigInteger.valueOf(rng.nextInt(2147483647))));
            }
        }
        pspace = api.loadParameterGraphFromDB(idExperiment);
        this.jobCPUTimeLimit = jobCPUTimeLimit;
    }

    public void shutdown() {
        api.disconnect();
    }

    protected List<Individual> initializePopulation(int size) throws Exception {
        List<Individual> population = new ArrayList<Individual>();
        for (int i = 0; i < size; i++) {
            ParameterConfiguration cfg = pspace.getRandomConfiguration(rng);
            int idSolverConfig = api.createSolverConfig(idExperiment, cfg, cfg.toString());
            population.add(new Individual(idSolverConfig, cfg));
        }
        return population;
    }

    protected void evaluatePopulation(List<Individual> population, int generation) throws Exception {
        List<Integer> jobs = new ArrayList<Integer>();
        for (Individual ind : population) {
            if (ind.getCost() == null) {
                for (int i = 0; i < parcour.size(); i++) {
                    jobs.add(api.launchJob(idExperiment, ind.getIdSolverConfiguration(),
                            parcour.get(i).idInstance, parcour.get(i).seed, jobCPUTimeLimit));
                }
            }
        }
        
        Map<Integer, ExperimentResult> results;
        while (true) {
            Thread.sleep(3000);
            boolean all_done = true;
            results = api.getJobsByIDs(jobs);
            for (ExperimentResult result: results.values()) {
                all_done &= (result.getStatus().getStatusCode() >= 1 ||
                             result.getStatus().getStatusCode() < -1);
            }
            if (all_done) break;
        }
        
        Map<Integer, Float> individual_time_sum = new HashMap<Integer, Float>();
        for (ExperimentResult result: results.values()) {
            if (!individual_time_sum.containsKey(result.getSolverConfigId()))
                individual_time_sum.put(result.getSolverConfigId(), 0.0f);
            individual_time_sum.put(result.getSolverConfigId(),
                        individual_time_sum.get(result.getSolverConfigId()) + result.getResultTime() * (result.getResultCode().getResultCode() > 0 ? 1 : 10));
        }
        
        for (Individual ind: population) {
            if (ind.getCost() == null) {
                // par10 result time is the cost
                ind.setCost(individual_time_sum.get(ind.getIdSolverConfiguration()) / parcour.size());
            }<commit_msg>use average instead of par10; changed some parameters; write solver config cost to the new DB columns<commit_after>package edacc.configurator.ga;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import edacc.api.API;
import edacc.model.ExperimentResult;
import edacc.model.Instance;
import edacc.parameterspace.ParameterConfiguration;
import edacc.parameterspace.graph.ParameterGraph;

/** Parcour entries */
class InstanceSeedPair {
    protected int idInstance;
    protected BigInteger seed;
    
    public InstanceSeedPair(int idInstance, BigInteger seed) {
        this.idInstance = idInstance;
        this.seed = seed;
    }
}

/**
 * Genetic algorithm configurator using the EDACC API.
 * 
 * Algorithm:
 * initialize & evaluate population
 * while not termination criterion reached maxTerminationCriterionHits times do:
 *    do #populationSize of times:
 *        with a crossover probability select 2 parents, recombine them, add child to new generation
 *        otherwise copy one individual into new generation
 *    
 *    for each individual of the new population do:
 *      mutate each gene with a mutation probability
 *    
 *    replace old generation with new generation
 *    evaluate population
 *    
 * ----
 * 
 * Termination criterion: generation average cost has to be better than 0.95 times the average
 * of the prevrious generation in order to continue
 * 
 * Parent selection is done via tournament selection of size 5.
 * 5 individuals of the current population are picked at random and the best
 * is selected.
 * 
 * ----
 * 
 * Evaluation of each individual is done on all instances of the EDACC experiment.
 * The cost of an inidividual is its par10 runtime of all runs.
 */
public class GAConfigurator {
    final int populationSize = 40;
    final int tournamentSize = 3;
    final float crossoverProbability = 0.8f;
    final float mutationProbability = 0.1f;
    final float mutationStandardDeviationFactor = 0.1f;
    private final int maxTerminationCriterionHits = 3;
    
    private int terminationCriterionHits = 0;
    private int idExperiment;
    private API api;
    private List<InstanceSeedPair> parcour;
    private Random rng;
    private ParameterGraph pspace;
    private int jobCPUTimeLimit;

    public static void main(String... args) throws Exception {
        if (args.length < 8) {
            System.out.println("arguments: <hostname> <port> <username> <password> <database> <experiment id> <runs per instance> <job CPU time limit>");
            return;
        }
        GAConfigurator ga = new GAConfigurator(args[0], Integer.valueOf(args[1]), args[2],
                args[3], args[4], Integer.valueOf(args[5]), Integer.valueOf(args[6]), Integer.valueOf(args[7]));
        ga.evolve();
        ga.shutdown();
    }

    public GAConfigurator(String hostname, int port, String username,
            String password, String database, int idExperiment,
            int numRunsPerInstance, int jobCPUTimeLimit) throws Exception {
        api = new API();
        api.connect(hostname, port, database, username, password);
        this.idExperiment = idExperiment;
        rng = new edacc.util.MersenneTwister();
        List<Instance> expInstances = api.getExperimentInstances(idExperiment);
        // generate parcour, eventually this should come from the database
        parcour = new ArrayList<InstanceSeedPair>();
        for (int i = 0; i < numRunsPerInstance; i++) {
            for (Instance instance : expInstances) {
                parcour.add(new InstanceSeedPair(instance.getId(),BigInteger.valueOf(rng.nextInt(2147483647))));
            }
        }
        pspace = api.loadParameterGraphFromDB(idExperiment);
        this.jobCPUTimeLimit = jobCPUTimeLimit;
    }

    public void shutdown() {
        api.disconnect();
    }

    protected List<Individual> initializePopulation(int size) throws Exception {
        List<Individual> population = new ArrayList<Individual>();
        for (int i = 0; i < size; i++) {
            ParameterConfiguration cfg = pspace.getRandomConfiguration(rng);
            int idSolverConfig = api.createSolverConfig(idExperiment, cfg, cfg.toString());
            population.add(new Individual(idSolverConfig, cfg));
        }
        return population;
    }

    protected void evaluatePopulation(List<Individual> population, int generation) throws Exception {
        List<Integer> jobs = new ArrayList<Integer>();
        for (Individual ind : population) {
            if (ind.getCost() == null) {
                for (int i = 0; i < parcour.size(); i++) {
                    jobs.add(api.launchJob(idExperiment, ind.getIdSolverConfiguration(),
                            parcour.get(i).idInstance, parcour.get(i).seed, jobCPUTimeLimit));
                }
            }
        }
        
        Map<Integer, ExperimentResult> results;
        while (true) {
            Thread.sleep(3000);
            boolean all_done = true;
            results = api.getJobsByIDs(jobs);
            for (ExperimentResult result: results.values()) {
                all_done &= (result.getStatus().getStatusCode() >= 1 ||
                             result.getStatus().getStatusCode() < -1);
            }
            if (all_done) break;
        }
        
        Map<Integer, Float> individual_time_sum = new HashMap<Integer, Float>();
        for (ExperimentResult result: results.values()) {
            if (!individual_time_sum.containsKey(result.getSolverConfigId()))
                individual_time_sum.put(result.getSolverConfigId(), 0.0f);
            individual_time_sum.put(result.getSolverConfigId(),
                        individual_time_sum.get(result.getSolverConfigId()) +
                        result.getResultTime()); // * (result.getResultCode().getResultCode() > 0 ? 1 : 10));
        }
        
        for (Individual ind: population) {
            if (ind.getCost() == null) {
                // par10 result time is the cost
                float cost = individual_time_sum.get(ind.getIdSolverConfiguration()) / parcour.size();
                ind.setCost(cost);
                api.updateSolverConfigurationCost(ind.getIdSolverConfiguration(), cost, API.COST_FUNCTIONS.AVERAGE);
            }
{'fraction_non_alphanumeric': 0.09513539420081477, 'fraction_numerical': 0.00910615863886892, 'mean_word_length': 2.6469560151471017, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '969052', 'n_tokens_mistral': 3478, 'n_tokens_neox': 3316, 'n_words': 948}