#include "OptimizerEvolutionary.h" namespace mdd { void OptimizerEvolutionary::evaluate(size_t ignore_len) { _mutex.lock(); //clear duplicates //std::sort(_children.begin(), _children.end(), Permutation::smaller); //_children.erase(std::unique(_children.begin(), _children.end(), Permutation::same), _children.end()); std::vector save = _children; _children.clear(); for (size_t i = 0; i < save.size(); i++) { bool found = false; for (size_t j = 0; j < _children.size(); j++) { if (save[i] == _children[j]) { found = true; break; } } if (!found) { _children.push_back(save[i]); } } //print Modules which are not evaluated again opt_state def_state; def_state.module_state = state::UNCHANGED; for (int i = 0; i < ignore_len; ++i) { callback_evaluate_opt(_children[i]); } //calculate fitness for (auto it = _children.begin() + ignore_len; it != _children.end(); ++it) { auto per = findPermutation(it->dna); if (per == nullptr) { for (size_t j = 0; j < _inputs.size(); j++) { //std::cout << "set: " << j << ": " << it->dna[j] << std::endl; _inputs[j]->setValue() = it->dna[j]; } // Start timer auto start = std::chrono::steady_clock::now(); // Update Module opt_state opt = updateOutputs(); // Stop timer auto end = std::chrono::steady_clock::now(); std::chrono::duration elapsed_seconds = end - start; it->time = elapsed_seconds.count(); it->status = opt.module_state; it->fitness = opt.opt_value; callback_evaluate_opt(*it); if (opt.module_state == state::STATE_ERROR) { _children.erase(it); } } else{ callback_evaluate_opt(*per); } } //find fitest double min = _children[0].fitness; _bests.clear(); _bests.push_back(_children[0]); for (size_t i = 1; i < _children.size(); i++) { if (round(min/_precision)*_precision > round(_children[i].fitness / _precision) * _precision) { min = _children[i].fitness; _bests.clear(); _bests.push_back(_children[i]); } else if(round(min / _precision) * _precision == round(_children[i].fitness / _precision) * _precision) { bool found = false; for (size_t j = 0; j < _bests.size(); j++) { if (_children[i] == _bests[j]) { found = true; break; } } if (!found) { _bests.push_back(_children[i]); } } } _mutex.unlock(); callback_autosave(); } OptimizerEvolutionary::OptimizerEvolutionary() :OptimizerBase(R"JSON( { "converges": { "value": 0 }, "grow generation": { "value": 20 }, "max fitness": { "value": 0.0 }, "min generations": { "value": -1 }, "precission": { "value": 0.001 } })JSON") { key = "OptimizerEvolutionary"; _converges = 0; _grow_generation = 20; _max_fitness = 0.0; _min_generations = -1; _precision = 0.001; } bool OptimizerEvolutionary::configureChild(const json& config) { bool found = false; auto jit = config.find("converges"); if (jit != config.end()) { json jval = jit.value()["value"]; if (jval.is_string()) { jval = json::parse(jval.get()); } _converges = jval.get(); _base_config["configure"]["converges"]["value"] = _converges; found = true; } jit = config.find("grow generation"); if (jit != config.end()) { json jval = jit.value()["value"]; if (jval.is_string()) { jval = json::parse(jval.get()); } _grow_generation = jval.get(); _base_config["configure"]["grow generation"]["value"] = _grow_generation; found = true; } jit = config.find("max fitness"); if (jit != config.end()) { json jval = jit.value()["value"]; if (jval.is_string()) { jval = json::parse(jval.get()); } _max_fitness = jval.get(); _base_config["configure"]["max fitness"]["value"] = _max_fitness; found = true; } jit = config.find("min generations"); if (jit != config.end()) { json jval = jit.value()["value"]; if (jval.is_string()) { jval = json::parse(jval.get()); } _min_generations = jval.get(); _base_config["configure"]["min generations"]["value"] = _min_generations; found = true; } jit = config.find("precision"); if (jit != config.end()) { json jval = jit.value()["value"]; if (jval.is_string()) { jval = json::parse(jval.get()); } _precision = jval.get(); _base_config["configure"]["precision"]["value"] = _precision; found = true; } return found; } bool OptimizerEvolutionary::loadStepDB(const json& j) { bool ret = true; for (size_t i = 0; i < j.size(); i++) { json jstep = j.at(i); int counter = json::parse(jstep["step"].get()).get(); json dna = json::parse(jstep["dna"].get()); ret = ret && addStep(counter, dna); if (this->step != counter) { ++this->step; _children.clear(); } std::shared_ptr per_ptr = getPermutation(counter, dna); _children.push_back(*per_ptr); } return ret; } bool OptimizerEvolutionary::loadDB(const json& j) { bool ret = OptimizerBase::loadDB(j); if (step == -1) { return ret; } _bests = findBest(step -1); return ret; } void OptimizerEvolutionary::reset() { OptimizerBase::reset(); _children.clear(); _bests.clear(); _same_counter = 0; _old_best = Permutation(); } bool OptimizerEvolutionary::reachAbort(const std::vector& pers) { bool check; check = step < _min_generations || pers[0].fitness > _max_fitness; if (!check && _converges > 0) { bool found = false; for (size_t i = 0; i < pers.size(); i++) { Permutation p_container = pers.at(i); if (p_container == _old_best) { found = true; break; } } if (found) { ++_same_counter; } else { _old_best = pers[0]; _same_counter = 0; } if (_same_counter < _converges) { check = true; } } return !check; } double OptimizerEvolutionary::update() { if (_inputs.size() == 0) { std::cout << "ERROR: No optimizable inputs detected!" << std::endl; return -1.0; } do { ++step; std::cout << _children.size() << " | " << step << std::endl; evolve(); } while (!reachAbort(_bests)); return _bests[0].fitness; } std::vector OptimizerEvolutionary::mutateGene(std::shared_ptr input, std::vector seed) { const limits limit = *(input->getLimits()); auto ret = seed; if (!limit.elements.empty()) { int i = (int)random_num(0, limit.elements.size() - 1); ret = limit.elements[i]; return ret; } if (!limit.min.empty() && !limit.max.empty()) { bool check = true; do { if (!ret.empty()) { int i = (int)random_num(0, limit.min.size() - 1); if (!limit.step.empty()) { //randomly generate step dx in [min, max] /*std::cout << limit["min"][i].get() << " | "; std::cout << limit["max"][i].dump() << " | "; std::cout << limit["step"][i].dump() << " | ";*/ ret[i] = random_num(limit.min[i], limit.max[i], limit.step[i]); } else { int m = (int)random_num(0, 1); if (m == 0) { ret[i] = limit.min[i]; } else { ret[i] = limit.max[i]; } } } else { for (size_t i = 0; i < limit.min.size(); i++) { if (!limit.step.empty()) { //randomly generate step dx in [min, max] ret.push_back(random_num(limit.min[i], limit.max[i], limit.step[i])); } else { int m = (int)random_num(0, 1); if (m == 0) { ret.push_back(limit.min[i]); } else { ret.push_back(limit.max[i]); } } } } if (!limit.rule.empty()) { //use rule to check exprtk::symbol_table symbol_table; symbol_table.add_vector("val", ret); symbol_table.add_constants(); exprtk::expression func_expr; func_expr.register_symbol_table(symbol_table); exprtk::parser parser; parser.compile(limit.rule, func_expr); double func_val = func_expr.value(); if (func_val < 0.5) { check = false; } else { check = true; } } } while (!check); return ret; } std::cout << "ERROR in GENE elements! (END)" << std::endl; return input->getValue();//ERROR } void OptimizerEvolutionary::evolve() { _mutex.lock(); if (_children.empty()) { for (size_t i = 0; i < _grow_generation * 2; i++) { bool not_found = true; Permutation per; while (not_found) { per = generatePermutation(); not_found = false; for (size_t i = 0; i < _children.size(); i++) { if (_children[i].dna == per.dna) { not_found = true; break; } } } _children.push_back(per); } } else { ////fittest should breed more //detect lower border double max = _children[0].fitness; for (size_t i = 1; i < _children.size(); i++) { if (max < _children[i].fitness) { max = _children[i].fitness; } } //detect lower border double sum = 0; for (size_t i = 0; i < _children.size(); i++) { sum += max - _children[i].fitness; } if (sum == 0) { sum = 1.0; } //multiply fitter parents std::vector gen_pool = _children; for (size_t i = 0; i < _children.size(); i++) { int additional = std::round((max - _children[i].fitness) / sum * 20); //std::cout << additional << std::endl; for (size_t j = 1; j < additional; j++) { gen_pool.push_back(_children[i]); } } //fittest parents will also be new childrens _children = _bests; //breed for (size_t i = 0; i < _grow_generation; i++) { bool not_found = true; Permutation per; int try_counter = 0; while (not_found && try_counter < 20) { int p1 = (int)random_num(0, gen_pool.size() - 1); int p2 = (int)random_num(0, gen_pool.size() - 1); per = combine(gen_pool[p1], gen_pool[p2]); ++try_counter; not_found = false; for (size_t i = 0; i < _children.size(); i++) { if (_children[i].dna == per.dna) { not_found = true; break; } } } _children.push_back(per); } } _mutex.unlock(); callback_add_step(_children); evaluate(_bests.size()); } OptimizerEvolutionary::Permutation OptimizerEvolutionary::combine(Permutation par1, Permutation par2, int try_counter) { size_t len = par1.dna.size(); Permutation child; for (size_t i = 0; i < len; i++) { double p = random_num(0.0, 100.0, 1.0); if (p<45.0 - try_counter) { child.dna.push_back(par1.dna[i]); } else if(p<90.0 - 2*try_counter){ child.dna.push_back(par2.dna[i]); } else { child.dna.push_back(mutateGene(_inputs[i])); } } return child; } OptimizerEvolutionary::Permutation OptimizerEvolutionary::generatePermutation() { Permutation ret; for (size_t i = 0; i < _inputs.size(); i++) { ret.dna.push_back(mutateGene(_inputs[i])); } return ret; } std::vector OptimizerEvolutionary::getBests() { return _bests; } double OptimizerEvolutionary::evaluateFitness(const Permutation& ind) { for (size_t j = 0; j < _inputs.size(); j++) { _inputs[j]->setValue() = ind.dna[j]; } auto opt = updateOutputs(); return opt.opt_value; } }