source: cpp/frams/genetics/fL/fL_general.cpp @ 1244

Last change on this file since 1244 was 973, checked in by Maciej Komosinski, 4 years ago

Increased SString and std::string compatibility: introduced length(), size(), and capacity(), and removed legacy methods that have std::string equivalents

File size: 34.6 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include <algorithm>
6#include <stack>
7#include "fL_general.h"
8#include <frams/util/multirange.h>
9#include <iterator>
10
11const char *fL_part_names[FL_PART_PROPS_COUNT] = { "dn", "fr", "ing", "as" };
12const char *fL_part_fullnames[FL_PART_PROPS_COUNT] = { "details", "friction", "ingestion", "assimilation" };
13
14const char *fL_joint_names[FL_JOINT_PROPS_COUNT] = { "stif", "rotstif", "stam" };
15const char *fL_joint_fullnames[FL_JOINT_PROPS_COUNT] = { "stiffness", "rotation stiffness", "stamina" };
16
17#define FIELDSTRUCT fL_Word
18ParamEntry fL_word_paramtab[] =
19{
20        { "Word", 1, 2, "w" },
21        { "name", 0, PARAM_CANOMITNAME, "word name", "s", FIELD(name), },
22        { "npar", 0, PARAM_CANOMITNAME, "number of parameters", "d 0 " FL_MAXPARAMS " 0", FIELD(npar), },
23        { 0, 0, 0, }
24};
25#undef FIELDSTRUCT
26
27#define FIELDSTRUCT fL_Rule
28ParamEntry fL_rule_paramtab[] =
29{
30        { "Rule", 1, 3, "r" },
31        { "pred", 0, 0, "predecessor", "s", FIELD(predecessor), },
32        { "cond", 0, 0, "parameter condition", "s", FIELD(condition), },
33        { "succ", 0, 0, "successor", "s", FIELD(successor), },
34        { 0, 0, 0, }
35};
36#undef FIELDSTRUCT
37
38#define FIELDSTRUCT fL_Builder
39ParamEntry fL_builder_paramtab[] =
40{
41                {"LSystemInfo", 1, 4, "i"},
42                {"axiom", 0, 0, "starting sequence of L-System", "s", FIELD(axiom),},
43                {"time", 0, PARAM_CANOMITNAME, "development time", "f 0.0 " FL_MAXITER " 1.0", FIELD(time),},
44                {"numckp", 0, PARAM_CANOMITNAME, "number of checkpoints", "d 1 50 1", FIELD(numckp),},
45                {"maxwords", 0, PARAM_CANOMITNAME, "Maximum number of words within genotype sequence", "d -1 9999 -1", FIELD(maxwords),},
46                {0,0,0,}
47};
48#undef FIELDSTRUCT
49
50fL_Builder::~fL_Builder()
51{
52        // first remove words from builder
53        for (fL_Word *word : genotype)
54        {
55                delete word;
56        }
57        genotype.clear();
58        // remove rules from builder
59        for (fL_Rule *rule : rules)
60        {
61                delete rule;
62        }
63        rules.clear();
64
65        // remove words definitions with their ParamTabs
66        std::unordered_map<std::string, fL_Word *>::iterator it;
67        for (it = words.begin(); it != words.end(); it++)
68        {
69                ParamObject::freeParamTab(it->second->tab);
70                delete it->second;
71        }
72        words.clear();
73}
74
75bool fL_Builder::getNextObject(int &pos, const SString &src, SString &token)
76{
77        // if position exceeds length then return false
78        if (pos >= src.length()) return false;
79        int opencount = -1;
80        int i = pos;
81        for (; i < src.length(); i++)
82        {
83                // token cannot contain branching parenthesis
84                if (src[i] == '[' || src[i] == ']')
85                {
86                        // if token started - return parenthesis mismatch
87                        if (opencount != -1)
88                        {
89                                pos = -1;
90                                return false;
91                        }
92                        // otherwise [ and ] are interpreted as tokens and they do not have parenthesis
93                        token = src.substr(pos, i + 1 - pos);
94                        pos = i + 1;
95                        return true;
96                }
97                // every word, except [ and ], has () parenthesis
98                // every open parenthesis increment opencount counter;
99                if (src[i] == '(')
100                {
101                        if (opencount == -1) opencount = 1;
102                        else opencount++;
103                }
104                // every close parenthesis decrement opencount counter
105                else if (src[i] == ')')
106                {
107                        // if there were no open parenthesis, return parenthesis mismatch
108                        if (opencount == -1)
109                        {
110                                pos = -1;
111                                return false;
112                        }
113                        else opencount--;
114                }
115                // if counter reaches 0, the token extraction is finished
116                if (opencount == 0)
117                {
118                        break;
119                }
120        }
121        if (opencount == 0)
122        {
123                token = src.substr(pos, i + 1 - pos);
124                pos = i + 1;
125                return true;
126        }
127        // if there was no closing parenthesis, then return parenthesis mismatch
128        pos = -1;
129        return false;
130}
131
132std::string fL_Builder::trimSpaces(const std::string& data)
133{
134        size_t first = data.find_first_not_of(' ');
135        if (std::string::npos == first)
136        {
137                return data;
138        }
139        size_t last = data.find_last_not_of(' ');
140        return data.substr(first, (last - first + 1));
141}
142
143int fL_Builder::createWord(const SString &token, fL_Word *&word, int numparams, int begin, int end)
144{
145        SString wordn;
146        int tokpos = 0;
147        // if word name cannot be extracted, then return error
148        if (!token.getNextToken(tokpos, wordn, '('))
149        {
150                return 1;
151        }
152        std::string wordname = fL_Builder::trimSpaces(wordn.c_str());
153        // if word cannot be found in available words, then return error
154        if (words.find(wordname) == words.end())
155        {
156                SString message = "Word '";
157                message += wordname.c_str();
158                message += "' in sequence does not exist";
159                logMessage("fL_Builder", "createWord", LOG_ERROR, message.c_str());
160                return 1;
161        }
162
163        if (word) delete word;
164        // create new word and assign parameters
165        word = new fL_Word(false, begin, end);
166
167        *word = *words[wordname];
168
169        SString temp;
170        temp = token.substr(tokpos);
171        temp = temp.substr(0, temp.length() - 1);
172
173        // if word has parameters
174        if (word->npar > 0)
175        {
176                // create ParamObject that will hold parameter data
177                word->data = ParamObject::makeObject(word->tab);
178                Param par(word->tab);
179                par.select(word->data);
180                par.setDefault();
181                ParamInterface::LoadOptions opts;
182
183                // load parameters from string
184                par.load(ParamInterface::FormatSingleLine, temp, &opts);
185                for (int i = 0; i < par.getPropCount(); i++)
186                {
187                        SString t(par.id(i));
188                        if (word->builtin && (t == SString("d") || t == SString(FL_PE_CONN_ATTR)))
189                        {
190                                word->parevals.push_back(NULL);
191                        }
192                        else
193                        {
194                                // create MathEvaluation object to check if string contained by
195                                // parameter is valid
196                                double tmp;
197                                MathEvaluation *eval = NULL;
198                                SString seq = par.getString(i);
199                                // if string is empty, then evaluate this with 0
200                                // if sequence could not be evaluated, then return error
201                                if (seq.length() > 0)
202                                {
203                                        eval = new MathEvaluation(numparams);
204                                        if (eval->evaluate(seq.c_str(), tmp) != 0)
205                                        {
206                                                SString message = "Word in sequence has invalid parameter:  ";
207                                                message += temp;
208                                                logMessage("fL_Builder", "createWord", LOG_ERROR, message.c_str());
209                                                delete eval;
210                                                delete word;
211                                                word = NULL;
212                                                return 1;
213                                        }
214                                }
215                                word->parevals.push_back(eval);
216                        }
217                }
218        }
219        else if (word->npar == 0 && temp.length() > 0)
220        {
221                SString message = "Too many parameters for word:  ";
222                message += token;
223                logMessage("fL_Builder", "createWord", LOG_ERROR, message.c_str());
224                delete word;
225                word = NULL;
226                return 1;
227        }
228        return 0;
229}
230
231int fL_Builder::tokenize(const SString &sequence, std::list<fL_Word *> &result, int numparams, int begin, int end)
232{
233        int pos = 0;
234        SString token;
235        int branchcount = 0;
236        if (result.size() > 0)
237        {
238                for (fL_Word *word : result)
239                {
240                        delete word;
241                }
242                result.clear();
243        }
244        // iterate through available tokens
245        while (getNextObject(pos, sequence, token))
246        {
247                // if token is of open branch type, then add start of branch
248                if (token.indexOf("[", 0) != -1)
249                {
250                        fL_Branch *word = new fL_Branch(fL_Branch::BranchType::OPEN, begin, end);
251                        result.push_back(word);
252                        branchcount++;
253                        continue;
254                }
255                // if token is of closed branch type, then add end of branch
256                if (token.indexOf("]", 0) != -1)
257                {
258                        if (branchcount == 0)
259                        {
260                                SString message = "Branch parenthesis mismatch at:  ";
261                                message += sequence;
262                                logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
263                                return 1;
264                        }
265                        fL_Branch *word = new fL_Branch(fL_Branch::BranchType::CLOSE, begin, end);
266                        result.push_back(word);
267                        branchcount--;
268                        continue;
269                }
270                fL_Word *word = NULL;
271                if (createWord(token, word, numparams, begin, end) != 0)
272                {
273                        SString message = "Error during parsing words sequence:  ";
274                        message += sequence;
275                        logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
276                        return 1;
277                }
278                if (word->name == "C")
279                {
280                        Param par(word->tab, word->data);
281                        SString attr = par.getStringById(FL_PE_CONN_ATTR);
282                        if (attr.indexOf("$t", 0) != -1)
283                        {
284                                logMessage("fL_Builder", "tokenize", LOG_ERROR, "Attractor definition cannot contain time variable");
285                                delete word;
286                                return 1;
287
288                        }
289                        if (attr != "")
290                        {
291                                fL_Word *attrword = NULL;
292                                if (createWord(attr, attrword, numparams, begin, end) != 0)
293                                {
294                                        SString message = "Error during parsing attractor word:  ";
295                                        message += attr;
296                                        logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
297                                        delete word;
298                                        if (attrword) delete attrword;
299                                        return 1;
300                                }
301                                if (attrword->builtin)
302                                {
303                                        logMessage("fL_Builder", "tokenize", LOG_ERROR, "Attractor words cannot be built-in");
304                                        delete word;
305                                        delete attrword;
306                                        return 1;
307                                }
308                                delete attrword;
309                        }
310                }
311                result.push_back(word);
312        }
313
314        // check if there were no parenthesis errors in genotype
315        if (pos == -1)
316        {
317                SString message = "Parenthesis mismatch at:  ";
318                message += sequence;
319                logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
320                return 1;
321        }
322        if (branchcount != 0)
323        {
324                SString message = "Branching mismatch at:  ";
325                message += sequence;
326                logMessage("fL_Builder", "tokenize", LOG_ERROR, message.c_str());
327                return 1;
328        }
329        return 0;
330}
331
332void fL_Word::operator=(const fL_Word& src)
333{
334        if (&src != this)
335        {
336                name = src.name;
337                npar = src.npar;
338
339                //mut = src.mut;
340                tab = src.tab;
341
342                parevals = src.parevals;
343
344                builtin = src.builtin;
345
346                data = NULL; // properties cannot be copied
347        }
348}
349
350int fL_Word::processDefinition(fL_Builder *builder)
351{
352        // if word already exist, then return error
353        if (this->name.length() == 0)
354        {
355                logMessage("fL_Word", "processDefinition", LOG_ERROR, "Axiom name is empty");
356                return 1;
357        }
358        if (builder->words.find(this->name.c_str()) != builder->words.end())
359        {
360                std::string message = "Word redefinition:  ";
361                message += this->name.c_str();
362                logMessage("fL_Word", "processDefinition", LOG_ERROR, message.c_str());
363                return 1;
364        }
365
366        // create ParamTab for word
367        for (int i = 0; i < npar; i++)
368        {
369                std::string n = "n";
370                n += std::to_string(i);
371                mut.addProperty(NULL, n.c_str(), LSYSTEM_PARAM_TYPE, n.c_str(), "", PARAM_CANOMITNAME, 0, -1);
372        }
373
374        tab = ParamObject::makeParamTab((ParamInterface *)&mut, 0, 0, mut.firstMutableIndex());
375
376        builder->words[this->name.c_str()] = this;
377        builder->wordnames.push_back(this->name.c_str());
378        return 0;
379}
380
381int fL_Rule::processDefinition(fL_Builder *builder)
382{
383        // if there is no word among words that matches predecessor, then return error
384        if (builder->words.find(predecessor.c_str()) == builder->words.end())
385        {
386                logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Word in Rule condition does not exist");
387                return 1;
388        }
389
390        objpred = new fL_Word();
391        *objpred = *builder->words[predecessor.c_str()];
392
393        if (objpred->builtin)
394        {
395                logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Builtin words cannot be predecessors");
396                return 1;
397        }
398
399        // parse condition
400        if (condition != "")
401        {
402                if (objpred->builtin && (objpred->name == "N" || objpred->name == "C"))
403                {
404                        logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Rules with neuron/connection word predecessors cannot contain conditions");
405                        return 1;
406                }
407                std::string cond = condition.c_str();
408                condeval = new MathEvaluation(objpred->npar);
409                double tmp;
410                if (condeval->evaluate(condition.c_str(), tmp) != 0)
411                {
412                        SString message = "Parametric condition of rule invalid:  ";
413                        message += condition;
414                        logMessage("fL_Rule", "processDefinition", LOG_ERROR, message.c_str());
415                        return 1;
416                }
417        }
418
419        // parse successor
420        if (successor == "")
421        {
422                logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Successor cannot be empty");
423                return 1;
424        }
425
426        if (builder->tokenize(successor, objsucc, objpred->npar, begin, end) != 0)
427        {
428                logMessage("fL_Rule", "processDefinition", LOG_ERROR, "Unable to process successor sequence");
429                return 1;
430        }
431
432        builder->rules.push_back(this);
433        return 0;
434}
435
436int fL_Builder::processDefinition(fL_Builder *builder)
437{
438        // tokenize axiom
439        if (tokenize(axiom, genotype, 0, begin, end) != 0)
440        {
441                logMessage("fL_Builder", "processDefinition", LOG_ERROR, "Unable to process axiom sequence");
442                return 1;
443        }
444        else if (genotype.size() == 0)
445        {
446                logMessage("fL_Builder", "processDefinition", LOG_ERROR, "Axiom sequence is empty");
447                return 1;
448        }
449        return 0;
450}
451
452int fL_Builder::processLine(fLElementType type, const SString &line, fL_Element *&obj, int linenumber, int begin, int end)
453{
454        ParamEntry *tab;
455        // choose proper ParamTab and construct proper object
456        switch (type)
457        {
458        case fLElementType::TERM:
459        {
460                tab = fL_word_paramtab;
461                obj = new fL_Word();
462                break;
463        }
464        case fLElementType::INFO:
465        {
466                tab = fL_builder_paramtab;
467                obj = this;
468                break;
469        }
470        case fLElementType::RULE:
471        {
472                tab = fL_rule_paramtab;
473                obj = new fL_Rule(begin, end);
474                break;
475        }
476        default:
477                tab = NULL;
478                obj = NULL;
479                break;
480        }
481        Param par(tab);
482        par.select(obj);
483        par.setDefault();
484        ParamInterface::LoadOptions opts;
485
486        par.load(ParamInterface::FormatSingleLine, line, &opts);
487
488        if (opts.parse_failed)
489        {
490                std::string message = "Error in parsing parameters at line:  " + std::to_string(linenumber);
491                logMessage("fL_Builder", "processLine", LOG_ERROR, message.c_str());
492                if (obj != this) delete obj;
493                return begin + 1;
494        }
495
496        return 0;
497}
498
499void fL_Builder::addModelWords()
500{
501        // stick S
502        fL_Word *stick = new fL_Word(true);
503        stick->name = "S";
504        stick->npar = 8;
505        for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
506        {
507                stick->mut.addProperty(NULL, fL_part_names[i], "s", fL_part_fullnames[i], fL_part_fullnames[i], PARAM_CANOMITNAME, 0, -1);
508        }
509
510        for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
511        {
512                stick->mut.addProperty(NULL, fL_joint_names[i], "s", fL_joint_fullnames[i], fL_joint_fullnames[i], PARAM_CANOMITNAME, 0, -1);
513        }
514
515        stick->mut.addProperty(NULL, "l", "s", "length", "length", PARAM_CANOMITNAME, 0, -1);
516        stick->tab = ParamObject::makeParamTab((ParamInterface *)&stick->mut, 0, 0, stick->mut.firstMutableIndex());
517        words["S"] = stick;
518        wordnames.push_back("S");
519
520        // neuron N
521        fL_Word *neuron = new fL_Word(true);
522        neuron->name = "N";
523        neuron->npar = 1;
524        neuron->mut.addProperty(NULL, "d", "s", "details", "details", 0, 0, -1);
525        neuron->tab = ParamObject::makeParamTab((ParamInterface *)&neuron->mut, 0, 0, neuron->mut.firstMutableIndex());
526        words["N"] = neuron;
527        wordnames.push_back("N");
528
529        // connection C
530        fL_Word *connection = new fL_Word(true);
531        connection->name = "C";
532        connection->npar = 2;
533        connection->mut.addProperty(NULL, FL_PE_CONN_WEIGHT, "s", "weight", "weight", PARAM_CANOMITNAME, 0, -1);
534        connection->mut.addProperty(NULL, FL_PE_CONN_ATTR, "s", "attractor", "connection attractor", PARAM_CANOMITNAME, 0, -1);
535        connection->tab = ParamObject::makeParamTab((ParamInterface *)&connection->mut, 0, 0, connection->mut.firstMutableIndex());
536        words["C"] = connection;
537        wordnames.push_back("C");
538
539        // rotation objects
540        fL_Word *rotx = new fL_Word(true);
541        rotx->name = "rotX";
542        rotx->npar = 1;
543        rotx->processDefinition(this);
544
545        fL_Word *roty = new fL_Word(true);
546        roty->name = "rotY";
547        roty->npar = 1;
548        roty->processDefinition(this);
549
550        fL_Word *rotz = new fL_Word(true);
551        rotz->name = "rotZ";
552        rotz->npar = 1;
553        rotz->processDefinition(this);
554
555        //fL_Branch *branch = new fL_Branch(fL_Branch::BranchType::OPEN, 0, 0);
556        //branch->processDefinition(this);
557
558        builtincount = words.size();
559}
560
561int fL_Builder::parseGenotype(const SString &genotype)
562{
563        int pos = 0;
564        int lastpos = 0;
565        SString line;
566        int linenumber = 0;
567
568        fLElementType type = fLElementType::TERM;
569
570        // add default words first to prevent redefinitions
571        addModelWords();
572
573        while (genotype.getNextToken(pos, line, '\n'))
574        {
575                if (line.length() > 0)
576                {
577                        // words can be defined in the beginning of genotype
578                        if (line.startsWith("w:") && type != fLElementType::TERM)
579                        {
580                                logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "All words should be defined in the beginning of genotype");
581                                return lastpos + 1;
582                        }
583                        else if (line.startsWith("i:"))
584                        {
585                                // after all words are defined, next definition should be information
586                                if (type == fLElementType::TERM)
587                                {
588                                        type = fLElementType::INFO;
589                                }
590                                else
591                                {
592                                        logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "Axioms and iteration number should be defined after word definitions");
593                                        return lastpos + 1;
594                                }
595                        }
596                        else if (line.startsWith("r:"))
597                        {
598                                // after information definition, the last thing is rule definitions
599                                if (type == fLElementType::TERM)
600                                {
601                                        logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "Axiom is not defined - define it after words definition");
602                                        return lastpos + 1;
603                                }
604                                else if (type == fLElementType::INFO)
605                                {
606                                        type = fLElementType::RULE;
607                                }
608                        }
609                        // create object
610                        fL_Element *obj = NULL;
611                        int res = processLine(type, line.substr(2), obj, linenumber, lastpos, pos - 1);
612                        if (res != 0)
613                        {
614                                if (obj && obj != this) delete obj;
615                                return res;
616                        }
617                        if (obj == this)
618                        {
619                                begin = lastpos;
620                                end = pos - 1;
621                        }
622                        res = obj->processDefinition(this);
623                        if (res != 0)
624                        {
625                                if (obj && obj != this) delete obj;
626                                return res;
627                        }
628                }
629                lastpos = pos;
630        }
631        if (type == fLElementType::TERM)
632        {
633                logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "Info line was not declared");
634                return 1;
635        }
636        return 0;
637}
638
639int fL_Word::saveEvals(bool keepformulas)
640{
641        if (npar > 0)
642        {
643                Param par(tab);
644                par.select(data);
645                for (int i = 0; i < npar; i++)
646                {
647                        SString t(par.id(i));
648                        if (parevals[i] != NULL)
649                        {
650                                double val;
651                                if (parevals[i]->evaluateRPN(val) != 0)
652                                {
653                                        logMessage("fL_Word", "saveEvals", LOG_ERROR, "Could not stringify mathematical expression in Word");
654                                        return 1;
655                                }
656                                if (val == 0)
657                                {
658                                        par.setString(i, "");
659                                }
660                                else
661                                {
662                                        if (keepformulas)
663                                        {
664                                                std::string res;
665                                                if (parevals[i]->RPNToInfix(res) != 0)
666                                                {
667                                                        logMessage("fL_Word", "saveEvals", LOG_ERROR, "Could not stringify mathematical expression in Word");
668                                                        return 1;
669                                                }
670                                                par.setString(i, res.c_str());
671                                        }
672                                        else
673                                        {
674                                                SString r = SString::valueOf(val);
675                                                par.setString(i, r);
676                                        }
677                                }
678                        }
679                }
680        }
681        return 0;
682}
683
684// Methods for converting L-System objects to string
685
686SString fL_Word::toString()
687{
688        Param par(fL_word_paramtab);
689        fL_Word *obj = new fL_Word();
690        par.select(this);
691        SString res;
692        par.saveSingleLine(res, obj, true, false);
693        res = SString("w:") + res;
694        delete obj;
695        return res;
696}
697
698SString fL_Word::stringify(bool keepformulas)
699{
700        SString res = name;
701        SString params = "";
702        if (npar > 0)
703        {
704                saveEvals(keepformulas);
705                Param par(tab);
706                void *obj = ParamObject::makeObject(tab);
707                par.select(obj);
708                par.setDefault();
709                par.select(data);
710                par.saveSingleLine(params, obj, false, false);
711                ParamObject::freeObject(obj);
712        }
713        res += "(";
714        res += params + ")";
715        return res;
716}
717
718SString fL_Rule::toString()
719{
720        predecessor = objpred->name;
721        std::string tmp;
722        if (condeval)
723        {
724                condeval->RPNToInfix(tmp);
725                condition = tmp.c_str();
726        }
727        else
728        {
729                condition = "";
730        }
731        successor = "";
732        std::list<fL_Word *>::iterator i;
733        for (i = objsucc.begin(); i != objsucc.end(); i++)
734        {
735                successor += (*i)->stringify();
736        }
737        Param par(fL_rule_paramtab);
738        fL_Rule *obj = new fL_Rule(0, 0);
739        par.select(this);
740        SString res;
741        par.saveSingleLine(res, obj, true, false);
742        res = SString("r:") + res;
743        delete obj;
744        return res;
745}
746
747SString fL_Builder::getStringifiedProducts()
748{
749        axiom = "";
750        std::list<fL_Word *>::iterator i;
751        for (i = genotype.begin(); i != genotype.end(); i++)
752        {
753                axiom += (*i)->stringify(false);
754        }
755        return axiom;
756}
757
758SString fL_Builder::toString()
759{
760        SString res;
761        for (std::unordered_map<std::string, fL_Word *>::iterator it = words.begin(); it != words.end(); it++)
762        {
763                if (!it->second->builtin)
764                {
765                        res += it->second->toString();
766                }
767        }
768        getStringifiedProducts();
769        removeRedundantRules();
770        Param par(fL_builder_paramtab);
771        fL_Builder *obj = new fL_Builder();
772        par.select(this);
773        SString tmp;
774        par.saveSingleLine(tmp, obj, true, false);
775        res += SString("i:") + tmp;
776        delete obj;
777        for (fL_Rule * rule : rules)
778        {
779                res += rule->toString();
780        }
781        return res;
782}
783
784int fL_Rule::deploy(fL_Builder *builder, fL_Word *in, std::list<fL_Word *>::iterator &it, double currtime)
785{
786        // if predecessor and given word differ, then rule is not applicable
787        if (in->name != objpred->name || in->npar != objpred->npar)
788        {
789                return 1;
790        }
791        // store predecessor values in separate array
792        double *inwordvalues = new double[in->npar];
793        for (int i = 0; i < in->npar; i++)
794        {
795                if (in->parevals[i] != NULL)
796                {
797                        in->parevals[i]->modifyVariable(-1, currtime == in->creationiter + 1.0 ? 1.0 : currtime - floor(currtime));
798                        in->parevals[i]->evaluateRPN(inwordvalues[i]);
799                }
800                else
801                {
802                        inwordvalues[i] = 0;
803                }
804        }
805        // if condition exists
806        if (condeval)
807        {
808                // check if condition is satisfied. If not, rule is not applicable
809                for (int i = 0; i < in->npar; i++)
810                {
811                        condeval->modifyVariable(i, inwordvalues[i]);
812                }
813                double condvalue;
814                condeval->evaluateRPN(condvalue);
815                if (condvalue == 0)
816                {
817                        delete[] inwordvalues;
818                        return 1;
819                }
820        }
821
822        // remove predecessor word from genotype and replace it with successor
823        it = builder->genotype.erase(it);
824        for (std::list<fL_Word *>::iterator word = objsucc.begin(); word != objsucc.end(); word++)
825        {
826                // create new word and copy properties from word definition
827                fL_Word *nword = new fL_Word(false, begin, end);
828                *nword = **word;
829                // store information about when word has been created
830                nword->creationiter = currtime;
831                nword->parevals.clear();
832                if (nword->npar > 0)
833                {
834                        nword->data = ParamObject::makeObject(nword->tab);
835                }
836                // calculate word parameters and store MathEvaluation objects for further
837                // time manipulations.
838                Param par((*word)->tab, (*word)->data);
839                Param npar(nword->tab, nword->data);
840                for (int q = 0; q < nword->npar; q++)
841                {
842                        if ((*word)->parevals[q] == NULL)
843                        {
844                                if ((*word)->builtin && (strcmp(npar.id(q), "d") == 0))
845                                {
846                                        SString t = par.getString(q);
847                                        npar.setString(q, t);
848                                        nword->parevals.push_back(NULL);
849                                }
850                                if ((*word)->builtin && (strcmp(npar.id(q), FL_PE_CONN_ATTR) == 0))
851                                {
852                                        SString t = par.getString(q);
853                                        if (t.length() > 0)
854                                        {
855                                                fL_Word *attrword = NULL;
856                                                builder->createWord(t, attrword, in->npar, begin, end);
857                                                for (int j = 0; j < attrword->npar; j++)
858                                                {
859                                                        if (attrword->parevals[j])
860                                                        {
861                                                                for (int i = 0; i < in->npar; i++)
862                                                                {
863                                                                        attrword->parevals[j]->modifyVariable(i, inwordvalues[i]);
864                                                                }
865                                                        }
866                                                }
867                                                SString res = attrword->stringify(false);
868                                                npar.setString(q, res);
869                                                nword->parevals.push_back(NULL);
870                                                delete attrword;
871                                        }
872                                }
873                                else
874                                {
875                                        //MathEvaluation *ev = new MathEvaluation(0);
876                                        //ev->convertString("0");
877                                        //nword->parevals.push_back(ev);
878                                        nword->parevals.push_back(NULL);
879                                }
880                        }
881                        else
882                        {
883                                std::string tmp;
884                                (*word)->parevals[q]->RPNToInfix(tmp);
885                                MathEvaluation *ev = new MathEvaluation(in->npar);
886                                for (int i = 0; i < in->npar; i++)
887                                {
888                                        ev->modifyVariable(i, inwordvalues[i]);
889                                }
890                                ev->modifyVariable(-1, currtime == (*word)->creationiter + 1.0 ? 1.0 : currtime - floor(currtime));
891                                ev->convertString(tmp);
892                                nword->parevals.push_back(ev);
893                        }
894                }
895                builder->genotype.insert(it, nword);
896        }
897        delete[] inwordvalues;
898        delete in;
899        return 0;
900}
901
902int fL_Builder::iterate(double currtime)
903{
904        // deploy proper rules for all words in current genotype
905        std::list<fL_Word *>::iterator word = genotype.begin();
906        while (word != genotype.end())
907        {
908                bool deployed = false;
909                for (fL_Rule * rule : rules)
910                {
911                        if (rule->deploy(this, (*word), word, currtime) == 0)
912                        {
913                                deployed = true;
914                                break;
915                        }
916                }
917                if (!deployed) word++;
918        }
919        return 0;
920}
921
922int fL_Builder::alterTimedProperties(double currtime)
923{
924        // alter parameters of all words, if they are time-dependent
925        std::list<fL_Word *>::iterator word = genotype.begin();
926        while (word != genotype.end())
927        {
928                if (currtime - (*word)->creationiter <= 1.0)
929                {
930                        for (MathEvaluation *ev : (*word)->parevals)
931                        {
932                                if (ev) ev->modifyVariable(-1, currtime == (*word)->creationiter + 1.0 ? 1.0 : currtime - floor(currtime));
933                        }
934                }
935                word++;
936        }
937        return 0;
938}
939
940int fL_Builder::alterPartProperties(Part *part, fL_Word *stickword, double &alterationcount)
941{
942        Param par(stickword->tab, stickword->data);
943        Param ppar = part->properties();
944        for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
945        {
946                double mn, mx, df;
947                ppar.getMinMaxDouble(ppar.findId(fL_part_names[i]), mn, mx, df);
948                double currval;
949                if (!stickword->parevals[i])
950                {
951                        currval = df;
952                }
953                else
954                {
955                        stickword->parevals[i]->evaluateRPN(currval);
956                        currval = sigmoidTransform(currval, mn, mx);
957                }
958                double partprop = (ppar.getDoubleById(fL_part_names[i]) * alterationcount +
959                        currval) / (alterationcount + 1.0);
960                ppar.setDoubleById(fL_part_names[i], partprop);
961        }
962        return 0;
963}
964
965double fL_Word::distance(fL_Word *right)
966{
967        if (name != right->name || npar != right->npar)
968        {
969                return -1;
970        }
971        double distance = 0;
972        for (int i = 0; i < npar; i++)
973        {
974                double l = 0;
975                double r = 0;
976                if (parevals[i]) parevals[i]->evaluateRPN(l);
977                if (right->parevals[i]) right->parevals[i]->evaluateRPN(r);
978                distance += (l - r) * (l - r);
979        }
980        return sqrt(distance);
981}
982
983Neuro *fL_Builder::findInputNeuron(std::pair<std::list<fL_Word *>::iterator, Neuro *> currneu, fL_Word *attractor)
984{
985        if (!attractor)
986        {
987                std::list<fL_Word *>::reverse_iterator riter(currneu.first);
988                std::list<fL_Word *>::iterator iter(currneu.first);
989                iter++;
990                while (riter != genotype.rend() || iter != genotype.end())
991                {
992                        if (iter != genotype.end())
993                        {
994                                if ((*iter)->name == "N" && (*iter)->bodyelementpointer != currneu.second)
995                                {
996                                        return (Neuro *)(*iter)->bodyelementpointer;
997                                }
998                                iter++;
999                        }
1000                        if (riter != genotype.rend())
1001                        {
1002                                if ((*riter)->name == "N" && (*riter)->bodyelementpointer != currneu.second)
1003                                {
1004                                        return (Neuro *)(*riter)->bodyelementpointer;
1005                                }
1006                                riter++;
1007                        }
1008                }
1009                return NULL;
1010        }
1011        else
1012        {
1013                double mindistance = -1;
1014                std::list<fL_Word *>::iterator minit = genotype.end();
1015                for (std::list<fL_Word *>::iterator it = genotype.begin(); it != genotype.end(); it++)
1016                {
1017                        double currdist = attractor->distance((*it));
1018                        if (currdist != -1 && (currdist < mindistance || mindistance == -1))
1019                        {
1020                                mindistance = currdist;
1021                                minit = it;
1022                        }
1023                }
1024                if (minit != genotype.end())
1025                {
1026                        for (; minit != genotype.end(); minit++)
1027                        {
1028                                if ((*minit)->name == "N" && (*minit)->bodyelementpointer)
1029                                {
1030                                        Neuro *n = (Neuro *)(*minit)->bodyelementpointer;
1031                                        if (n->getClass()->getPreferredOutput() != 0)
1032                                        {
1033                                                return n;
1034                                        }
1035                                }
1036                        }
1037                }
1038        }
1039        return NULL;
1040}
1041
1042double fL_Builder::sigmoidTransform(double input, double mn, double mx)
1043{
1044        return mn + (mx - mn) * (1.0 / (1.0 + exp(-input)));
1045}
1046
1047int fL_Builder::buildModelFromSequence(Model *model)
1048{
1049        fL_State currstate;
1050        std::unordered_map<Part *, double> counters;
1051        std::stack<fL_State> statestack;
1052        std::vector<std::pair<std::list<fL_Word *>::iterator, Neuro *>> connsbuffer;
1053        Part *firstpart = NULL;
1054
1055        for (std::list<fL_Word *>::iterator w = genotype.begin(); w != genotype.end(); w++)
1056        {
1057                fL_Word *word = (*w);
1058                if (word->builtin)
1059                {
1060                        if (word->name == "S")
1061                        {
1062                                if (!currstate.currpart)
1063                                {
1064                                        if (!firstpart)
1065                                        {
1066                                                firstpart = new Part();
1067                                                firstpart->p = Pt3D_0;
1068                                                counters[firstpart] = 0;
1069                                                model->addPart(firstpart);
1070                                                if (using_mapping) firstpart->addMapping(IRange(word->begin, word->end));
1071                                        }
1072                                        currstate.currpart = firstpart;
1073                                }
1074                                if (alterPartProperties(currstate.currpart, word, counters[currstate.currpart]) != 0)
1075                                {
1076                                        return 1;
1077                                }
1078                                counters[currstate.currpart] += 1;
1079                                Part *newpart = new Part();
1080                                counters[newpart] = 0;
1081                                if (alterPartProperties(newpart, word, counters[newpart]) != 0)
1082                                {
1083                                        delete newpart;
1084                                        return 1;
1085                                }
1086                                Param par(word->tab, word->data);
1087                                double length;
1088                                if (!word->parevals[FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT])
1089                                {
1090                                        length = FL_DEFAULT_LENGTH; // default length value
1091                                }
1092                                else
1093                                {
1094                                        double parsedval = 0.0;
1095                                        if (word->parevals[FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT]->evaluateRPN(parsedval) != 0)
1096                                        {
1097                                                delete newpart;
1098                                                logMessage("fL_Builder", "developModel", LOG_ERROR,
1099                                                        "Error parsing word parameter");
1100                                                return 1;
1101                                        }
1102                                        length = sigmoidTransform(parsedval, FL_MINIMAL_LENGTH, FL_MAXIMAL_LENGTH);
1103                                }
1104                                newpart->p = currstate.currpart->p + currstate.direction * length;
1105                                counters[newpart] += 1;
1106                                model->addPart(newpart);
1107                                if (using_mapping) newpart->addMapping(IRange(word->begin, word->end));
1108                                Joint *newjoint = new Joint();
1109                                newjoint->attachToParts(currstate.currpart, newpart);
1110
1111                                Param jpar = newjoint->properties();
1112                                for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
1113                                {
1114                                        double mn, mx, df;
1115                                        jpar.getMinMaxDouble(jpar.findId(fL_joint_names[i]), mn, mx, df);
1116                                        double jointprop;
1117                                        if (!word->parevals[FL_PART_PROPS_COUNT + i])
1118                                        {
1119                                                jointprop = df; // assign default value
1120                                        }
1121                                        else
1122                                        {
1123                                                if (word->parevals[FL_PART_PROPS_COUNT + i]->evaluateRPN(jointprop) != 0)
1124                                                {
1125                                                        logMessage("fL_Builder", "developModel", LOG_ERROR,
1126                                                                "Error parsing word parameter");
1127                                                        delete newjoint;
1128                                                        return 1;
1129                                                }
1130                                                jointprop = sigmoidTransform(jointprop, mn, mx);
1131                                        }
1132                                        jpar.setDoubleById(fL_joint_names[i], jointprop);
1133                                }
1134                                model->addJoint(newjoint);
1135                                if (using_mapping) newjoint->addMapping(IRange(word->begin, word->end));
1136                                currstate.currpart = newpart;
1137                        }
1138                        else if (word->name == "N")
1139                        {
1140                                Param npar(word->tab, word->data);
1141                                Neuro *neu = new Neuro();
1142                                SString details = npar.getStringById("d");
1143                                if (details == "")
1144                                {
1145                                        details = "N";
1146                                }
1147                                neu->setDetails(details);
1148                                if (!neu->getClass())
1149                                {
1150                                        logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing neuron class");
1151                                        delete neu;
1152                                        return 1;
1153                                }
1154                                model->addNeuro(neu);
1155                                if (using_mapping) neu->addMapping(IRange(word->begin, word->end));
1156                                if (neu->getClass()->getPreferredInputs() != 0)
1157                                {
1158                                        currstate.currneuron = neu;
1159                                }
1160                                word->bodyelementpointer = neu;
1161                        }
1162                        else if (word->name == "C")
1163                        {
1164                                connsbuffer.push_back({ w, currstate.currneuron });
1165                        }
1166                        else if (word->name.startsWith("rot"))
1167                        {
1168                                Orient rotmatrix = Orient_1;
1169                                double rot;
1170                                if (!word->parevals[0])
1171                                {
1172                                        rot = 0;
1173                                }
1174                                else if (word->parevals[0]->evaluateRPN(rot) != 0)
1175                                {
1176                                        logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing rotation word");
1177                                        return 1;
1178                                }
1179
1180                                rot = sigmoidTransform(rot, -M_PI, M_PI);
1181
1182                                if (word->name == "rotX")
1183                                {
1184                                        rotmatrix.rotate(Pt3D(rot, 0, 0));
1185                                }
1186                                else if (word->name == "rotY")
1187                                {
1188                                        rotmatrix.rotate(Pt3D(0, rot, 0));
1189                                }
1190                                else if (word->name == "rotZ")
1191                                {
1192                                        rotmatrix.rotate(Pt3D(0, 0, rot));
1193                                }
1194                                currstate.direction = rotmatrix.transform(currstate.direction);
1195                                currstate.direction.normalize();
1196                        }
1197                        else if (word->name == "[")
1198                        {
1199                                statestack.push(currstate);
1200                        }
1201                        else if (word->name == "]")
1202                        {
1203                                currstate = statestack.top();
1204                                statestack.pop();
1205                        }
1206                }
1207        }
1208
1209        // connections need
1210        // std::pair<std::list<fL_Word *>::iterator, Neuro *> conndata : connsbuffer
1211        for (unsigned int i = 0; i < connsbuffer.size(); i++)
1212        {
1213                if (connsbuffer[i].second == NULL ||
1214                        (connsbuffer[i].second->getClass()->getPreferredInputs() != -1 &&
1215                                connsbuffer[i].second->getInputCount() >=
1216                                connsbuffer[i].second->getClass()->getPreferredInputs()))
1217                {
1218                        // since connections are separated entities from neurons, it may happen
1219                        // that there will be no neuron to connect to
1220                        // logMessage("fL_Builder", "developModel", LOG_DEBUG, "Connection could not be established");
1221                }
1222                else
1223                {
1224                        Param par((*connsbuffer[i].first)->tab, (*connsbuffer[i].first)->data);
1225                        SString attr = par.getStringById(FL_PE_CONN_ATTR);
1226                        fL_Word *attractor = NULL;
1227                        if (attr.length() > 0)
1228                        {
1229                                createWord(attr, attractor, 0, (*connsbuffer[i].first)->begin, (*connsbuffer[i].first)->end);
1230                        }
1231                        Neuro *neu = findInputNeuron(connsbuffer[i], attractor);
1232                        double weight = 0.0;
1233                        if ((*connsbuffer[i].first)->parevals[0])
1234                        {
1235                                if ((*connsbuffer[i].first)->parevals[0]->evaluateRPN(weight) != 0)
1236                                {
1237                                        logMessage("fL_Builder", "developModel", LOG_ERROR,
1238                                                "Error parsing word parameter");
1239                                        delete attractor;
1240                                        return 1;
1241                                }
1242                        }
1243                        if (neu)
1244                        {
1245                                connsbuffer[i].second->addInput(neu, weight);
1246                                if (using_mapping) neu->addMapping(
1247                                        IRange((*connsbuffer[i].first)->begin,
1248                                                (*connsbuffer[i].first)->end));
1249                        }
1250                        else
1251                        {
1252                                connsbuffer[i].second->addInput(connsbuffer[i].second, weight);
1253                                if (using_mapping) neu->addMapping(
1254                                        IRange((*connsbuffer[i].first)->begin,
1255                                                (*connsbuffer[i].first)->end));
1256                        }
1257                        delete attractor;
1258                }
1259        }
1260        return 0;
1261}
1262
1263void fL_Builder::clearModelElements(Model *m)
1264{
1265        for (int i = 0; i < m->getJointCount(); i++)
1266        {
1267                m->removeJoint(i, 0);
1268        }
1269        for (int i = 0; i < m->getNeuroCount(); i++)
1270        {
1271                m->removeNeuro(i, true);
1272        }
1273        for (int i = 0; i < m->getNeuroCount(); i++)
1274        {
1275                m->removePart(i, 0, 0);
1276        }
1277        m->clearMap();
1278}
1279
1280Model* fL_Builder::developModel(double &neededtime)
1281{
1282        double curriter = 0;
1283        double timestamp = 1.0 / numckp;
1284        double t = 0;
1285        Model *m = new Model();
1286        m->open(using_checkpoints);
1287        bool wordsexceeded = false;
1288        for (; t <= time; t += timestamp)
1289        {
1290                alterTimedProperties(t); // always alter timed properties in the beginning
1291                // if iteration exceeds integer value, then deploy rules
1292                if (floor(t) > curriter)
1293                {
1294                        iterate(t);
1295                        curriter += 1.0;
1296                }
1297                if (using_checkpoints)
1298                {
1299                        clearModelElements(m);
1300                        if (buildModelFromSequence(m) != 0)
1301                        {
1302                                delete m;
1303                                return NULL;
1304                        }
1305                        m->checkpoint();
1306                }
1307                if (maxwords != -1 && ((int)genotype.size()) > maxwords)
1308                {
1309                        wordsexceeded = true;
1310                        break;
1311                }
1312        }
1313
1314        if (wordsexceeded)
1315        {
1316                neededtime = t;
1317        }
1318        else
1319        {
1320                neededtime = time;
1321        }
1322
1323        // if exact time of development was not reached due to floating point errors,
1324        // then alter timed properties
1325        if (time < t)
1326        {
1327                alterTimedProperties(time);
1328        }
1329        clearModelElements(m);
1330        if (buildModelFromSequence(m) != 0)
1331        {
1332                delete m;
1333                return NULL;
1334        }
1335        if (using_checkpoints)
1336        {
1337                m->checkpoint();
1338        }
1339        m->close();
1340        return m;
1341}
1342
1343int fL_Builder::countSticksInSequence(std::list<fL_Word *> *sequence)
1344{
1345        int count = 0;
1346        for (std::list<fL_Word *>::iterator it = sequence->begin(); it != sequence->end(); it++)
1347        {
1348                if ((*it)->builtin && (*it)->name == "S")
1349                {
1350                        count++;
1351                }
1352        }
1353        return count;
1354}
1355
1356int fL_Builder::countDefinedWords()
1357{
1358        return words.size() - builtincount;
1359}
1360
1361int fL_Builder::countWordsInLSystem()
1362{
1363        int count = genotype.size();
1364        for (fL_Rule *rul : rules)
1365        {
1366                count += rul->objsucc.size();
1367        }
1368        count += words.size();
1369        return count;
1370}
1371
1372void fL_Builder::removeRedundantRules()
1373{
1374        for (std::vector<fL_Rule *>::iterator it = rules.begin();
1375                it != rules.end(); it++)
1376        {
1377                std::vector<fL_Rule *>::iterator it2 = it;
1378                it2++;
1379                while (it2 != rules.end())
1380                {
1381                        bool todelete = false;
1382                        if ((*it)->objpred->name == (*it2)->objpred->name)
1383                        {
1384                                if ((*it)->condeval == NULL && (*it2)->condeval == NULL)
1385                                {
1386                                        todelete = true;
1387                                }
1388                                else if ((*it)->condeval == NULL && (*it2)->condeval != NULL)
1389                                {
1390                                        std::iter_swap(it, it2);
1391                                }
1392                                else if ((*it)->condeval != NULL && (*it2)->condeval != NULL)
1393                                {
1394                                        if ((*it)->condeval->getStringifiedRPN() ==
1395                                                (*it2)->condeval->getStringifiedRPN())
1396                                        {
1397                                                todelete = true;
1398                                        }
1399                                }
1400                        }
1401                        if (todelete)
1402                        {
1403                                delete (*it2);
1404                                it2 = rules.erase(it2);
1405                        }
1406                        else
1407                        {
1408                                it2++;
1409                        }
1410                }
1411        }
1412}
Note: See TracBrowser for help on using the repository browser.