Browse Source

worked on fixing Optimizer

Willi Zschiebsch 5 years atrás
parent
commit
60f392a643

+ 4 - 4
lib/include/IOptimizer.h

@@ -6,12 +6,12 @@
 namespace mdd {
 	class IOptimizer {
 		public:
-			virtual bool connect(std::shared_ptr<IModule> module) = 0;
+			//virtual bool connect(std::shared_ptr<IModule> module) = 0;
 			/*
 			limit{
 				("min": []
 				"max": []
-				("increment": []
+				("step": []
 				or
 				"rule": string))
 				nor
@@ -25,7 +25,7 @@ namespace mdd {
 			limit{
 				("min": []
 				"max": []
-				("increment": []
+				("step": []
 				or
 				"rule": string))
 				nor
@@ -36,7 +36,7 @@ namespace mdd {
 			virtual void removeModifier(std::string input_id) = 0;
 			virtual void removeReader(std::string output_id) = 0;
 			virtual bool setEvaluation(std::string func) = 0;
-			virtual state update() = 0;
+			virtual json update() = 0;
 	};
 }
 #endif // !IOPTIMIZER_H

+ 6 - 1
lib/include/OptimizerBase.h

@@ -4,6 +4,11 @@
 #include "exprtk.hpp"
 
 namespace mdd {
+	////////////////////////////////////////////////////////////
+	//
+	//Optimization is always minimization!
+	//
+	////////////////////////////////////////////////////////////
 	class OptimizerBase : public IOptimizer {
 	protected:
 		std::shared_ptr<IModule> _module;
@@ -14,7 +19,7 @@ namespace mdd {
 		exprtk::expression<double> _func_expr;
 
 		struct opt_state {
-			state module_state;
+			state module_state = state::STATE_ERROR;
 			double opt_value = 0;
 		};
 		opt_state updateReader();

+ 19 - 28
lib/include/OptimizerEvolutionary.h

@@ -5,43 +5,34 @@
 namespace mdd {
 	class OptimizerEvolutionary : public OptimizerBase {
 	protected:
-		class Gene {
-		public:
-			json limit;
-			json value;
-			void mutate();
-			static void combine(Gene gene1, Gene gene2);
-		};
-
-		class DNA {
-		public:
-			json code;
-			void mutate();
-			void combine();
-			void applyDNA(std::vector<Gene>& genes);
-			static DNA generate(const std::vector<Gene>& genes);
-		};
+		static int random_num(double min, double max, double inc = 1.0)
+		{
+			int range = (max - min)/inc + 1;
+			return min + inc*(std::rand() % range);
+		}
 
-		class Individuum {
+		struct Individual {
 		public:
-			DNA dna;
+			json dna;
 			double fitness;
-
 		};
 
-		class Population {
-		public:
-			std::vector<Individuum> inivids:
-			std::vector<std::shared_ptr<Population>> grow(int size);
+		Individual generateIndividual();
+		Individual combine(Individual par1, Individual par2);
 
-		};
+		std::vector<Individual> _children;
+		Individual _best;
+		int _min_generations;
+		double _max_fitness;
+		size_t _grow_generation;
 
-		std::vector<Gene> _genes
-		Population _pops;
+		void evolve(std::vector<Individual> parents);
+		void evaluate(size_t ignore_len = 0);
+		json mutateGene(json limit, json seed = json());
 
 	public:
-		OptimizerEvolutionary(std::shared_ptr<IModule> module);
-		state update() override;
+		OptimizerEvolutionary(std::shared_ptr<IModule> module, size_t grow_generation = 20, double max_fitness = 0.0, int min_generations = -1);
+		json update() override;
 	};
 }
 #endif

+ 6 - 1
lib/src/OptimizerBase.cpp

@@ -1,4 +1,5 @@
 #include "OptimizerBase.h"
+#include <iostream>
 
 namespace mdd{
 	OptimizerBase::opt_state OptimizerBase::updateReader()
@@ -10,8 +11,10 @@ namespace mdd{
 			for (size_t i = 0; i < _output_vals.size(); ++i)
 			{
 				_output_vals[i] = _outputs[i]->getValue()["value"][0].get<double>();
+				std::cout << "get: " << i << ": " << _output_vals[i] << std::endl;
 			}
 			ret.opt_value = _func_expr.value();
+			std::cout << "get: " << "opt" << ": " << ret.opt_value << std::endl;
 		}
 		
 		return ret;
@@ -54,8 +57,10 @@ namespace mdd{
 			if (_inputs[i]->getID() == input_id)
 			{
 				_input_limits[i] = limit;
+				return true;
 			}
 		}
+		return false;
 	}
 
 	void OptimizerBase::removeModifier(std::string input_id)
@@ -83,7 +88,7 @@ namespace mdd{
 		exprtk::symbol_table<double> symbol_table;
 		for (size_t i = 0; i < _output_vals.size(); ++i)
 		{
-			symbol_table.add_variable("o"+i, _output_vals[i]);
+			symbol_table.add_variable("out" + std::to_string(i), _output_vals[i]);
 		}
 		symbol_table.add_constants();
 	

+ 229 - 3
lib/src/OptimizerEvolutionary.cpp

@@ -1,11 +1,237 @@
 #include "OptimizerEvolutionary.h"
+#include <iostream>
 
 namespace mdd {
-	OptimizerEvolutionary::OptimizerEvolutionary(std::shared_ptr<IModule> module)
+	void OptimizerEvolutionary::evaluate(size_t ignore_len)
 	{
+		//calculate fitness
+		for (auto& it = _children.begin() + ignore_len; it != _children.end(); ++it)
+		{
+			for (size_t j = 0; j < _inputs.size(); j++)
+			{
+				std::cout << "set: " << j << ": " << it->dna[j] << std::endl;
+				_inputs[j]->setDefaultValue() = it->dna[j];
+			}
+			auto opt = updateReader();
+			if (opt.module_state == state::STATE_ERROR) {
+				_children.erase(it);
+			}
+			else {
+				it->fitness = opt.opt_value;
+			}
+		}
+		//find fitest
+		double max = _children[0].fitness;
+		for (size_t i = 1; i < _children.size(); i++)
+		{
+			if (max < _children[i].fitness) {
+				max = _children[i].fitness;
+				_best = _children[i];
+			}
+		}
 	}
-	state OptimizerEvolutionary::update()
+	OptimizerEvolutionary::OptimizerEvolutionary(std::shared_ptr<IModule> module, size_t grow_generation, double max_fitness, int min_generations)
 	{
-		return state();
+		_module = module;
+		_grow_generation = grow_generation;
+		_min_generations = min_generations;
+		_max_fitness = max_fitness;
+	}
+	json OptimizerEvolutionary::update()
+	{
+		size_t gen = -1;
+		do
+		{
+			if (_children.empty())
+			{
+				for (size_t i = 0; i < _grow_generation*2; i++)
+				{
+					_children.push_back(generateIndividual());
+				}
+				evaluate();
+			}
+			else {
+				evolve(_children);
+			}
+			++gen;
+		} while (gen < _min_generations || _best.fitness > _max_fitness);
+		
+		return _best.dna;
+	}
+	json OptimizerEvolutionary::mutateGene(json limit, json seed)
+	{
+		/*
+			limit{
+				(("min": []
+				or
+				"max" : [])
+				("step": []
+				or
+				"rule" : string))
+				nor
+				"elements": [[],[],[]]
+		}
+			*/
+		json ret = seed;
+		
+		
+		if (limit.contains("elements"))
+		{
+			if (limit["elements"].is_array()) {
+				int i = (int)random_num(0, limit["elements"].size()-1);
+				ret["value"] = limit["elements"][i].is_array();
+			}
+			std::cout << "ERROR in GENE elements!" << std::endl;
+			return ret;
+		}
+		if (limit.contains("min") && limit.contains("max")) {
+
+			bool check = true;
+			do {
+				if (!ret.empty())
+				{
+					int i = (int)random_num(0, limit["min"].size() - 1);
+					if (limit.contains("step")) {
+						//randomly generate step dx in [min, max]
+						/*std::cout << limit["min"][i].get<double>() << " | ";
+						std::cout << limit["max"][i].dump() << " | ";
+						std::cout << limit["step"][i].dump() << " | ";*/
+						ret["value"][i] = random_num(limit["min"][i].get<double>(), limit["max"][i].get<double>(), limit["step"][i].get<double>());
+					}
+					else {
+						int m = (int)random_num(0, 1);
+						if (m == 0)
+						{
+							ret["value"][i] = limit["min"][i];
+						}
+						else {
+							ret["value"][i] = limit["max"][i];
+						}
+					}
+				}
+				else
+				{
+					for (size_t i = 0; i < limit["min"].size(); i++)
+					{
+						if (limit.contains("step")) {
+							//randomly generate step dx in [min, max]
+							ret["value"].push_back(random_num(limit["min"][i].get<double>(), limit["max"][i].get<double>(), limit["step"][i].get<double>()));
+						}
+						else {
+							int m = (int)random_num(0, 1);
+							if (m == 0)
+							{
+								ret["value"].push_back(limit["min"][i]);
+							}
+							else {
+								ret["value"].push_back(limit["max"][i]);
+							}
+						}
+					}
+				}
+				
+				if (limit.contains("rule")) {
+					//use rule to check
+					std::cout << "ERROR in GENE elements! (rule not implemented)" << std::endl;
+					check = false;
+				}
+			} while (!check);
+			return ret; 
+			
+		}
+		std::cout << "ERROR in GENE elements! (END)" << std::endl;
+		return json();//ERROR
+	}
+
+	void OptimizerEvolutionary::evolve(std::vector<Individual> parents)
+	{
+		////fittest should breed more
+		//detect lower border
+		double max = parents[0].fitness;
+		for (size_t i = 1; i < parents.size(); i++)
+		{
+			if (max < parents[i].fitness) {
+				max = parents[i].fitness;
+			}
+		}
+		//detect lower border
+		double sum = 0;
+		for (size_t i = 0; i < parents.size(); i++)
+		{
+			sum += max - parents[i].fitness;
+		}
+		//multiply fitter parents
+		std::vector<Individual> gen_pool = parents;
+		for (size_t i = 0; i < parents.size(); i++)
+		{
+			for (size_t j = 1; j < std::round((max - parents[i].fitness)/sum*20); i++)
+			{
+				gen_pool.push_back(parents[i]);
+			}
+		}
+		//fittest parents will also be new childrens
+		_children.clear();
+		double min = parents[0].fitness;
+		for (size_t i = 1; i < parents.size(); i++)
+		{
+			if (max > parents[i].fitness) {
+				min = parents[i].fitness;
+			}
+		}
+		for (size_t i = 0; i < parents.size(); i++)
+		{
+			if (max == parents[i].fitness) {
+				bool exist = false;
+				for (size_t j = 0; j < _children.size(); j++)
+				{
+					if (_children[j].dna == parents[i].dna) {
+						exist = true;
+						break;
+					}
+				}
+				if (!exist) {
+					_children.push_back(parents[i]);
+				}
+			}
+		}
+		//breed
+		size_t init_len = _children.size();
+		for (size_t i = 0; i < _grow_generation; i++)
+		{
+			int p1 = (int)random_num(0, gen_pool.size() - 1);
+			int p2 = (int)random_num(0, gen_pool.size() - 1);
+			_children.push_back(combine(gen_pool[p1], gen_pool[p2]));
+		}
+		evaluate(init_len);
+	}
+	OptimizerEvolutionary::Individual OptimizerEvolutionary::combine(Individual par1, Individual par2)
+	{
+		size_t len = par1.dna.size();
+		Individual child;
+		for (size_t i = 0; i < len; i++)
+		{
+			double p = random_num(0.0, 100.0, 1.0);
+			if (p<45.0)
+			{
+				child.dna.push_back(par1.dna[i]);
+			}
+			else if(p<90.0){
+				child.dna.push_back(par2.dna[i]);
+			}
+			else
+			{
+				child.dna.push_back(mutateGene(_input_limits[i]));
+			}
+		}
+		return child;
+	}
+	OptimizerEvolutionary::Individual OptimizerEvolutionary::generateIndividual()
+	{
+		Individual ret;
+		for (size_t i = 0; i < _inputs.size(); i++)
+		{
+			ret.dna.push_back(mutateGene(_input_limits[i]));
+		}
+		return ret;
 	}
 }

+ 1 - 0
lib/test/CMakeLists.txt

@@ -3,6 +3,7 @@ add_executable(auslegung_test
         test_ModuleMath.cpp
         test_ModuleSQL.cpp
         test_ModuleSwitch.cpp
+	test_OptimizerEvolutionary.cpp
         test_ProcessorStandard.cpp
         )
 target_link_libraries(auslegung_test gtest gtest_main ${PROJECT_NAME})

+ 27 - 0
lib/test/test_OptimizerEvolutionary.cpp

@@ -0,0 +1,27 @@
+#include <gtest/gtest.h>
+#include <json.hpp>
+//#define private public
+#include "OptimizerEvolutionary.h"
+#include <ModuleMath.h>
+#include <ModuleSwitch.h>
+
+
+using namespace mdd;
+TEST(OptimizerEvolutionary, OptimizeSimpleFormula) {
+    //optimize f(x)=x^2
+    std::vector<std::string> inputs;
+    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
+    OptimizerEvolutionary optimizer(f1);
+    inputs = f1->getInputIDs();
+    json limit;
+    limit["min"] = { -10 };
+    limit["max"] = { 10 };
+    limit["step"] = { 1 };
+    optimizer.addModifier(inputs[0], limit);
+    optimizer.addModifier(inputs[1], limit);
+    optimizer.addReader(f1->getOutputIDs()[0]);
+    optimizer.setEvaluation("out0");
+    json res = optimizer.update();
+    std::cout << res.dump() << std::endl;
+    EXPECT_EQ(1,2);
+}