1 | // This file is a part of Framsticks SDK. http://www.framsticks.com/ |
---|
2 | // Copyright (C) 1999-2024 Maciej Komosinski and Szymon Ulatowski. |
---|
3 | // See LICENSE.txt for details. |
---|
4 | |
---|
5 | #include "genman.h" |
---|
6 | #include <frams/vm/classes/genoobj.h> |
---|
7 | #include GEN_CONFIG_FILE //configuration of active genetic operators |
---|
8 | #include "common/log.h" |
---|
9 | #include "common/nonstd_math.h" |
---|
10 | #include "common/util-string.h" |
---|
11 | #include <common/loggers/loggers.h> |
---|
12 | |
---|
13 | |
---|
14 | #define GENMAN_REPEAT_FAILED 100 //how many times GenMan tries to repeat a mutation or crossover when the operator does not return an acceptable genotype |
---|
15 | #define STRINGIFY_1(x) #x |
---|
16 | #define STRINGIFY(x) STRINGIFY_1(x) //this second-level macro allows the parameter to be a macro itself and to stringify its value, not its name |
---|
17 | #define GENMAN_REPEAT_FAILED_STR STRINGIFY(GENMAN_REPEAT_FAILED) |
---|
18 | |
---|
19 | |
---|
20 | #ifdef USE_GENMAN_f0 |
---|
21 | #include "f0/f0_oper.h" |
---|
22 | #endif |
---|
23 | #ifdef USE_GENMAN_f0s |
---|
24 | #include "f0s/f0s_oper.h" |
---|
25 | #endif |
---|
26 | #ifdef USE_GENMAN_f0FUZZY |
---|
27 | #include "f0/f0Fuzzy_oper.h" |
---|
28 | #endif |
---|
29 | #ifdef USE_GENMAN_f1 |
---|
30 | #include "f1/f1_oper.h" |
---|
31 | #endif |
---|
32 | #ifdef USE_GENMAN_f2 |
---|
33 | #include "f2/f2_oper.h" |
---|
34 | #endif |
---|
35 | #ifdef USE_GENMAN_f2 |
---|
36 | #include "f3/f3_oper.h" |
---|
37 | #endif |
---|
38 | #ifdef USE_GENMAN_f4 |
---|
39 | #include "f4/f4_oper.h" |
---|
40 | #endif |
---|
41 | #ifdef USE_GENMAN_f5 |
---|
42 | #include "f5/f5_oper.h" |
---|
43 | #endif |
---|
44 | #ifdef USE_GENMAN_f6 |
---|
45 | #include "f6/f6_oper.h" |
---|
46 | #endif |
---|
47 | #ifdef USE_GENMAN_f7 |
---|
48 | #include "f7/f7_oper.h" |
---|
49 | #endif |
---|
50 | #ifdef USE_GENMAN_f8 |
---|
51 | #include "f8/f8_oper.h" |
---|
52 | #endif |
---|
53 | #ifdef USE_GENMAN_f9 |
---|
54 | #include "f9/f9_oper.h" |
---|
55 | #endif |
---|
56 | #ifdef USE_GENMAN_fF |
---|
57 | #include "fF/fF_oper.h" |
---|
58 | #endif |
---|
59 | #ifdef USE_GENMAN_fn |
---|
60 | #include "fn/fn_oper.h" |
---|
61 | #endif |
---|
62 | #ifdef USE_GENMAN_fT |
---|
63 | #include "fT/fTest_oper.h" |
---|
64 | #endif |
---|
65 | #ifdef USE_GENMAN_fB |
---|
66 | #include "fB/fB_oper.h" |
---|
67 | #endif |
---|
68 | #ifdef USE_GENMAN_fH |
---|
69 | #include "fH/fH_oper.h" |
---|
70 | #endif |
---|
71 | #ifdef USE_GENMAN_fL |
---|
72 | #include "fL/fL_oper.h" |
---|
73 | #endif |
---|
74 | #ifdef USE_GENMAN_fS |
---|
75 | #include "fS/fS_oper.h" |
---|
76 | #endif |
---|
77 | |
---|
78 | using namespace std; //string, vector |
---|
79 | |
---|
80 | //old code needs update: |
---|
81 | //#include "gengroups.h" |
---|
82 | //extern GenGroup *listaGen; |
---|
83 | // GENGROUP(0)->l_del.add(sim->GM.onDelGen,&sim->GM); //before delete |
---|
84 | // GENGROUP(0)->l_del.remove(sim->GM.onDelGen,&sim->GM); //before delete |
---|
85 | |
---|
86 | |
---|
87 | #define FIELDSTRUCT GenMan |
---|
88 | |
---|
89 | static ParamEntry GMparam_tab[] = |
---|
90 | { |
---|
91 | { "Genetics", 1, 11, "GenMan", }, |
---|
92 | { "gen_hist", 0, PARAM_DONTSAVE, "Remember history of genetic operations", "d 0 1 0", FIELD(history), "Required for phylogenetic analysis", }, |
---|
93 | { "gen_hilite", 0, 0, "Use syntax highlighting", "d 0 1 1", FIELD(hilite), "Use colors for genes?\n(slows down viewing/editing of huge genotypes)", }, |
---|
94 | { "gen_extmutinfo", 0, 0, "Extended mutation info", "d 0 2 0 ~Off~Method ID~Method description", FIELD(extmutinfo), "If active, information about employed mutation method will be stored in the 'info' field of each mutated genotype.", }, |
---|
95 | { "operReport", 0, PARAM_DONTSAVE, "Operators report", "p()", PROCEDURE(p_report), "Show available genetic operators", }, |
---|
96 | { "toHTML", 0, PARAM_DONTSAVE, "HTMLize a genotype", "p s(s)", PROCEDURE(p_htmlize), "returns genotype expressed as colored HTML", }, |
---|
97 | { "toHTMLshort", 0, PARAM_DONTSAVE, "HTMLize a genotype, shorten if needed", "p s(s)", PROCEDURE(p_htmlizeshort), "returns genotype (abbreviated if needed) in colored HTML format", }, |
---|
98 | { "toLaTeX", 0, PARAM_DONTSAVE, "LaTeXize a genotype", "p s(s)", PROCEDURE(p_latexize), "returns genotype in colored LaTeX format", }, |
---|
99 | { "validate", 0, PARAM_DONTSAVE | PARAM_USERHIDDEN, "Validate", "p oGeno(oGeno)", PROCEDURE(p_validate), "returns validated (if possible) Geno object from supplied Geno", }, |
---|
100 | { "mutate", 0, PARAM_DONTSAVE | PARAM_USERHIDDEN, "Mutate", "p oGeno(oGeno)", PROCEDURE(p_mutate), "returns mutated Geno object from supplied Geno", }, |
---|
101 | { "crossOver", 0, PARAM_DONTSAVE | PARAM_USERHIDDEN, "Crossover", "p oGeno(oGeno,oGeno)", PROCEDURE(p_crossover), "returns crossed over genotype", }, |
---|
102 | { "getSimplest", 0, PARAM_DONTSAVE | PARAM_USERHIDDEN, "Get simplest genotype", "p oGeno(s format)", PROCEDURE(p_getsimplest), "returns the simplest genotype for a given encoding (format). \"0\" means f0, \"4\" means f4, etc.", }, |
---|
103 | { 0, }, |
---|
104 | }; |
---|
105 | |
---|
106 | static ParamEntry GMstats_tab[] = |
---|
107 | { |
---|
108 | { "Genetics", 1, 12, "GenManStats", "Statistics for genetic operations." }, |
---|
109 | { "gen_count", 0, PARAM_READONLY, "Number of genetic operations so far", "d", FIELD(count), "", }, |
---|
110 | { "gen_mvalid", 0, PARAM_READONLY, "Mutations valid", "d", FIELD(valid_m), "", }, |
---|
111 | { "gen_mvalidated", 0, PARAM_READONLY, "Mutations validated", "d", FIELD(validated_m), "", }, |
---|
112 | { "gen_minvalid", 0, PARAM_READONLY, "Mutations invalid", "d", FIELD(invalid_m), "couldn't be repaired", }, |
---|
113 | { "gen_mfailed", 0, PARAM_READONLY, "Mutations failed", "d", FIELD(failed_m), "couldn't be performed", }, |
---|
114 | { "gen_xovalid", 0, PARAM_READONLY, "Crossovers valid", "d", FIELD(valid_xo), "", }, |
---|
115 | { "gen_xovalidated", 0, PARAM_READONLY, "Crossovers validated", "d", FIELD(validated_xo), "", }, |
---|
116 | { "gen_xoinvalid", 0, PARAM_READONLY, "Crossovers invalid", "d", FIELD(invalid_xo), "couldn't be repaired", }, |
---|
117 | { "gen_xofailed", 0, PARAM_READONLY, "Crossovers failed", "d", FIELD(failed_xo), "couldn't be performed", }, |
---|
118 | { "gen_mutimpr", 0, PARAM_READONLY, "Mutations total effect", "f", FIELD(mutchg), "total cumulative mutation change", }, |
---|
119 | { "gen_xoimpr", 0, PARAM_READONLY, "Crossovers total effect", "f", FIELD(xochg), "total cumulative crossover change", }, |
---|
120 | { "clrstats", 0, PARAM_DONTSAVE, "Clear stats and history", "p()", PROCEDURE(p_clearStats), "", }, |
---|
121 | { 0, }, |
---|
122 | }; |
---|
123 | |
---|
124 | #undef FIELDSTRUCT |
---|
125 | |
---|
126 | GenMan::GenMan() : localpar(GMparam_tab, this), localstats(GMstats_tab, this), |
---|
127 | seloperpar("GenOperators", "Genetics: Active operators"), |
---|
128 | neuronsparam("Genetics: Neurons to add", "neuronsAdd", "neuadd_"), |
---|
129 | par("GenMan", "Manages various genetic operations, using appropriate operators for the argument genotype format.") |
---|
130 | { |
---|
131 | history = 0; |
---|
132 | hilite = 1; |
---|
133 | clearStats(); |
---|
134 | |
---|
135 | #ifdef USE_GENMAN_f0 |
---|
136 | oper_fx_list.push_back(new Geno_f0); |
---|
137 | #endif |
---|
138 | #ifdef USE_GENMAN_f0s |
---|
139 | oper_fx_list.push_back(new Geno_f0s); |
---|
140 | #endif |
---|
141 | #ifdef USE_GENMAN_f0FUZZY |
---|
142 | oper_fx_list.push_back(new Geno_f0Fuzzy); |
---|
143 | #endif |
---|
144 | #ifdef USE_GENMAN_f1 |
---|
145 | oper_fx_list.push_back(new Geno_f1); |
---|
146 | #endif |
---|
147 | #ifdef USE_GENMAN_f2 |
---|
148 | oper_fx_list.push_back(new Geno_f2); |
---|
149 | #endif |
---|
150 | #ifdef USE_GENMAN_f3 |
---|
151 | oper_fx_list.push_back(new Geno_f3); |
---|
152 | #endif |
---|
153 | #ifdef USE_GENMAN_f4 |
---|
154 | oper_fx_list.push_back(new Geno_f4); |
---|
155 | #endif |
---|
156 | #ifdef USE_GENMAN_f5 |
---|
157 | oper_fx_list.push_back(new Geno_f5); |
---|
158 | #endif |
---|
159 | #ifdef USE_GENMAN_f6 |
---|
160 | oper_fx_list.push_back(new Geno_f6); |
---|
161 | #endif |
---|
162 | #ifdef USE_GENMAN_f7 |
---|
163 | oper_fx_list.push_back(new Geno_f7); |
---|
164 | #endif |
---|
165 | #ifdef USE_GENMAN_f8 |
---|
166 | oper_fx_list.push_back(new Geno_f8); |
---|
167 | #endif |
---|
168 | #ifdef USE_GENMAN_f9 |
---|
169 | oper_fx_list.push_back(new GenoOper_f9); |
---|
170 | #endif |
---|
171 | #ifdef USE_GENMAN_fF |
---|
172 | oper_fx_list.push_back(new GenoOper_fF); |
---|
173 | #endif |
---|
174 | #ifdef USE_GENMAN_fn |
---|
175 | oper_fx_list.push_back(new GenoOper_fn); |
---|
176 | #endif |
---|
177 | #ifdef USE_GENMAN_fT |
---|
178 | oper_fx_list.push_back(new GenoOper_fTest); |
---|
179 | #endif |
---|
180 | #ifdef USE_GENMAN_fB |
---|
181 | oper_fx_list.push_back(new Geno_fB); |
---|
182 | #endif |
---|
183 | #ifdef USE_GENMAN_fH |
---|
184 | oper_fx_list.push_back(new Geno_fH); |
---|
185 | #endif |
---|
186 | #ifdef USE_GENMAN_fL |
---|
187 | oper_fx_list.push_back(new Geno_fL); |
---|
188 | #endif |
---|
189 | #ifdef USE_GENMAN_fS |
---|
190 | oper_fx_list.push_back(new GenoOper_fS); |
---|
191 | #endif |
---|
192 | |
---|
193 | seloper = new int[oper_fx_list.size()]; //may result in a little overhead if some of the operators on the oper_fx_list concern the same genetic format |
---|
194 | int selopercount = 0; |
---|
195 | for (unsigned int i = 0; i < oper_fx_list.size(); i++) |
---|
196 | { |
---|
197 | if (findOperFormatIndex(oper_fx_list[i]->supported_format) != -1) continue; |
---|
198 | string type = string("~") + oper_fx_list[i]->name; |
---|
199 | int dup = 0; |
---|
200 | for (unsigned int j = i + 1; j < oper_fx_list.size(); j++) |
---|
201 | if (oper_fx_list[i]->supported_format == oper_fx_list[j]->supported_format) |
---|
202 | { |
---|
203 | type += "~"; |
---|
204 | type += oper_fx_list[j]->name; |
---|
205 | dup++; |
---|
206 | } |
---|
207 | type = ssprintf("d 0 %d ", dup) + type; |
---|
208 | string id = ssprintf("genoper_f%s", oper_fx_list[i]->supported_format.c_str()); |
---|
209 | string name = ssprintf("Operators for f%s", oper_fx_list[i]->supported_format.c_str()); |
---|
210 | seloper[selopercount] = 0; |
---|
211 | operformats += &oper_fx_list[i]->supported_format; |
---|
212 | //printf("%x %s %s %s\n",&seloper[selopercount],(const char*)id,(const char*)type,(const char*)name); |
---|
213 | seloperpar.addProperty(&seloper[selopercount++], id.c_str(), type.c_str(), name.c_str(), "", PARAM_READONLY * (dup == 0)); |
---|
214 | } |
---|
215 | |
---|
216 | par += &localpar; |
---|
217 | par += &seloperpar; |
---|
218 | par += &neuronsparam; |
---|
219 | for (unsigned int i = 0; i < oper_fx_list.size(); i++) |
---|
220 | if (oper_fx_list[i]->par.getParamTab()) par += &oper_fx_list[i]->par; |
---|
221 | |
---|
222 | setDefaults(); //use Param to initialize all values of fields in the paramtab of this object and genetic operators on oper_fx_list |
---|
223 | } |
---|
224 | |
---|
225 | GenMan::~GenMan() |
---|
226 | { |
---|
227 | for (unsigned int i = 0; i < oper_fx_list.size(); i++) delete oper_fx_list[i]; |
---|
228 | delete[] seloper; |
---|
229 | } |
---|
230 | |
---|
231 | int GenMan::findOperFormatIndex(const SString& format) |
---|
232 | { |
---|
233 | for (int i = 0; i < operformats.size(); i++) |
---|
234 | if (*operformats(i) == format) |
---|
235 | return i; |
---|
236 | return -1; |
---|
237 | } |
---|
238 | |
---|
239 | void GenMan::setDefaults() |
---|
240 | { |
---|
241 | for (unsigned int i = 0; i < oper_fx_list.size(); i++) |
---|
242 | { |
---|
243 | oper_fx_list[i]->par.setDefault(); |
---|
244 | oper_fx_list[i]->setDefaults(); |
---|
245 | } |
---|
246 | localpar.setDefault(); |
---|
247 | //...and we do not reset others that are linked to 'par', |
---|
248 | //because there quite a few of them, and not every of them defines defaults for each of its parameters. |
---|
249 | } |
---|
250 | |
---|
251 | int GenMan::testValidity(Geno &g, bool &canvalidate) |
---|
252 | { |
---|
253 | SString ggs = g.getGenes(); |
---|
254 | const char *gg = ggs.c_str(); |
---|
255 | GenoOperators *gf = getOper_f(g.getFormat()); |
---|
256 | int check1; |
---|
257 | if (!gf) { canvalidate = false; return GENOPER_NOOPER; } |
---|
258 | else check1 = gf->checkValidity(gg, g.getName().c_str()); |
---|
259 | if (!canvalidate) return check1; //just checking |
---|
260 | if (check1 == GENOPER_OK) { canvalidate = false; return check1; } |
---|
261 | char *g2 = strdup(gg); |
---|
262 | if (gf->validate(g2, g.getName().c_str()) == GENOPER_NOOPER) { free(g2); canvalidate = false; return check1; } |
---|
263 | if (check1 == GENOPER_NOOPER) //disaster: cannot check because there is no check operator |
---|
264 | { |
---|
265 | g.setGenesAssumingSameFormat(g2); free(g2); canvalidate = false; return GENOPER_NOOPER; |
---|
266 | } |
---|
267 | int check2 = gf->checkValidity(g2, "validated"); |
---|
268 | if (check2 == GENOPER_OK) g.setGenesAssumingSameFormat(g2); |
---|
269 | free(g2); |
---|
270 | if (check2 == GENOPER_OK) return check1; |
---|
271 | canvalidate = false; |
---|
272 | return check1; //could not validate. |
---|
273 | } |
---|
274 | |
---|
275 | int GenMan::testGenoValidity(Geno& g) |
---|
276 | { |
---|
277 | bool fix = false; |
---|
278 | switch (testValidity(g, fix)) |
---|
279 | { |
---|
280 | case GENOPER_OK: return 1; |
---|
281 | case GENOPER_NOOPER: return -1; |
---|
282 | default: return 0; |
---|
283 | } |
---|
284 | } |
---|
285 | |
---|
286 | Geno GenMan::validate(const Geno& geny) |
---|
287 | { |
---|
288 | SString format = geny.getFormat(); |
---|
289 | GenoOperators *gf = getOper_f(format); |
---|
290 | if (gf == NULL) |
---|
291 | return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: Validate(): don't know how to handle genetic format %s", format.c_str())); |
---|
292 | char *g2 = strdup(geny.getGenes().c_str()); //copy for validation |
---|
293 | int res = gf->validate(g2, geny.getName().c_str()); |
---|
294 | SString sg2 = g2; |
---|
295 | free(g2); |
---|
296 | if (res == GENOPER_OK) |
---|
297 | return Geno(sg2, format, geny.getName(), geny.getComment()); |
---|
298 | else |
---|
299 | return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: validate() for format %s returned invalid value", format.c_str())); |
---|
300 | } |
---|
301 | |
---|
302 | Geno GenMan::mutate(const Geno& g) |
---|
303 | { |
---|
304 | float chg; //how many changes |
---|
305 | int method; //mutation method |
---|
306 | SString format = g.getFormat(); |
---|
307 | GenoOperators *gf = getOper_f(format); |
---|
308 | if (gf == NULL) |
---|
309 | return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: Mutate(): don't know how to handle genetic format %s", format.c_str())); |
---|
310 | Geno gv = g; |
---|
311 | bool canvalidate = true; |
---|
312 | if (testValidity(gv, canvalidate) > 0 && canvalidate == false) |
---|
313 | return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: Mutate(): cannot validate invalid source genotype"); |
---|
314 | bool ok = false; |
---|
315 | int pcount = count; |
---|
316 | while (!ok) |
---|
317 | { |
---|
318 | char *gn = strdup(gv.getGenes().c_str()); //copy for mutation |
---|
319 | chg = 0; |
---|
320 | if (gf->mutate(gn, chg, method) == GENOPER_OK) |
---|
321 | { |
---|
322 | LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreFirstMessage); //mute testValidity() |
---|
323 | Geno G(gn, gv.getFormat(), "", ""); |
---|
324 | canvalidate = true; |
---|
325 | int res = testValidity(G, canvalidate); |
---|
326 | if (res == GENOPER_OK && canvalidate == false) { valid_m++; ok = true; } |
---|
327 | else |
---|
328 | if (res > 0 && canvalidate == false) invalid_m++; else |
---|
329 | { |
---|
330 | validated_m++; ok = true; |
---|
331 | } |
---|
332 | if (ok) gv = G; |
---|
333 | } |
---|
334 | else failed_m++; |
---|
335 | free(gn); |
---|
336 | count++; |
---|
337 | if (!ok && (count - pcount > GENMAN_REPEAT_FAILED)) |
---|
338 | { |
---|
339 | logPrintf("GenMan", "Mutate", LOG_WARN, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s", g.getGenes().c_str()); |
---|
340 | return Geno("", -1, "", "GENOPER_OPFAIL: Mutate() tried " GENMAN_REPEAT_FAILED_STR "x and failed"); |
---|
341 | } |
---|
342 | } |
---|
343 | mutchg += chg; |
---|
344 | if (history) saveLink(g.getGenes().c_str(), "", gv.getGenes().c_str(), chg); |
---|
345 | SString mutinfo; |
---|
346 | if (extmutinfo == 0) mutinfo = SString::sprintf("%.2f%% mutation of '%s'", 100 * chg, g.getName().c_str()); else |
---|
347 | if (extmutinfo == 1) mutinfo = SString::sprintf("%.2f%% mutation(%d) of '%s'", 100 * chg, method, g.getName().c_str()); else |
---|
348 | mutinfo = SString::sprintf("%.2f%% mutation(%s) of '%s'", 100 * chg, gf->mutation_method_names ? gf->mutation_method_names[method] : "unspecified method name", g.getName().c_str()); |
---|
349 | gv.setComment(mutinfo); |
---|
350 | return gv; |
---|
351 | } |
---|
352 | |
---|
353 | Geno GenMan::crossOver(const Geno& g1, const Geno& g2) |
---|
354 | { |
---|
355 | SString format = g1.getFormat(); |
---|
356 | if (format != g2.getFormat()) return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: CrossOver(): does not work for parents with differing genetic formats (%s and %s)", format.c_str(), g2.getFormat().c_str())); |
---|
357 | GenoOperators *gf = getOper_f(format); |
---|
358 | if (gf == NULL) |
---|
359 | return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: CrossOver(): no operators found for genetic format %s", format.c_str())); |
---|
360 | Geno g1v = g1, g2v = g2; |
---|
361 | |
---|
362 | { |
---|
363 | LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreFirstMessage); //mute testValidity() |
---|
364 | bool canvalidate = true; |
---|
365 | if (testValidity(g1v, canvalidate) > 0 && canvalidate == false) |
---|
366 | return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #1"); |
---|
367 | canvalidate = true; |
---|
368 | if (testValidity(g2v, canvalidate) > 0 && canvalidate == false) |
---|
369 | return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #2"); |
---|
370 | } |
---|
371 | |
---|
372 | float chg; //fraction of parent1 genes in the child |
---|
373 | bool ok = false; |
---|
374 | int pcount = count; |
---|
375 | |
---|
376 | while (!ok) |
---|
377 | { |
---|
378 | float chg1, chg2; |
---|
379 | char *g1n = strdup(g1.getGenes().c_str()); //copy for crossover |
---|
380 | char *g2n = strdup(g2.getGenes().c_str()); //copy for crossover |
---|
381 | chg1 = chg2 = 0; |
---|
382 | bool swap_parents = rndUint(2) == 0; //some implementations of crossover may be not entirely symmetric (f0 is an example). This is not ideal, because crossover(p1,p2) may demonstrate slightly different characteristics or biases than crossover(p2,p1) - maybe extremely rarely, maybe in corner or error cases, but still. Since we assume that "the order of parents should not matter for crossover", here we randomize the order of parents passed to crossover to balance the characteristics. |
---|
383 | if (swap_parents) std::swap(g1n, g2n); |
---|
384 | if (gf->crossOver(g1n, g2n, chg1, chg2) == GENOPER_OK) |
---|
385 | { |
---|
386 | if (swap_parents) { std::swap(g1n, g2n); std::swap(chg1, chg2); } //gf->crossOver() got swapped parents, but we want to keep referring children and change fractions to the original parent order, so we restore the original order in the outcomes of gf->crossOver(). g1n and g2n are now children (or could be one child if gf->crossOver() provided only one). |
---|
387 | char *gn; |
---|
388 | if (g1n[0] && g2n[0]) if (rndUint(2) == 0) g1n[0] = 0; else g2n[0] = 0; //both provided? we want only one |
---|
389 | if (g1n[0]) { gn = g1n; chg = chg1; } |
---|
390 | else { gn = g2n; chg = 1 - chg2; } |
---|
391 | LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreFirstMessage); //mute testValidity() |
---|
392 | Geno G(gn, g1v.getFormat(), "", ""); |
---|
393 | bool canvalidate = true; |
---|
394 | int res = testValidity(G, canvalidate); |
---|
395 | if (res == GENOPER_OK && canvalidate == false) { valid_xo++; ok = true; } |
---|
396 | else |
---|
397 | if (res > 0 && canvalidate == false) invalid_xo++; else |
---|
398 | { |
---|
399 | validated_xo++; ok = true; |
---|
400 | } |
---|
401 | if (ok) g1v = G; |
---|
402 | } |
---|
403 | else failed_xo++; |
---|
404 | free(g1n); |
---|
405 | free(g2n); |
---|
406 | count++; |
---|
407 | if (!ok && (count - pcount > GENMAN_REPEAT_FAILED)) |
---|
408 | { |
---|
409 | logPrintf("GenMan", "CrossOver", LOG_WARN, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s and %s", g1.getGenes().c_str(), g2.getGenes().c_str()); |
---|
410 | return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: CrossOver() tried " GENMAN_REPEAT_FAILED_STR "x and failed"); |
---|
411 | } |
---|
412 | } |
---|
413 | // result in g1v |
---|
414 | xochg += chg; |
---|
415 | if (history) saveLink(g1.getGenes().c_str(), g2.getGenes().c_str(), g1v.getGenes().c_str(), chg); |
---|
416 | SString xoinfo = SString::sprintf("Crossing over of '%s' (%.2f%%) and '%s' (%.2f%%)", |
---|
417 | g1.getName().c_str(), 100 * chg, g2.getName().c_str(), 100 * (1 - chg)); |
---|
418 | g1v.setComment(xoinfo); |
---|
419 | return g1v; |
---|
420 | } |
---|
421 | |
---|
422 | float GenMan::similarity(const Geno& g1, const Geno& g2) |
---|
423 | { |
---|
424 | SString format = g1.getFormat(); |
---|
425 | if (format != g2.getFormat()) return GENOPER_NOOPER; |
---|
426 | GenoOperators *gf = getOper_f(format); |
---|
427 | if (!gf) return GENOPER_NOOPER; else return gf->similarity(g1.getGenes().c_str(), g2.getGenes().c_str()); |
---|
428 | } |
---|
429 | |
---|
430 | uint32_t GenMan::getStyle(const char *g, const Geno *G, int pos) |
---|
431 | { |
---|
432 | SString format = G->getFormat(); |
---|
433 | if (format == Geno::FORMAT_INVALID) |
---|
434 | return GENSTYLE_RGBS(64, 64, 64, 0); // gray & "valid" (unknown format so we don't know what is valid and what is not) |
---|
435 | if ((pos = G->mapStringToGen(pos)) == -1) return GENSTYLE_COMMENT; |
---|
436 | GenoOperators *gf = getOper_f(format); |
---|
437 | if (!gf) return GENSTYLE_CS(0, 0); //black & valid |
---|
438 | else return gf->style(G->getGenes().c_str(), pos); |
---|
439 | } |
---|
440 | |
---|
441 | uint32_t GenMan::getStyle(const char *g, int pos) |
---|
442 | { |
---|
443 | Geno G(g); |
---|
444 | return getStyle(g, &G, pos); |
---|
445 | } |
---|
446 | |
---|
447 | void GenMan::getFullStyle(const char *g, const Geno *G, uint32_t *styletab) |
---|
448 | { |
---|
449 | SString format = G->getFormat(); |
---|
450 | if (format == Geno::FORMAT_INVALID) |
---|
451 | { |
---|
452 | for (unsigned int pos = 0; pos < strlen(g); pos++) |
---|
453 | styletab[pos] = GENSTYLE_RGBS(64, 64, 64, 0); // gray & "valid" (unknown format so we don't know what is valid and what is not) |
---|
454 | return; |
---|
455 | } |
---|
456 | GenoOperators *gf = getOper_f(format); |
---|
457 | SString geny = G->getGenes(); |
---|
458 | for (unsigned int pos = 0; pos < strlen(g); pos++) |
---|
459 | { |
---|
460 | int posmapped = G->mapStringToGen(pos); |
---|
461 | if (posmapped == -1) styletab[pos] = GENSTYLE_COMMENT; |
---|
462 | else if (!gf) styletab[pos] = GENSTYLE_CS(0, 0); //black & valid |
---|
463 | else styletab[pos] = gf->style(geny.c_str(), posmapped); |
---|
464 | //logPrintf("GenMan", "getFullStyle", LOG_INFO, "%d char='%c' (%d) format=0x%08x", pos, g[pos], g[pos], styletab[pos]); |
---|
465 | } |
---|
466 | } |
---|
467 | |
---|
468 | void GenMan::getFullStyle(const char *g, uint32_t *styletab) |
---|
469 | { |
---|
470 | Geno G(g); |
---|
471 | getFullStyle(g, &G, styletab); |
---|
472 | } |
---|
473 | |
---|
474 | string GenMan::HTMLize(const char *g) { return HTMLize(g, false); } |
---|
475 | |
---|
476 | string GenMan::HTMLizeShort(const char *g) { return HTMLize(g, true); } |
---|
477 | |
---|
478 | string GenMan::HTMLize(const char *g, bool shorten) |
---|
479 | { |
---|
480 | char buf[50]; |
---|
481 | int len = int(strlen(g)); |
---|
482 | int chars = 0, lines = 0; |
---|
483 | bool shortened = false; |
---|
484 | uint32_t *styletab = new uint32_t[len]; |
---|
485 | getFullStyle(g, styletab); |
---|
486 | string html = "<style>" |
---|
487 | "span.geno{background:white; padding:0.2em; font-family:arial,helvetica,sans-serif}" |
---|
488 | "</style>\n\n"; |
---|
489 | html += "<span class=\"geno\">"; |
---|
490 | uint32_t prevstyle, prevcolor, style = 0, color = 0; |
---|
491 | for (int i = 0; i < len; i++) |
---|
492 | { |
---|
493 | if (shorten && ((lines == 0 && chars > 160) || (lines > 5 || chars > 300))) { shortened = true; break; } |
---|
494 | if (g[i] == '\r') continue; |
---|
495 | if (g[i] == '\n') { html += "<br>\n"; lines++; continue; } |
---|
496 | chars++; |
---|
497 | prevstyle = style; |
---|
498 | prevcolor = color; |
---|
499 | style = GENGETSTYLE(styletab[i]); |
---|
500 | color = GENGETCOLOR(styletab[i]); |
---|
501 | if ((i != 0 && (color != prevcolor))) html += "</font>"; |
---|
502 | if ((style & GENSTYLE_INVALID) != (prevstyle & GENSTYLE_INVALID)) |
---|
503 | { |
---|
504 | html += "<"; if (!(style & GENSTYLE_INVALID)) html += "/"; html += "u>"; |
---|
505 | } |
---|
506 | if ((style & GENSTYLE_BOLD) != (prevstyle & GENSTYLE_BOLD)) |
---|
507 | { |
---|
508 | html += "<"; if (!(style & GENSTYLE_BOLD)) html += "/"; html += "b>"; |
---|
509 | } |
---|
510 | if ((style & GENSTYLE_ITALIC) != (prevstyle & GENSTYLE_ITALIC)) |
---|
511 | { |
---|
512 | html += "<"; if (!(style & GENSTYLE_ITALIC)) html += "/"; html += "i>"; |
---|
513 | } |
---|
514 | if ((i == 0 || (color != prevcolor))) |
---|
515 | { |
---|
516 | sprintf(buf, "<font color=#%02x%02x%02x>", GENGET_R(color), GENGET_G(color), GENGET_B(color)); html += buf; |
---|
517 | } |
---|
518 | if (g[i] == '<') html += "<"; else if (g[i] == '>') html += ">"; else html += g[i]; |
---|
519 | if ((i % 3) == 0 && g[i] == ' ') html += "\n"; //for readability, insert some newlines into html... |
---|
520 | } |
---|
521 | delete[] styletab; |
---|
522 | html += "</u></b></i></font>"; |
---|
523 | if (shortened) html += " [etc...]"; |
---|
524 | html += "</span>\n"; |
---|
525 | return html; |
---|
526 | } |
---|
527 | |
---|
528 | void GenMan::p_htmlize(ExtValue *args, ExtValue *ret) |
---|
529 | { |
---|
530 | ret->setString(HTMLize(args->getString().c_str()).c_str()); |
---|
531 | } |
---|
532 | |
---|
533 | void GenMan::p_htmlizeshort(ExtValue *args, ExtValue *ret) |
---|
534 | { |
---|
535 | ret->setString(HTMLizeShort(args->getString().c_str()).c_str()); |
---|
536 | } |
---|
537 | |
---|
538 | string GenMan::LaTeXize(const char *g) |
---|
539 | { |
---|
540 | char buf[50]; |
---|
541 | int len = int(strlen(g)); |
---|
542 | int chars = 0, lines = 0; //currently not used |
---|
543 | uint32_t *styletab = new uint32_t[len]; |
---|
544 | getFullStyle(g, styletab); |
---|
545 | string latex = "\\usepackage{xcolor}\n% Using \\texttt{} may be beneficial for some genetic encodings, but then you may lose bold/italic.\n\\noindent \\sloppy"; |
---|
546 | uint32_t prevstyle, prevcolor, style = 0, color = 0; |
---|
547 | for (int i = 0; i < len; i++) |
---|
548 | { |
---|
549 | if (g[i] == '\r') continue; |
---|
550 | if (g[i] == '\n') { latex += "\\\\\n"; lines++; continue; } |
---|
551 | chars++; |
---|
552 | prevstyle = style; |
---|
553 | prevcolor = color; |
---|
554 | style = GENGETSTYLE(styletab[i]); |
---|
555 | color = GENGETCOLOR(styletab[i]); |
---|
556 | |
---|
557 | // Unfortunately, LaTeX (as opposed to HTML) uses the same closing tags "}" for color, bold, italic, underline - so they cannot intersect. |
---|
558 | // Therefore we have to "turn off" everything on every change of style or color, and then "turn on" (to avoid problems with e.g. red-bold-blue-unbold or bold-italic-unbold-unitalic). |
---|
559 | // This could be optimized by a more complex logic and tracking which color/style section starts and ends within another section. |
---|
560 | |
---|
561 | if (((style & GENSTYLE_INVALID) != (prevstyle & GENSTYLE_INVALID)) |
---|
562 | || |
---|
563 | ((style & GENSTYLE_BOLD) != (prevstyle & GENSTYLE_BOLD)) |
---|
564 | || |
---|
565 | ((style & GENSTYLE_ITALIC) != (prevstyle & GENSTYLE_ITALIC)) |
---|
566 | || |
---|
567 | ((i == 0 || (color != prevcolor)))) |
---|
568 | { |
---|
569 | if (prevstyle & GENSTYLE_INVALID) latex += "}"; |
---|
570 | if (prevstyle & GENSTYLE_BOLD) latex += "}"; |
---|
571 | if (prevstyle & GENSTYLE_ITALIC) latex += "}"; |
---|
572 | if (i != 0) latex += "}"; //for color |
---|
573 | if (style & GENSTYLE_INVALID) latex += "\\underline{"; |
---|
574 | if (style & GENSTYLE_BOLD) latex += "\\textbf{"; |
---|
575 | if (style & GENSTYLE_ITALIC) latex += "\\textit{"; |
---|
576 | sprintf(buf, "\\textcolor[rgb]{%.2g,%.2g,%.2g}{", GENGET_R(color) / 255.0, GENGET_G(color) / 255.0, GENGET_B(color) / 255.0); latex += buf; |
---|
577 | } |
---|
578 | if (g[i] == '<') latex += "$<$"; else if (g[i] == '>') latex += "$>$"; else |
---|
579 | if (g[i] == '-') latex += "$-$"; else if (g[i] == '|') latex += "$|$"; else |
---|
580 | if (g[i] == '$') latex += "\\$"; else if (g[i] == '%') latex += "\\%"; else |
---|
581 | if (g[i] == '#') latex += "\\#"; else latex += g[i]; |
---|
582 | if ((i % 3) == 0 && g[i] == ' ') latex += "\n"; //for readability, insert some newlines into latex... |
---|
583 | if (i % 10 == 0) latex += "{\\hskip 0pt}"; // https://tex.stackexchange.com/questions/33526/automatic-line-breaking-of-long-lines-of-text |
---|
584 | } |
---|
585 | delete[] styletab; |
---|
586 | latex += "}"; //for color (it was used at least once) |
---|
587 | if (style & GENSTYLE_INVALID) latex += "}"; |
---|
588 | if (style & GENSTYLE_BOLD) latex += "}"; |
---|
589 | if (style & GENSTYLE_ITALIC) latex += "}"; |
---|
590 | latex += "\n"; |
---|
591 | return latex; |
---|
592 | } |
---|
593 | |
---|
594 | void GenMan::p_latexize(ExtValue *args, ExtValue *ret) |
---|
595 | { |
---|
596 | ret->setString(LaTeXize(args->getString().c_str()).c_str()); |
---|
597 | } |
---|
598 | |
---|
599 | Geno GenMan::getSimplest(const SString& format) |
---|
600 | { |
---|
601 | GenoOperators *gf = getOper_f(format); |
---|
602 | if (!gf) return Geno(); |
---|
603 | string info = "The simplest genotype of format f"; info += format.c_str(); |
---|
604 | info += " for operators '"; info += gf->name; info += "'."; |
---|
605 | return Geno(gf->getSimplest(), format, "Root", info.c_str()); |
---|
606 | } |
---|
607 | |
---|
608 | void GenMan::p_getsimplest(ExtValue *args, ExtValue *ret) |
---|
609 | { |
---|
610 | SString format = GenoObj::formatFromExtValue(args[0]); |
---|
611 | if (!getOper_f(format)) |
---|
612 | ret->setEmpty(); |
---|
613 | else |
---|
614 | *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(getSimplest(format))); |
---|
615 | } |
---|
616 | |
---|
617 | const char *GenMan::getOpName(const SString& format) |
---|
618 | { |
---|
619 | GenoOperators *gf = getOper_f(format); |
---|
620 | if (!gf) return "n/a"; else return gf->name.c_str(); |
---|
621 | } |
---|
622 | |
---|
623 | GenoOperators* GenMan::getOper_f(const SString& format) |
---|
624 | { |
---|
625 | int ind = findOperFormatIndex(format); |
---|
626 | if (ind == -1) return NULL; |
---|
627 | int which_oper_of_format = seloper[ind]; |
---|
628 | for (unsigned int i = 0; i < oper_fx_list.size(); i++) |
---|
629 | if (oper_fx_list[i]->supported_format == format) |
---|
630 | if (which_oper_of_format == 0) return oper_fx_list[i]; else which_oper_of_format--; |
---|
631 | return NULL; //should never happen |
---|
632 | } |
---|
633 | |
---|
634 | void GenMan::saveLink(const string parent1, const string parent2, const string child, const float chg) |
---|
635 | { |
---|
636 | GenoLink l; |
---|
637 | l.count = count; |
---|
638 | l.parent1 = parent1; |
---|
639 | l.parent2 = parent2; |
---|
640 | l.child = child; |
---|
641 | l.chg = chg; |
---|
642 | l.fit = 0; //temporarily. Will be set when the genotype dies |
---|
643 | //logPrintf("GenMan","saveLink",0,"#%d: [%d] '%s' + '%s' -> '%s'",GenoLinkList.size(),count,parent1.c_str(),parent2.c_str(),child.c_str()); |
---|
644 | GenoLinkList.push_back(l); |
---|
645 | } |
---|
646 | |
---|
647 | void GenMan::onDelGen(void *obj, intptr_t n) |
---|
648 | { |
---|
649 | //old code needs update: |
---|
650 | // ((SpeciesList*)obj)->przyDodaniu(i); |
---|
651 | /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
---|
652 | GenMan *gm=(GenMan*)obj; |
---|
653 | Genotype *gt=(Genotype*)(*listaGen)(n); //there is no more "listaGen" |
---|
654 | string g=(const char*)gt->genotype.getGene(); |
---|
655 | float fit=gt->getFinalFitness(); |
---|
656 | for(int i=0;i<gm->GenoLinkList.size();i++) //find genotype |
---|
657 | if (gm->GenoLinkList[i].g1==g) {gm->GenoLinkList[i].fit=fit; break;} |
---|
658 | */ |
---|
659 | } |
---|
660 | |
---|
661 | void GenMan::clearStats() |
---|
662 | { |
---|
663 | count = 0; |
---|
664 | valid_m = valid_xo = validated_m = validated_xo = invalid_m = invalid_xo = failed_m = failed_xo = 0; |
---|
665 | mutchg = xochg = 0; |
---|
666 | GenoLinkList.clear(); |
---|
667 | } |
---|
668 | |
---|
669 | void GenMan::p_clearStats(ExtValue *args, ExtValue *ret) { clearStats(); } |
---|
670 | |
---|
671 | void GenMan::p_report(ExtValue *args, ExtValue *ret) |
---|
672 | { //should be updated to handle multiple operators for a single format |
---|
673 | char *g, *g2; |
---|
674 | float f1, f2; |
---|
675 | int m; |
---|
676 | logMessage("GenMan", "Report", 0, "The following genetic operators are available:"); |
---|
677 | for (unsigned int i = 0; i < oper_fx_list.size(); i++) |
---|
678 | { |
---|
679 | string l; |
---|
680 | if (oper_fx_list[i]->checkValidity("", "") != GENOPER_NOOPER) l += " checkValidity"; |
---|
681 | if (oper_fx_list[i]->getSimplest()) |
---|
682 | { |
---|
683 | g = strdup(oper_fx_list[i]->getSimplest()); |
---|
684 | g2 = strdup(g); |
---|
685 | if (oper_fx_list[i]->validate(g, "") != GENOPER_NOOPER) l += " validate"; |
---|
686 | if (oper_fx_list[i]->mutate(g, f1, m) != GENOPER_NOOPER) l += " mutate"; |
---|
687 | if (oper_fx_list[i]->crossOver(g, g2, f1, f2) != GENOPER_NOOPER) l += " crossover"; |
---|
688 | l += " getSimplest"; |
---|
689 | free(g); free(g2); |
---|
690 | } |
---|
691 | // if (oper_fx_list[i]->similarity("","")!=GENOPER_NOOPER) l+=" similarity"; |
---|
692 | logPrintf("GenMan", "Report", LOG_INFO, "format f%s (%s):%s", |
---|
693 | oper_fx_list[i]->supported_format.c_str(), oper_fx_list[i]->name.c_str(), l.c_str()); |
---|
694 | } |
---|
695 | } |
---|
696 | |
---|
697 | void GenMan::p_validate(ExtValue *args, ExtValue *ret) |
---|
698 | { |
---|
699 | Geno *g = GenoObj::fromObject(args[0]); |
---|
700 | if (g == NULL) |
---|
701 | ret->setEmpty(); |
---|
702 | else |
---|
703 | *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(validate(*g))); |
---|
704 | } |
---|
705 | |
---|
706 | void GenMan::p_mutate(ExtValue *args, ExtValue *ret) |
---|
707 | { |
---|
708 | Geno *g = GenoObj::fromObject(args[0]); |
---|
709 | if (g == NULL) |
---|
710 | ret->setEmpty(); |
---|
711 | else |
---|
712 | *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(mutate(*g))); |
---|
713 | } |
---|
714 | |
---|
715 | void GenMan::p_crossover(ExtValue *args, ExtValue *ret) |
---|
716 | { |
---|
717 | Geno *g1 = GenoObj::fromObject(args[1]); |
---|
718 | Geno *g2 = GenoObj::fromObject(args[0]); |
---|
719 | if (g1 == NULL || g2 == NULL) |
---|
720 | ret->setEmpty(); |
---|
721 | else |
---|
722 | *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(crossOver(*g1, *g2))); |
---|
723 | } |
---|
724 | |
---|