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

Last change on this file since 1329 was 1317, checked in by Maciej Komosinski, 6 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
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[1316]2// Copyright (C) 1999-2024  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[138]4
5#include "genman.h"
6#include <frams/vm/classes/genoobj.h>
7#include GEN_CONFIG_FILE //configuration of active genetic operators
[375]8#include "common/log.h"
[138]9#include "common/nonstd_math.h"
[841]10#include "common/util-string.h"
[391]11#include <common/loggers/loggers.h>
[138]12
13
[1273]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
[761]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
[139]20#ifdef USE_GENMAN_f0
[779]21#include "f0/f0_oper.h"
[138]22#endif
[977]23#ifdef USE_GENMAN_f0s
24#include "f0s/f0s_oper.h"
25#endif
[139]26#ifdef USE_GENMAN_f0FUZZY
[779]27#include "f0/f0Fuzzy_oper.h"
[138]28#endif
[139]29#ifdef USE_GENMAN_f1
[779]30#include "f1/f1_oper.h"
[138]31#endif
[139]32#ifdef USE_GENMAN_f2
[779]33#include "f2/f2_oper.h"
[138]34#endif
[139]35#ifdef USE_GENMAN_f2
[779]36#include "f3/f3_oper.h"
[138]37#endif
[139]38#ifdef USE_GENMAN_f4
[779]39#include "f4/f4_oper.h"
[138]40#endif
[139]41#ifdef USE_GENMAN_f5
[779]42#include "f5/f5_oper.h"
[138]43#endif
[139]44#ifdef USE_GENMAN_f6
[779]45#include "f6/f6_oper.h"
[138]46#endif
[139]47#ifdef USE_GENMAN_f7
[779]48#include "f7/f7_oper.h"
[138]49#endif
[139]50#ifdef USE_GENMAN_f8
[779]51#include "f8/f8_oper.h"
[138]52#endif
[139]53#ifdef USE_GENMAN_f9
[779]54#include "f9/f9_oper.h"
[138]55#endif
[139]56#ifdef USE_GENMAN_fF
[779]57#include "fF/fF_oper.h"
[139]58#endif
[748]59#ifdef USE_GENMAN_fn
[779]60#include "fn/fn_oper.h"
[748]61#endif
[194]62#ifdef USE_GENMAN_fT
[779]63#include "fT/fTest_oper.h"
[194]64#endif
[780]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
[955]74#ifdef USE_GENMAN_fS
75#include "fS/fS_oper.h"
76#endif
[138]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{
[965]91        { "Genetics", 1, 11, "GenMan", },
[138]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", },
[965]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", },
[240]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", },
[668]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.", },
[138]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"),
[319]128neuronsparam("Genetics: Neurons to add", "neuronsAdd", "neuadd_"),
129par("GenMan", "Manages various genetic operations, using appropriate operators for the argument genotype format.")
[138]130{
131        history = 0;
132        hilite = 1;
133        clearStats();
134
[139]135#ifdef USE_GENMAN_f0
[145]136        oper_fx_list.push_back(new Geno_f0);
[138]137#endif
[977]138#ifdef USE_GENMAN_f0s
139        oper_fx_list.push_back(new Geno_f0s);
140#endif
[139]141#ifdef USE_GENMAN_f0FUZZY
[145]142        oper_fx_list.push_back(new Geno_f0Fuzzy);
[138]143#endif
[139]144#ifdef USE_GENMAN_f1
[145]145        oper_fx_list.push_back(new Geno_f1);
[138]146#endif
[139]147#ifdef USE_GENMAN_f2
[145]148        oper_fx_list.push_back(new Geno_f2);
[138]149#endif
[139]150#ifdef USE_GENMAN_f3
[145]151        oper_fx_list.push_back(new Geno_f3);
[138]152#endif
[139]153#ifdef USE_GENMAN_f4
[145]154        oper_fx_list.push_back(new Geno_f4);
[138]155#endif
[139]156#ifdef USE_GENMAN_f5
[145]157        oper_fx_list.push_back(new Geno_f5);
[138]158#endif
[139]159#ifdef USE_GENMAN_f6
[145]160        oper_fx_list.push_back(new Geno_f6);
[138]161#endif
[139]162#ifdef USE_GENMAN_f7
[145]163        oper_fx_list.push_back(new Geno_f7);
[138]164#endif
[139]165#ifdef USE_GENMAN_f8
[145]166        oper_fx_list.push_back(new Geno_f8);
[138]167#endif
[139]168#ifdef USE_GENMAN_f9
[145]169        oper_fx_list.push_back(new GenoOper_f9);
[138]170#endif
[139]171#ifdef USE_GENMAN_fF
[145]172        oper_fx_list.push_back(new GenoOper_fF);
[139]173#endif
[748]174#ifdef USE_GENMAN_fn
175        oper_fx_list.push_back(new GenoOper_fn);
176#endif
[194]177#ifdef USE_GENMAN_fT
178        oper_fx_list.push_back(new GenoOper_fTest);
179#endif
[780]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
[955]189#ifdef USE_GENMAN_fS
190        oper_fx_list.push_back(new GenoOper_fS);
191#endif
[138]192
[145]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
[138]194        int selopercount = 0;
[145]195        for (unsigned int i = 0; i < oper_fx_list.size(); i++)
[138]196        {
[955]197                if (findOperFormatIndex(oper_fx_list[i]->supported_format) != -1) continue;
[200]198                string type = string("~") + oper_fx_list[i]->name;
[138]199                int dup = 0;
[145]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)
[138]202                        {
[955]203                                type += "~";
204                                type += oper_fx_list[j]->name;
205                                dup++;
[138]206                        }
[200]207                type = ssprintf("d 0 %d ", dup) + type;
[955]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());
[138]210                seloper[selopercount] = 0;
[955]211                operformats += &oper_fx_list[i]->supported_format;
[138]212                //printf("%x %s %s %s\n",&seloper[selopercount],(const char*)id,(const char*)type,(const char*)name);
[955]213                seloperpar.addProperty(&seloper[selopercount++], id.c_str(), type.c_str(), name.c_str(), "", PARAM_READONLY * (dup == 0));
[138]214        }
215
216        par += &localpar;
217        par += &seloperpar;
218        par += &neuronsparam;
[145]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;
[715]221
222        setDefaults(); //use Param to initialize all values of fields in the paramtab of this object and genetic operators on oper_fx_list
[138]223}
224
225GenMan::~GenMan()
226{
[145]227        for (unsigned int i = 0; i < oper_fx_list.size(); i++) delete oper_fx_list[i];
[138]228        delete[] seloper;
229}
230
[955]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
[138]239void GenMan::setDefaults()
240{
[145]241        for (unsigned int i = 0; i < oper_fx_list.size(); i++)
[138]242        {
[145]243                oper_fx_list[i]->par.setDefault();
244                oper_fx_list[i]->setDefaults();
[138]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{
[761]253        SString ggs = g.getGenes();
[348]254        const char *gg = ggs.c_str();
[145]255        GenoOperators *gf = getOper_f(g.getFormat());
[138]256        int check1;
257        if (!gf) { canvalidate = false; return GENOPER_NOOPER; }
[761]258        else check1 = gf->checkValidity(gg, g.getName().c_str());
[138]259        if (!canvalidate) return check1; //just checking
260        if (check1 == GENOPER_OK) { canvalidate = false; return check1; }
261        char *g2 = strdup(gg);
[761]262        if (gf->validate(g2, g.getName().c_str()) == GENOPER_NOOPER) { free(g2); canvalidate = false; return check1; }
[138]263        if (check1 == GENOPER_NOOPER) //disaster: cannot check because there is no check operator
264        {
[534]265                g.setGenesAssumingSameFormat(g2); free(g2); canvalidate = false; return GENOPER_NOOPER;
[138]266        }
[513]267        int check2 = gf->checkValidity(g2, "validated");
[534]268        if (check2 == GENOPER_OK) g.setGenesAssumingSameFormat(g2);
[138]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
[532]286Geno GenMan::validate(const Geno& geny)
[138]287{
[955]288        SString format = geny.getFormat();
[168]289        GenoOperators *gf = getOper_f(format);
290        if (gf == NULL)
[988]291                return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: Validate(): don't know how to handle genetic format %s", format.c_str()));
[534]292        char *g2 = strdup(geny.getGenes().c_str()); //copy for validation
[513]293        int res = gf->validate(g2, geny.getName().c_str());
[168]294        SString sg2 = g2;
[138]295        free(g2);
[168]296        if (res == GENOPER_OK)
297                return Geno(sg2, format, geny.getName(), geny.getComment());
[138]298        else
[988]299                return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: validate() for format %s returned invalid value", format.c_str()));
[138]300}
301
[532]302Geno GenMan::mutate(const Geno& g)
[138]303{
304        float chg; //how many changes
305        int method; //mutation method
[955]306        SString format = g.getFormat();
[168]307        GenoOperators *gf = getOper_f(format);
308        if (gf == NULL)
[988]309                return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: Mutate(): don't know how to handle genetic format %s", format.c_str()));
[168]310        Geno gv = g;
311        bool canvalidate = true;
312        if (testValidity(gv, canvalidate) > 0 && canvalidate == false)
[988]313                return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: Mutate(): cannot validate invalid source genotype");
[168]314        bool ok = false;
315        int pcount = count;
[138]316        while (!ok)
317        {
[534]318                char *gn = strdup(gv.getGenes().c_str()); //copy for mutation
[168]319                chg = 0;
320                if (gf->mutate(gn, chg, method) == GENOPER_OK)
[138]321                {
[375]322                        LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreFirstMessage); //mute testValidity()
[168]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                                {
[955]330                                        validated_m++; ok = true;
[168]331                                }
332                        if (ok) gv = G;
333                }
334                else failed_m++;
[138]335                free(gn);
336                count++;
[761]337                if (!ok && (count - pcount > GENMAN_REPEAT_FAILED))
[138]338                {
[955]339                        logPrintf("GenMan", "Mutate", LOG_WARN, "Tried " GENMAN_REPEAT_FAILED_STR "x and failed: %s", g.getGenes().c_str());
[761]340                        return Geno("", -1, "", "GENOPER_OPFAIL: Mutate() tried " GENMAN_REPEAT_FAILED_STR "x and failed");
[138]341                }
342        }
[168]343        mutchg += chg;
[534]344        if (history) saveLink(g.getGenes().c_str(), "", gv.getGenes().c_str(), chg);
[138]345        SString mutinfo;
[348]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());
[138]349        gv.setComment(mutinfo);
350        return gv;
351}
352
[532]353Geno GenMan::crossOver(const Geno& g1, const Geno& g2)
[138]354{
[955]355        SString format = g1.getFormat();
[988]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()));
[145]357        GenoOperators *gf = getOper_f(format);
[138]358        if (gf == NULL)
[988]359                return Geno("", Geno::FORMAT_INVALID, "", SString::sprintf("GENOPER_NOOPER: CrossOver(): no operators found for genetic format %s", format.c_str()));
[138]360        Geno g1v = g1, g2v = g2;
361
362        {
[375]363                LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreFirstMessage); //mute testValidity()
[138]364                bool canvalidate = true;
365                if (testValidity(g1v, canvalidate) > 0 && canvalidate == false)
[988]366                        return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #1");
[138]367                canvalidate = true;
368                if (testValidity(g2v, canvalidate) > 0 && canvalidate == false)
[988]369                        return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: CrossOver(): cannot validate invalid source genotype #2");
[138]370        }
371
[1316]372        float chg; //fraction of parent1 genes in the child
[138]373        bool ok = false;
374        int pcount = count;
375
376        while (!ok)
377        {
378                float chg1, chg2;
[534]379                char *g1n = strdup(g1.getGenes().c_str()); //copy for crossover
380                char *g2n = strdup(g2.getGenes().c_str()); //copy for crossover
[138]381                chg1 = chg2 = 0;
[1317]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);
[138]384                if (gf->crossOver(g1n, g2n, chg1, chg2) == GENOPER_OK)
385                {
[1317]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).
[138]387                        char *gn;
[896]388                        if (g1n[0] && g2n[0]) if (rndUint(2) == 0) g1n[0] = 0; else g2n[0] = 0; //both provided? we want only one
[138]389                        if (g1n[0]) { gn = g1n; chg = chg1; }
[1316]390                        else { gn = g2n; chg = 1 - chg2; }
[375]391                        LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreFirstMessage); //mute testValidity()
[138]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                                {
[955]399                                        validated_xo++; ok = true;
[138]400                                }
401                        if (ok) g1v = G;
402                }
403                else failed_xo++;
404                free(g1n);
405                free(g2n);
406                count++;
[761]407                if (!ok && (count - pcount > GENMAN_REPEAT_FAILED))
[138]408                {
[955]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());
[988]410                        return Geno("", Geno::FORMAT_INVALID, "", "GENOPER_OPFAIL: CrossOver() tried " GENMAN_REPEAT_FAILED_STR "x and failed");
[138]411                }
412        }
413        // result in g1v
414        xochg += chg;
[534]415        if (history) saveLink(g1.getGenes().c_str(), g2.getGenes().c_str(), g1v.getGenes().c_str(), chg);
[138]416        SString xoinfo = SString::sprintf("Crossing over of '%s' (%.2f%%) and '%s' (%.2f%%)",
[348]417                g1.getName().c_str(), 100 * chg, g2.getName().c_str(), 100 * (1 - chg));
[138]418        g1v.setComment(xoinfo);
419        return g1v;
420}
421
[532]422float GenMan::similarity(const Geno& g1, const Geno& g2)
[138]423{
[955]424        SString format = g1.getFormat();
[138]425        if (format != g2.getFormat()) return GENOPER_NOOPER;
[145]426        GenoOperators *gf = getOper_f(format);
[534]427        if (!gf) return GENOPER_NOOPER; else return gf->similarity(g1.getGenes().c_str(), g2.getGenes().c_str());
[138]428}
429
[532]430uint32_t GenMan::getStyle(const char *g, const Geno *G, int pos)
[138]431{
[955]432        SString format = G->getFormat();
[988]433        if (format == Geno::FORMAT_INVALID)
[532]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);
[138]437        if (!gf) return GENSTYLE_CS(0, 0); //black & valid
[534]438        else return gf->style(G->getGenes().c_str(), pos);
[138]439}
440
[532]441uint32_t GenMan::getStyle(const char *g, int pos)
[138]442{
443        Geno G(g);
[532]444        return getStyle(g, &G, pos);
445}
446
447void GenMan::getFullStyle(const char *g, const Geno *G, uint32_t *styletab)
448{
[955]449        SString format = G->getFormat();
[988]450        if (format == Geno::FORMAT_INVALID)
[532]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);
[761]457        SString geny = G->getGenes();
[138]458        for (unsigned int pos = 0; pos < strlen(g); pos++)
459        {
[532]460                int posmapped = G->mapStringToGen(pos);
[138]461                if (posmapped == -1) styletab[pos] = GENSTYLE_COMMENT;
462                else if (!gf) styletab[pos] = GENSTYLE_CS(0, 0); //black & valid
[348]463                else styletab[pos] = gf->style(geny.c_str(), posmapped);
[955]464                //logPrintf("GenMan", "getFullStyle", LOG_INFO, "%d  char='%c' (%d)  format=0x%08x", pos, g[pos], g[pos], styletab[pos]);
[138]465        }
466}
467
[532]468void GenMan::getFullStyle(const char *g, uint32_t *styletab)
469{
470        Geno G(g);
471        getFullStyle(g, &G, styletab);
472}
473
[467]474string GenMan::HTMLize(const char *g) { return HTMLize(g, false); }
[138]475
[467]476string GenMan::HTMLizeShort(const char *g) { return HTMLize(g, true); }
[138]477
[467]478string GenMan::HTMLize(const char *g, bool shorten)
[138]479{
480        char buf[50];
[1238]481        int len = int(strlen(g));
[138]482        int chars = 0, lines = 0;
483        bool shortened = false;
[247]484        uint32_t *styletab = new uint32_t[len];
[532]485        getFullStyle(g, styletab);
[1238]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\">";
[247]490        uint32_t prevstyle, prevcolor, style = 0, color = 0;
[761]491        for (int i = 0; i < len; i++)
[138]492        {
[761]493                if (shorten && ((lines == 0 && chars > 160) || (lines > 5 || chars > 300))) { shortened = true; break; }
[138]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>";
[955]502                if ((style & GENSTYLE_INVALID) != (prevstyle & GENSTYLE_INVALID))
[138]503                {
[955]504                        html += "<"; if (!(style & GENSTYLE_INVALID)) html += "/"; html += "u>";
[138]505                }
[955]506                if ((style & GENSTYLE_BOLD) != (prevstyle & GENSTYLE_BOLD))
[138]507                {
[955]508                        html += "<"; if (!(style & GENSTYLE_BOLD)) html += "/"; html += "b>";
[138]509                }
[955]510                if ((style & GENSTYLE_ITALIC) != (prevstyle & GENSTYLE_ITALIC))
[138]511                {
[955]512                        html += "<"; if (!(style & GENSTYLE_ITALIC)) html += "/"; html += "i>";
[138]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...]";
[1238]524        html += "</span>\n";
[138]525        return html;
526}
527
528void GenMan::p_htmlize(ExtValue *args, ExtValue *ret)
529{
[467]530        ret->setString(HTMLize(args->getString().c_str()).c_str());
[138]531}
532
533void GenMan::p_htmlizeshort(ExtValue *args, ExtValue *ret)
534{
[467]535        ret->setString(HTMLizeShort(args->getString().c_str()).c_str());
[138]536}
537
[965]538string GenMan::LaTeXize(const char *g)
539{
540        char buf[50];
[1238]541        int len = int(strlen(g));
[965]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
[1273]580                                if (g[i] == '$') latex += "\\$"; else if (g[i] == '%') latex += "\\%"; else
581                                        if (g[i] == '#') latex += "\\#"; else latex += g[i];
[965]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)
[966]587        if (style & GENSTYLE_INVALID) latex += "}";
[965]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
[955]599Geno GenMan::getSimplest(const SString& format)
[138]600{
[145]601        GenoOperators *gf = getOper_f(format);
[138]602        if (!gf) return Geno();
[955]603        string info = "The simplest genotype of format f"; info += format.c_str();
[138]604        info += " for operators '"; info += gf->name; info += "'.";
[200]605        return Geno(gf->getSimplest(), format, "Root", info.c_str());
[138]606}
607
608void GenMan::p_getsimplest(ExtValue *args, ExtValue *ret)
609{
[955]610        SString format = GenoObj::formatFromExtValue(args[0]);
[145]611        if (!getOper_f(format))
[138]612                ret->setEmpty();
613        else
[532]614                *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(getSimplest(format)));
[138]615}
616
[955]617const char *GenMan::getOpName(const SString& format)
[138]618{
[145]619        GenoOperators *gf = getOper_f(format);
[200]620        if (!gf) return "n/a"; else return gf->name.c_str();
[138]621}
622
[955]623GenoOperators* GenMan::getOper_f(const SString& format)
[138]624{
[955]625        int ind = findOperFormatIndex(format);
[138]626        if (ind == -1) return NULL;
[168]627        int which_oper_of_format = seloper[ind];
[145]628        for (unsigned int i = 0; i < oper_fx_list.size(); i++)
629                if (oper_fx_list[i]->supported_format == format)
[168]630                        if (which_oper_of_format == 0) return oper_fx_list[i]; else which_oper_of_format--;
[138]631        return NULL; //should never happen
632}
633
[275]634void GenMan::saveLink(const string parent1, const string parent2, const string child, const float chg)
[138]635{
636        GenoLink l;
637        l.count = count;
[275]638        l.parent1 = parent1;
639        l.parent2 = parent2;
640        l.child = child;
[138]641        l.chg = chg;
642        l.fit = 0; //temporarily. Will be set when the genotype dies
[375]643        //logPrintf("GenMan","saveLink",0,"#%d: [%d] '%s' + '%s' -> '%s'",GenoLinkList.size(),count,parent1.c_str(),parent2.c_str(),child.c_str());
[138]644        GenoLinkList.push_back(l);
645}
646
[247]647void GenMan::onDelGen(void *obj, intptr_t n)
[138]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;
[145]674        float f1, f2;
675        int m;
[375]676        logMessage("GenMan", "Report", 0, "The following genetic operators are available:");
[145]677        for (unsigned int i = 0; i < oper_fx_list.size(); i++)
[138]678        {
[200]679                string l;
[761]680                if (oper_fx_list[i]->checkValidity("", "") != GENOPER_NOOPER) l += " checkValidity";
[145]681                if (oper_fx_list[i]->getSimplest())
[138]682                {
[145]683                        g = strdup(oper_fx_list[i]->getSimplest());
[138]684                        g2 = strdup(g);
[761]685                        if (oper_fx_list[i]->validate(g, "") != GENOPER_NOOPER) l += " validate";
[145]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";
[138]688                        l += " getSimplest";
689                        free(g); free(g2);
690                }
[145]691                //      if (oper_fx_list[i]->similarity("","")!=GENOPER_NOOPER) l+=" similarity";
[955]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());
[138]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
[532]703                *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(validate(*g)));
[138]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
[532]712                *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(mutate(*g)));
[138]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
[532]722                *ret = GenoObj::makeDynamicObjectAndDecRef(new Geno(crossOver(*g1, *g2)));
[138]723}
[532]724
Note: See TracBrowser for help on using the repository browser.