jdisset / gaga

GAGA is a fast, header only, multi-objective, and distributed evolutionary algorithm library written in modern C++. It is designed to be easily usable with various genotype representations and allows the user to enable or disable several features such as novelty search or speciation. It also produces and exports various customizable statistics.
MIT License
17 stars 4 forks source link

Sample in action: terminal output readability problems on windows #8

Open DJuego opened 5 years ago

DJuego commented 5 years ago

Hi again!

I'm considering using this to evolve/optimize behaviors in mobile robots.

Would it be possible to add small practical examples (optimizing a function or something like that) that are illustrative and also allow the library to be tested?

Thanks!

DJuego

jdisset commented 5 years ago

Hi, Yes, I agree this would be very useful. I have a few examples I can comment and put in an "examples/" folder. I'll do that asap.

jdisset commented 5 years ago

I have added a simple onemax example (with custom DNA, single objective and sqlite export). I will try to add more soon. Is there any specific example you had in mind that you think could benefit a new user?

DJuego commented 5 years ago

Hi! First: thank you for your effort! :-)

Before proposing anything I wanted to verify the example codes. I use Microsoft Visual Studio 2017 in Windows 10 x64 (Spanish)

I see two cpp main files in folder onemax:

simple_onemax.cpp mainevo.cpp (?)

When i try mainevo.cpp i get:

gaga_mainevo png

When i try onemax.cpp i get:

image

In https://github.com/jdisset/gaga/issues/7 I suggest possible (?) solution for _mkdir problem in Visual Studio 2017. But there are more problems. Could you consider them? Thanks in advance!

Anyways i get this output for onemax.cpp (release mode):

output

Sadly, there are readability problems... :-(

DJuego

jdisset commented 3 years ago

For the readability problems, I'm not very familiar with Visual Studio. Judging from your screenshot it seems the terminal doesn't recognize utf-8 symbols, nor does it seem recognize any ANSI escape sequence (at least for color). To "fix" that, I've added 2 preprocessor symbols: GAGA_COLOR_DISABLED -> if defined before including gaga.hpp, disables color output GAGA_UTF8_DEBUG_PRINT_DISABLED -> disables UTF-8 symbols in terminal output Can you maybe try these and let me know if the readability issues are fixed?

DJuego commented 3 years ago

The presentation improves significantly with GAGA_UTF8_DEBUG_PRINT_DISABLED, although it is not perfect.

The code:

#include <array>
#include <cassert>
#include <random>
#include <sstream>

#define GAGA_COLOR_DISABLED 
#define GAGA_UTF8_DEBUG_PRINT_DISABLED

#include <gaga/gaga.hpp>

static std::default_random_engine globalRand;

struct MyDNA {
    // MyDNA is a simple example of a DNA class
    // it contains an array of N integers,
    // implements a simple mutation
    // and a simple uniform crossover
    // Through initialization & mutation, we ensure that the dna will only contain 0 or 1

    static const constexpr size_t N = 40;
    std::array<int, N> numbers;

    MyDNA() {}

    // deserialization : we just read a line of numbers separated by a space
    MyDNA(const std::string& s) {
        std::stringstream ss(s);
        std::string item;
        int i = 0;
        while (std::getline(ss, item, ' ')) numbers[i++] = stoi(item);
        assert(i == N);
    }

    // serialization : write every numbers on a line, separated by a space
    std::string serialize() const {
        std::stringstream ss;
        for (const auto& n : numbers) ss << n << " ";
        ss.seekp(-1, std::ios_base::end);
        return ss.str();
    }

    // mutation consists in replacing one of the numbers by a random number
    void mutate() {
        std::uniform_int_distribution<int> d5050(0, 1);
        std::uniform_int_distribution<int> dInt(0, N - 1);
        numbers[dInt(globalRand)] = d5050(globalRand) ? 1 : 0;
    }

    // this is a uniform crossover :
    // we randomly decide for each number if we take its value from parent 1 or parent 2
    MyDNA crossover(const MyDNA& other) {
        MyDNA res;
        std::uniform_int_distribution<int> d5050(0, 1);
        for (size_t i = 0; i < N; ++i)
            res.numbers[i] = d5050(globalRand) ? numbers[i] : other.numbers[i];
        return res;
    }

    // this is just a static  method that will generate a new random dna
    // we will use it to initialize the population
    static MyDNA random() {
        MyDNA res;
        std::uniform_int_distribution<int> d5050(0, 1);
        for (size_t i = 0; i < N; ++i) res.numbers[i] = d5050(globalRand);
        return res;
    }

};

int main(int, char**) {
    globalRand = std::default_random_engine(0);

    GAGA::GA<MyDNA> ga;  // declaration of the GAGA instance, with dna type MyDNA

    ga.setEvaluator(
        [](auto& individu, int) {  // second argument of the evaluator funciton is the cpuId
            int n = 0;
            for (int a : individu.dna.numbers) n += a;
            std::this_thread::sleep_for(std::chrono::milliseconds(1));  // we simulate load
            individu.fitnesses["number of ones"] = n;                   // only one objective
        },
        "sum");  // name of the evaluator, just used for saving purposes

#ifdef SQLITE_SAVE
    // OPTIONAL: we set up an sqlite saver
    std::string sqlFilename = "onemax.sql";
    SQLiteSaver sql(sqlFilename, "");  // second argument is any configuration detail for
                                       // the run you want to save in the database export
#endif

    // setting a few basic parameters.
    // see documentation for comprehensive list
    ga.setPopSize(200);
    ga.setMutationRate(0.8);
    ga.setCrossoverRate(0.2);
    ga.setVerbosity(1);
    ga.setNbThreads(8);

    // we initialize the population with random DNA. The function passed to
    // initPopulation is called enough time to fill the population vector
    ga.initPopulation([]() { return MyDNA::random(); });

    for (size_t i = 0; i < 10; ++i) {  // we run the ga for 10 generations
        ga.step();                       // next generation

#ifdef SQLITE_SAVE
        sql.newGen(ga);  // saving the generation to sql
#endif
    }

    return 0;
}

Without flags https://imgur.com/ksxXi6R

With flags https://imgur.com/VUGs2H1

DJuego