source: cpp/frams/genetics/genman.cpp @ 1317

Last change on this file since 1317 was 1317, checked in by Maciej Komosinski, 4 months ago

Randomly swap parents in crossover to avoid any imbalances or biases in crossover implementations (in case parent order in the implementation of operators would matter - while we assume it should not)

  • Property svn:eol-style set to native
File size: 26.9 KB
Line 
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
78using 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
89static 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
106static 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
126GenMan::GenMan() : localpar(GMparam_tab, this), localstats(GMstats_tab, this),
127seloperpar("GenOperators", "Genetics: Active operators"),
128neuronsparam("Genetics: Neurons to add", "neuronsAdd", "neuadd_"),
129par("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
225GenMan::~GenMan()
226{
227        for (unsigned int i = 0; i < oper_fx_list.size(); i++) delete oper_fx_list[i];
228        delete[] seloper;
229}
230
231int 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
239void 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
251int 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
275int 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
286Geno 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
302Geno 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
353Geno 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
422float 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
430uint32_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
441uint32_t GenMan::getStyle(const char *g, int pos)
442{
443        Geno G(g);
444        return getStyle(g, &G, pos);
445}
446
447void 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
468void GenMan::getFullStyle(const char *g, uint32_t *styletab)
469{
470        Geno G(g);
471        getFullStyle(g, &G, styletab);
472}
473
474string GenMan::HTMLize(const char *g) { return HTMLize(g, false); }
475
476string GenMan::HTMLizeShort(const char *g) { return HTMLize(g, true); }
477
478string 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 += "&lt;"; else if (g[i] == '>') html += "&gt;"; 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
528void GenMan::p_htmlize(ExtValue *args, ExtValue *ret)
529{
530        ret->setString(HTMLize(args->getString().c_str()).c_str());
531}
532
533void GenMan::p_htmlizeshort(ExtValue *args, ExtValue *ret)
534{
535        ret->setString(HTMLizeShort(args->getString().c_str()).c_str());
536}
537
538string 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
594void GenMan::p_latexize(ExtValue *args, ExtValue *ret)
595{
596        ret->setString(LaTeXize(args->getString().c_str()).c_str());
597}
598
599Geno 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
608void 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
617const 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
623GenoOperators* 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
634void 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
647void 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
661void 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
669void GenMan::p_clearStats(ExtValue *args, ExtValue *ret) { clearStats(); }
670
671void 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
697void 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
706void 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
715void 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
Note: See TracBrowser for help on using the repository browser.