// This file is a part of Framsticks SDK. http://www.framsticks.com/ // Copyright (C) 2019-2020 Maciej Komosinski and Szymon Ulatowski. // See LICENSE.txt for details. #include #include "common/loggers/loggertostdout.h" #include "frams/genetics/preconfigured.h" #include "frams/genetics/genman.h" #include "frams/model/model.h" struct Individual { Geno geno; double fitness; }; double criterion(char symbol, double value) { return isupper(symbol) ? value : -value; } void evaluate_fitness(Individual &ind, const char *fitness_def) { SString genotype = ind.geno.getGenes(); Model model = Model(ind.geno, Model::SHAPETYPE_UNKNOWN); double fitness = 0; const char *p = fitness_def; while (*p) { switch (*p) { case 'l': case 'L': fitness += criterion(*p, genotype.length()); break; case 'p': case 'P': fitness += criterion(*p, model.getPartCount()); break; case 'j': case 'J': fitness += criterion(*p, model.getJointCount()); break; case 'n': case 'N': fitness += criterion(*p, model.getNeuroCount()); break; case 'c': case 'C': fitness += criterion(*p, model.getConnectionCount()); break; // TODO add more criteria as described in main() below default: printf("Unknown fitness criterion symbol: '%c'\n", *p); exit(3); } p++; } ind.fitness = fitness; } int tournament(const vector &population, int tournament_size) { int best = -1; for (int i = 0; i < tournament_size; i++) { int rnd = rndUint(population.size()); if (best == -1) best = rnd; else if (population[rnd].fitness > population[best].fitness) //assume maximization best = rnd; } return best; } /** A minimalistic steady-state evolutionary algorithm. */ int main(int argc, char *argv[]) { PreconfiguredGenetics genetics; LoggerToStdout messages_to_stdout(LoggerBase::Enable); GenMan genman; bool deterministic; int pop_size, nr_evals; double prob_mut, prob_xover; const char* format; const char* fitness_def; if (argc < 8) { printf("Too few parameters!\n"); printf("Command line: \n"); printf("Example: 1 10 50 0.5 0.5 4 NC\n\n\n"); printf("Fitness definition is a sequence of capital (+1 weight) and small (-1 weight) letters.\n"); printf("Each letter corresponds to one fitness criterion, and they are all weighted and added together.\n"); printf(" l or L - genotype length in characters.\n"); printf(" p or P - the number of Parts.\n"); printf(" j or J - the number of Joints.\n"); printf(" n or N - the number of Neurons.\n"); printf(" c or C - the number of neural Connections.\n"); //TODO add b - bounding box volume (from Model), s - surface area (from geometry), v - volume (from geometry), h,w,d - three consecutive dimensions (from geometry) return 1; } deterministic = atoi(argv[1]) == 1; pop_size = atoi(argv[2]); nr_evals = atoi(argv[3]); prob_mut = atof(argv[4]); prob_xover = atof(argv[5]); format = argv[6]; fitness_def = argv[7]; if (!deterministic) rndGetInstance().randomize(); vector population(pop_size); for (Individual& ind : population) { ind.geno = genman.getSimplest(format); if (ind.geno.getGenes() == "") { printf("Could not get the simplest genotype for format '%s'\n", format); return 2; } evaluate_fitness(ind, fitness_def); } for (int i = 0; i < nr_evals; i++) { int selected_positive = tournament(population, max(2, int(sqrt(population.size()) / 2))); //moderate positive selection pressure int selected_negative = rndUint(population.size()); //random negative selection double rnd = rndDouble(prob_mut + prob_xover); if (rnd < prob_mut) { population[selected_negative].geno = genman.mutate(population[selected_positive].geno); //TODO handle failed mutation evaluate_fitness(population[selected_negative], fitness_def); } else { //TODO crossover } if (i % population.size() == 0 || i == nr_evals - 1) printf("Evaluation %d\t...\n", i); //TODO print min,avg,max fitness \t min,avg,max genotype length \t min,avg,max parts \t min,avg,max neurons } for (const Individual& ind : population) { printf("%.1f\t", ind.fitness); printf("%s\n", ind.geno.getGenes().c_str()); } return 0; }