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

Last change on this file since 812 was 803, checked in by Maciej Komosinski, 7 years ago

Performance improvements, including avoiding unnecessary passing of objects by value

File size: 34.5 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2018  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.len()) return false;
79        int opencount = -1;
80        int i = pos;
81        for (; i < src.len(); 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.len() - 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.len() > 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.len() > 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.len() == 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                        break;
478        }
479        Param par(tab);
480        par.select(obj);
481        par.setDefault();
482        ParamInterface::LoadOptions opts;
483
484        par.load(ParamInterface::FormatSingleLine, line, &opts);
485
486        if (opts.parse_failed)
487        {
488                std::string message = "Error in parsing parameters at line:  " + std::to_string(linenumber);
489                logMessage("fL_Builder", "processLine", LOG_ERROR, message.c_str());
490                delete obj;
491                return begin + 1;
492        }
493
494        return 0;
495}
496
497void fL_Builder::addModelWords()
498{
499        // stick S
500        fL_Word *stick = new fL_Word(true);
501        stick->name = "S";
502        stick->npar = 8;
503        for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
504        {
505                stick->mut.addProperty(NULL, fL_part_names[i], "s", fL_part_fullnames[i], fL_part_fullnames[i], PARAM_CANOMITNAME, 0, -1);
506        }
507
508        for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
509        {
510                stick->mut.addProperty(NULL, fL_joint_names[i], "s", fL_joint_fullnames[i], fL_joint_fullnames[i], PARAM_CANOMITNAME, 0, -1);
511        }
512
513        stick->mut.addProperty(NULL, "l", "s", "length", "length", PARAM_CANOMITNAME, 0, -1);
514        stick->tab = ParamObject::makeParamTab((ParamInterface *)&stick->mut, 0, 0, stick->mut.firstMutableIndex());
515        words["S"] = stick;
516        wordnames.push_back("S");
517
518        // neuron N
519        fL_Word *neuron = new fL_Word(true);
520        neuron->name = "N";
521        neuron->npar = 1;
522        neuron->mut.addProperty(NULL, "d", "s", "details", "details", 0, 0, -1);
523        neuron->tab = ParamObject::makeParamTab((ParamInterface *)&neuron->mut, 0, 0, neuron->mut.firstMutableIndex());
524        words["N"] = neuron;
525        wordnames.push_back("N");
526
527        // connection C
528        fL_Word *connection = new fL_Word(true);
529        connection->name = "C";
530        connection->npar = 2;
531        connection->mut.addProperty(NULL, FL_PE_CONN_WEIGHT, "s", "weight", "weight", PARAM_CANOMITNAME, 0, -1);
532        connection->mut.addProperty(NULL, FL_PE_CONN_ATTR, "s", "attractor", "connection attractor", PARAM_CANOMITNAME, 0, -1);
533        connection->tab = ParamObject::makeParamTab((ParamInterface *)&connection->mut, 0, 0, connection->mut.firstMutableIndex());
534        words["C"] = connection;
535        wordnames.push_back("C");
536
537        // rotation objects
538        fL_Word *rotx = new fL_Word(true);
539        rotx->name = "rotX";
540        rotx->npar = 1;
541        rotx->processDefinition(this);
542
543        fL_Word *roty = new fL_Word(true);
544        roty->name = "rotY";
545        roty->npar = 1;
546        roty->processDefinition(this);
547
548        fL_Word *rotz = new fL_Word(true);
549        rotz->name = "rotZ";
550        rotz->npar = 1;
551        rotz->processDefinition(this);
552
553        //fL_Branch *branch = new fL_Branch(fL_Branch::BranchType::OPEN, 0, 0);
554        //branch->processDefinition(this);
555
556        builtincount = words.size();
557}
558
559int fL_Builder::parseGenotype(const SString &genotype)
560{
561        int pos = 0;
562        int lastpos = 0;
563        SString line;
564        int linenumber = 0;
565
566        fLElementType type = fLElementType::TERM;
567
568        // add default words first to prevent redefinitions
569        addModelWords();
570
571        while (genotype.getNextToken(pos, line, '\n'))
572        {
573                if (line.len() > 0)
574                {
575                        // words can be defined in the beginning of genotype
576                        if (line.startsWith("w:") && type != fLElementType::TERM)
577                        {
578                                logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "All words should be defined in the beginning of genotype");
579                                return lastpos + 1;
580                        }
581                        else if (line.startsWith("i:"))
582                        {
583                                // after all words are defined, next definition should be information
584                                if (type == fLElementType::TERM)
585                                {
586                                        type = fLElementType::INFO;
587                                }
588                                else
589                                {
590                                        logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "Axioms and iteration number should be defined after word definitions");
591                                        return lastpos + 1;
592                                }
593                        }
594                        else if (line.startsWith("r:"))
595                        {
596                                // after information definition, the last thing is rule definitions
597                                if (type == fLElementType::TERM)
598                                {
599                                        logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "Axiom is not defined - define it after words definition");
600                                        return lastpos + 1;
601                                }
602                                else if (type == fLElementType::INFO)
603                                {
604                                        type = fLElementType::RULE;
605                                }
606                        }
607                        // create object
608                        fL_Element *obj = NULL;
609                        int res = processLine(type, line.substr(2), obj, linenumber, lastpos, pos - 1);
610                        if (res != 0)
611                        {
612                                if (obj && obj != this) delete obj;
613                                return res;
614                        }
615                        if (obj == this)
616                        {
617                                begin = lastpos;
618                                end = pos - 1;
619                        }
620                        res = obj->processDefinition(this);
621                        if (res != 0)
622                        {
623                                if (obj && obj != this) delete obj;
624                                return res;
625                        }
626                }
627                lastpos = pos;
628        }
629        if (type == fLElementType::TERM)
630        {
631                logMessage("fL_Builder", "parseGenotype", LOG_ERROR, "Info line was not declared");
632                return 1;
633        }
634        return 0;
635}
636
637int fL_Word::saveEvals(bool keepformulas)
638{
639        if (npar > 0)
640        {
641                Param par(tab);
642                par.select(data);
643                for (int i = 0; i < npar; i++)
644                {
645                        SString t(par.id(i));
646                        if (parevals[i] != NULL)
647                        {
648                                double val;
649                                if (parevals[i]->evaluateRPN(val) != 0)
650                                {
651                                        logMessage("fL_Word", "saveEvals", LOG_ERROR, "Could not stringify mathematical expression in Word");
652                                        return 1;
653                                }
654                                if (val == 0)
655                                {
656                                        par.setString(i, "");
657                                }
658                                else
659                                {
660                                        if (keepformulas)
661                                        {
662                                                std::string res;
663                                                if (parevals[i]->RPNToInfix(res) != 0)
664                                                {
665                                                        logMessage("fL_Word", "saveEvals", LOG_ERROR, "Could not stringify mathematical expression in Word");
666                                                        return 1;
667                                                }
668                                                par.setString(i, res.c_str());
669                                        }
670                                        else
671                                        {
672                                                SString r = SString::valueOf(val);
673                                                par.setString(i, r);
674                                        }
675                                }
676                        }
677                }
678        }
679        return 0;
680}
681
682// Methods for converting L-System objects to string
683
684SString fL_Word::toString()
685{
686        Param par(fL_word_paramtab);
687        fL_Word *obj = new fL_Word();
688        par.select(this);
689        SString res;
690        par.saveSingleLine(res, obj, true, false);
691        res = SString("w:") + res;
692        delete obj;
693        return res;
694}
695
696SString fL_Word::stringify(bool keepformulas)
697{
698        SString res = name;
699        SString params = "";
700        if (npar > 0)
701        {
702                saveEvals(keepformulas);
703                Param par(tab);
704                void *obj = ParamObject::makeObject(tab);
705                par.select(obj);
706                par.setDefault();
707                par.select(data);
708                par.saveSingleLine(params, obj, false, false);
709                ParamObject::freeObject(obj);
710        }
711        res += "(";
712        res += params + ")";
713        return res;
714}
715
716SString fL_Rule::toString()
717{
718        predecessor = objpred->name;
719        std::string tmp;
720        if (condeval)
721        {
722                condeval->RPNToInfix(tmp);
723                condition = tmp.c_str();
724        }
725        else
726        {
727                condition = "";
728        }
729        successor = "";
730        std::list<fL_Word *>::iterator i;
731        for (i = objsucc.begin(); i != objsucc.end(); i++)
732        {
733                successor += (*i)->stringify();
734        }
735        Param par(fL_rule_paramtab);
736        fL_Rule *obj = new fL_Rule(0, 0);
737        par.select(this);
738        SString res;
739        par.saveSingleLine(res, obj, true, false);
740        res = SString("r:") + res;
741        delete obj;
742        return res;
743}
744
745SString fL_Builder::getStringifiedProducts()
746{
747        axiom = "";
748        std::list<fL_Word *>::iterator i;
749        for (i = genotype.begin(); i != genotype.end(); i++)
750        {
751                axiom += (*i)->stringify(false);
752        }
753        return axiom;
754}
755
756SString fL_Builder::toString()
757{
758        SString res;
759        for (std::unordered_map<std::string, fL_Word *>::iterator it = words.begin(); it != words.end(); it++)
760        {
761                if (!it->second->builtin)
762                {
763                        res += it->second->toString();
764                }
765        }
766        getStringifiedProducts();
767        removeRedundantRules();
768        Param par(fL_builder_paramtab);
769        fL_Builder *obj = new fL_Builder();
770        par.select(this);
771        SString tmp;
772        par.saveSingleLine(tmp, obj, true, false);
773        res += SString("i:") + tmp;
774        delete obj;
775        for (fL_Rule * rule : rules)
776        {
777                res += rule->toString();
778        }
779        return res;
780}
781
782int fL_Rule::deploy(fL_Builder *builder, fL_Word *in, std::list<fL_Word *>::iterator &it, double currtime)
783{
784        // if predecessor and given word differ, then rule is not applicable
785        if (in->name != objpred->name || in->npar != objpred->npar)
786        {
787                return 1;
788        }
789        // store predecessor values in separate array
790        double *inwordvalues = new double[in->npar];
791        for (int i = 0; i < in->npar; i++)
792        {
793                if (in->parevals[i] != NULL)
794                {
795                        in->parevals[i]->modifyVariable(-1, currtime == in->creationiter + 1.0 ? 1.0 : currtime - floor(currtime));
796                        in->parevals[i]->evaluateRPN(inwordvalues[i]);
797                }
798                else
799                {
800                        inwordvalues[i] = 0;
801                }
802        }
803        // if condition exists
804        if (condeval)
805        {
806                // check if condition is satisfied. If not, rule is not applicable
807                for (int i = 0; i < in->npar; i++)
808                {
809                        condeval->modifyVariable(i, inwordvalues[i]);
810                }
811                double condvalue;
812                condeval->evaluateRPN(condvalue);
813                if (condvalue == 0)
814                {
815                        delete[] inwordvalues;
816                        return 1;
817                }
818        }
819
820        // remove predecessor word from genotype and replace it with successor
821        it = builder->genotype.erase(it);
822        for (std::list<fL_Word *>::iterator word = objsucc.begin(); word != objsucc.end(); word++)
823        {
824                // create new word and copy properties from word definition
825                fL_Word *nword = new fL_Word(false, begin, end);
826                *nword = **word;
827                // store information about when word has been created
828                nword->creationiter = currtime;
829                nword->parevals.clear();
830                if (nword->npar > 0)
831                {
832                        nword->data = ParamObject::makeObject(nword->tab);
833                }
834                // calculate word parameters and store MathEvaluation objects for further
835                // time manipulations.
836                Param par((*word)->tab, (*word)->data);
837                Param npar(nword->tab, nword->data);
838                for (int q = 0; q < nword->npar; q++)
839                {
840                        if ((*word)->parevals[q] == NULL)
841                        {
842                                if ((*word)->builtin && (strcmp(npar.id(q), "d") == 0))
843                                {
844                                        SString t = par.getString(q);
845                                        npar.setString(q, t);
846                                        nword->parevals.push_back(NULL);
847                                }
848                                if ((*word)->builtin && (strcmp(npar.id(q), FL_PE_CONN_ATTR) == 0))
849                                {
850                                        SString t = par.getString(q);
851                                        if (t.len() > 0)
852                                        {
853                                                fL_Word *attrword = NULL;
854                                                builder->createWord(t, attrword, in->npar, begin, end);
855                                                for (int j = 0; j < attrword->npar; j++)
856                                                {
857                                                        if (attrword->parevals[j])
858                                                        {
859                                                                for (int i = 0; i < in->npar; i++)
860                                                                {
861                                                                        attrword->parevals[j]->modifyVariable(i, inwordvalues[i]);
862                                                                }
863                                                        }
864                                                }
865                                                SString res = attrword->stringify(false);
866                                                npar.setString(q, res);
867                                                nword->parevals.push_back(NULL);
868                                                delete attrword;
869                                        }
870                                }
871                                else
872                                {
873                                        //MathEvaluation *ev = new MathEvaluation(0);
874                                        //ev->convertString("0");
875                                        //nword->parevals.push_back(ev);
876                                        nword->parevals.push_back(NULL);
877                                }
878                        }
879                        else
880                        {
881                                std::string tmp;
882                                (*word)->parevals[q]->RPNToInfix(tmp);
883                                MathEvaluation *ev = new MathEvaluation(in->npar);
884                                for (int i = 0; i < in->npar; i++)
885                                {
886                                        ev->modifyVariable(i, inwordvalues[i]);
887                                }
888                                ev->modifyVariable(-1, currtime == (*word)->creationiter + 1.0 ? 1.0 : currtime - floor(currtime));
889                                ev->convertString(tmp);
890                                nword->parevals.push_back(ev);
891                        }
892                }
893                builder->genotype.insert(it, nword);
894        }
895        delete[] inwordvalues;
896        delete in;
897        return 0;
898}
899
900int fL_Builder::iterate(double currtime)
901{
902        // deploy proper rules for all words in current genotype
903        std::list<fL_Word *>::iterator word = genotype.begin();
904        while (word != genotype.end())
905        {
906                bool deployed = false;
907                for (fL_Rule * rule : rules)
908                {
909                        if (rule->deploy(this, (*word), word, currtime) == 0)
910                        {
911                                deployed = true;
912                                break;
913                        }
914                }
915                if (!deployed) word++;
916        }
917        return 0;
918}
919
920int fL_Builder::alterTimedProperties(double currtime)
921{
922        // alter parameters of all words, if they are time-dependent
923        std::list<fL_Word *>::iterator word = genotype.begin();
924        while (word != genotype.end())
925        {
926                if (currtime - (*word)->creationiter <= 1.0)
927                {
928                        for (MathEvaluation *ev : (*word)->parevals)
929                        {
930                                if (ev) ev->modifyVariable(-1, currtime == (*word)->creationiter + 1.0 ? 1.0 : currtime - floor(currtime));
931                        }
932                }
933                word++;
934        }
935        return 0;
936}
937
938int fL_Builder::alterPartProperties(Part *part, fL_Word *stickword, double &alterationcount)
939{
940        Param par(stickword->tab, stickword->data);
941        Param ppar = part->properties();
942        for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
943        {
944                double mn, mx, df;
945                ppar.getMinMaxDouble(ppar.findId(fL_part_names[i]), mn, mx, df);
946                double currval;
947                if (!stickword->parevals[i])
948                {
949                        currval = df;
950                }
951                else
952                {
953                        stickword->parevals[i]->evaluateRPN(currval);
954                        currval = sigmoidTransform(currval, mn, mx);
955                }
956                double partprop = (ppar.getDoubleById(fL_part_names[i]) * alterationcount +
957                                currval) / (alterationcount + 1.0);
958                ppar.setDoubleById(fL_part_names[i], partprop);
959        }
960        return 0;
961}
962
963double fL_Word::distance(fL_Word *right)
964{
965        if (name != right->name || npar != right->npar)
966        {
967                return -1;
968        }
969        double distance = 0;
970        for (int i = 0; i < npar; i++)
971        {
972                double l = 0;
973                double r = 0;
974                if (parevals[i]) parevals[i]->evaluateRPN(l);
975                if (right->parevals[i]) right->parevals[i]->evaluateRPN(r);
976                distance += (l - r) * (l - r);
977        }
978        return sqrt(distance);
979}
980
981Neuro *fL_Builder::findInputNeuron(std::pair<std::list<fL_Word *>::iterator, Neuro *> currneu, fL_Word *attractor)
982{
983        if (!attractor)
984        {
985                std::list<fL_Word *>::reverse_iterator riter(currneu.first);
986                std::list<fL_Word *>::iterator iter(currneu.first);
987                iter++;
988                while (riter != genotype.rend() || iter != genotype.end())
989                {
990                        if (iter != genotype.end())
991                        {
992                                if ((*iter)->name == "N" && (*iter)->bodyelementpointer != currneu.second)
993                                {
994                                        return (Neuro *)(*iter)->bodyelementpointer;
995                                }
996                                iter++;
997                        }
998                        if (riter != genotype.rend())
999                        {
1000                                if ((*riter)->name == "N" && (*riter)->bodyelementpointer != currneu.second)
1001                                {
1002                                        return (Neuro *)(*riter)->bodyelementpointer;
1003                                }
1004                                riter++;
1005                        }
1006                }
1007                return NULL;
1008        }
1009        else
1010        {
1011                double mindistance = -1;
1012                std::list<fL_Word *>::iterator minit = genotype.end();
1013                for (std::list<fL_Word *>::iterator it = genotype.begin(); it != genotype.end(); it++)
1014                {
1015                        double currdist = attractor->distance((*it));
1016                        if (currdist != -1 && (currdist < mindistance || mindistance == -1))
1017                        {
1018                                mindistance = currdist;
1019                                minit = it;
1020                        }
1021                }
1022                if (minit != genotype.end())
1023                {
1024                        for (; minit != genotype.end(); minit++)
1025                        {
1026                                if ((*minit)->name == "N" && (*minit)->bodyelementpointer)
1027                                {
1028                                        Neuro *n = (Neuro *)(*minit)->bodyelementpointer;
1029                                        if (n->getClass()->getPreferredOutput() != 0)
1030                                        {
1031                                                return n;
1032                                        }
1033                                }
1034                        }
1035                }
1036        }
1037        return NULL;
1038}
1039
1040double fL_Builder::sigmoidTransform(double input, double mn, double mx)
1041{
1042        return mn + (mx - mn) * (1.0 / (1.0 + exp(-input)));
1043}
1044
1045int fL_Builder::buildModelFromSequence(Model *model)
1046{
1047        fL_State currstate;
1048        std::unordered_map<Part *, double> counters;
1049        std::stack<fL_State> statestack;
1050        std::vector<std::pair<std::list<fL_Word *>::iterator, Neuro *>> connsbuffer;
1051        Part *firstpart = NULL;
1052
1053        for (std::list<fL_Word *>::iterator w = genotype.begin(); w != genotype.end(); w++)
1054        {
1055                fL_Word *word = (*w);
1056                if (word->builtin)
1057                {
1058                        if (word->name == "S")
1059                        {
1060                                if (!currstate.currpart)
1061                                {
1062                                        if (!firstpart)
1063                                        {
1064                                                firstpart = new Part();
1065                                                firstpart->p = Pt3D_0;
1066                                                counters[firstpart] = 0;
1067                                                model->addPart(firstpart);
1068                                                if (using_mapping) firstpart->addMapping(IRange(word->begin, word->end));
1069                                        }
1070                                        currstate.currpart = firstpart;
1071                                }
1072                                if (alterPartProperties(currstate.currpart, word, counters[currstate.currpart]) != 0)
1073                                {
1074                                        return 1;
1075                                }
1076                                counters[currstate.currpart] += 1;
1077                                Part *newpart = new Part();
1078                                counters[newpart] = 0;
1079                                if (alterPartProperties(newpart, word, counters[newpart]) != 0)
1080                                {
1081                                        delete newpart;
1082                                        return 1;
1083                                }
1084                                Param par(word->tab, word->data);
1085                                double length;
1086                                if (!word->parevals[FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT])
1087                                {
1088                                        length = FL_DEFAULT_LENGTH; // default length value
1089                                }
1090                                else
1091                                {
1092                                        double parsedval = 0.0;
1093                                        if (word->parevals[FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT]->evaluateRPN(parsedval) != 0)
1094                                        {
1095                                                delete newpart;
1096                                                logMessage("fL_Builder", "developModel", LOG_ERROR,
1097                                                                "Error parsing word parameter");
1098                                                return 1;
1099                                        }
1100                                        length = sigmoidTransform(parsedval, FL_MINIMAL_LENGTH, FL_MAXIMAL_LENGTH);
1101                                }
1102                                newpart->p = currstate.currpart->p + currstate.direction * length;
1103                                counters[newpart] += 1;
1104                                model->addPart(newpart);
1105                                if (using_mapping) newpart->addMapping(IRange(word->begin, word->end));
1106                                Joint *newjoint = new Joint();
1107                                newjoint->attachToParts(currstate.currpart, newpart);
1108
1109                                Param jpar = newjoint->properties();
1110                                for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
1111                                {
1112                                        double mn, mx, df;
1113                                        jpar.getMinMaxDouble(jpar.findId(fL_joint_names[i]), mn, mx, df);
1114                                        double jointprop;
1115                                        if (!word->parevals[FL_PART_PROPS_COUNT + i])
1116                                        {
1117                                                jointprop = df; // assign default value
1118                                        }
1119                                        else
1120                                        {
1121                                                if (word->parevals[FL_PART_PROPS_COUNT + i]->evaluateRPN(jointprop) != 0)
1122                                                {
1123                                                        logMessage("fL_Builder", "developModel", LOG_ERROR,
1124                                                                        "Error parsing word parameter");
1125                                                        delete newjoint;
1126                                                        return 1;
1127                                                }
1128                                                jointprop = sigmoidTransform(jointprop, mn, mx);
1129                                        }
1130                                        jpar.setDoubleById(fL_joint_names[i], jointprop);
1131                                }
1132                                model->addJoint(newjoint);
1133                                if (using_mapping) newjoint->addMapping(IRange(word->begin, word->end));
1134                                currstate.currpart = newpart;
1135                        }
1136                        else if (word->name == "N")
1137                        {
1138                                Param npar(word->tab, word->data);
1139                                Neuro *neu = new Neuro();
1140                                SString details = npar.getStringById("d");
1141                                if (details == "")
1142                                {
1143                                        details = "N";
1144                                }
1145                                neu->setDetails(details);
1146                                if (!neu->getClass())
1147                                {
1148                                        logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing neuron class");
1149                                        delete neu;
1150                                        return 1;
1151                                }
1152                                model->addNeuro(neu);
1153                                if (using_mapping) neu->addMapping(IRange(word->begin, word->end));
1154                                if (neu->getClass()->getPreferredInputs() != 0)
1155                                {
1156                                        currstate.currneuron = neu;
1157                                }
1158                                word->bodyelementpointer = neu;
1159                        }
1160                        else if (word->name == "C")
1161                        {
1162                                connsbuffer.push_back({w, currstate.currneuron});
1163                        }
1164                        else if (word->name.startsWith("rot"))
1165                        {
1166                                Orient rotmatrix = Orient_1;
1167                                double rot;
1168                                if (!word->parevals[0])
1169                                {
1170                                        rot = 0;
1171                                }
1172                                else if (word->parevals[0]->evaluateRPN(rot) != 0)
1173                                {
1174                                        logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing rotation word");
1175                                        return 1;
1176                                }
1177
1178                                rot = sigmoidTransform(rot, -M_PI, M_PI);
1179
1180                                if (word->name == "rotX")
1181                                {
1182                                        rotmatrix.rotate(Pt3D(rot,0,0));
1183                                }
1184                                else if (word->name == "rotY")
1185                                {
1186                                        rotmatrix.rotate(Pt3D(0,rot,0));
1187                                }
1188                                else if (word->name == "rotZ")
1189                                {
1190                                        rotmatrix.rotate(Pt3D(0,0,rot));
1191                                }
1192                                currstate.direction = rotmatrix.transform(currstate.direction);
1193                                currstate.direction.normalize();
1194                        }
1195                        else if (word->name == "[")
1196                        {
1197                                statestack.push(currstate);
1198                        }
1199                        else if (word->name == "]")
1200                        {
1201                                currstate = statestack.top();
1202                                statestack.pop();
1203                        }
1204                }
1205        }
1206
1207        // connections need
1208        // std::pair<std::list<fL_Word *>::iterator, Neuro *> conndata : connsbuffer
1209        for (unsigned int i = 0; i < connsbuffer.size(); i++)
1210        {
1211                if (connsbuffer[i].second == NULL ||
1212                                (connsbuffer[i].second->getClass()->getPreferredInputs() != -1 &&
1213                                connsbuffer[i].second->getInputCount() >=
1214                                connsbuffer[i].second->getClass()->getPreferredInputs()))
1215                {
1216                        // since connections are separated entities from neurons, it may happen
1217                        // that there will be no neuron to connect to
1218                        // logMessage("fL_Builder", "developModel", LOG_DEBUG, "Connection could not be established");
1219                }
1220                else
1221                {
1222                        Param par((*connsbuffer[i].first)->tab, (*connsbuffer[i].first)->data);
1223                        SString attr = par.getStringById(FL_PE_CONN_ATTR);
1224                        fL_Word *attractor = NULL;
1225                        if (attr.len() > 0)
1226                        {
1227                                createWord(attr, attractor, 0, (*connsbuffer[i].first)->begin, (*connsbuffer[i].first)->end);
1228                        }
1229                        Neuro *neu = findInputNeuron(connsbuffer[i], attractor);
1230                        double weight = 0.0;
1231                        if ((*connsbuffer[i].first)->parevals[0])
1232                        {
1233                                if ((*connsbuffer[i].first)->parevals[0]->evaluateRPN(weight) != 0)
1234                                {
1235                                        logMessage("fL_Builder", "developModel", LOG_ERROR,
1236                                                        "Error parsing word parameter");
1237                                        delete attractor;
1238                                        return 1;
1239                                }
1240                        }
1241                        if (neu)
1242                        {
1243                                connsbuffer[i].second->addInput(neu, weight);
1244                                if (using_mapping) neu->addMapping(
1245                                                IRange((*connsbuffer[i].first)->begin,
1246                                                                (*connsbuffer[i].first)->end));
1247                        }
1248                        else
1249                        {
1250                                connsbuffer[i].second->addInput(connsbuffer[i].second, weight);
1251                                if (using_mapping) neu->addMapping(
1252                                                IRange((*connsbuffer[i].first)->begin,
1253                                                                (*connsbuffer[i].first)->end));
1254                        }
1255                        delete attractor;
1256                }
1257        }
1258        return 0;
1259}
1260
1261void fL_Builder::clearModelElements(Model *m)
1262{
1263        for (int i = 0; i < m->getJointCount(); i++)
1264        {
1265                m->removeJoint(i, 0);
1266        }
1267        for (int i = 0; i < m->getNeuroCount(); i++)
1268        {
1269                m->removeNeuro(i, true);
1270        }
1271        for (int i = 0; i < m->getNeuroCount(); i++)
1272        {
1273                m->removePart(i, 0, 0);
1274        }
1275        m->clearMap();
1276}
1277
1278Model* fL_Builder::developModel(double &neededtime)
1279{
1280        double curriter = 0;
1281        double timestamp = 1.0 / numckp;
1282        double t = 0;
1283        Model *m = new Model();
1284        m->open(using_checkpoints);
1285        bool wordsexceeded = false;
1286        for (; t <= time; t+= timestamp)
1287        {
1288                alterTimedProperties(t); // always alter timed properties in the beginning
1289                // if iteration exceeds integer value, then deploy rules
1290                if (floor(t) > curriter)
1291                {
1292                        iterate(t);
1293                        curriter+=1.0;
1294                }
1295                if (using_checkpoints)
1296                {
1297                        clearModelElements(m);
1298                        if (buildModelFromSequence(m) != 0)
1299                        {
1300                                delete m;
1301                                return NULL;
1302                        }
1303                        m->checkpoint();
1304                }
1305                if (maxwords != -1 && ((int)genotype.size()) > maxwords)
1306                {
1307                        wordsexceeded = true;
1308                        break;
1309                }
1310        }
1311
1312        if (wordsexceeded)
1313        {
1314                neededtime = t;
1315        }
1316        else
1317        {
1318                neededtime = time;
1319        }
1320
1321        // if exact time of development was not reached due to floating point errors,
1322        // then alter timed properties
1323        if (time < t)
1324        {
1325                alterTimedProperties(time);
1326        }
1327        clearModelElements(m);
1328        if (buildModelFromSequence(m) != 0)
1329        {
1330                delete m;
1331                return NULL;
1332        }
1333        if (using_checkpoints)
1334        {
1335                m->checkpoint();
1336        }
1337        m->close();
1338        return m;
1339}
1340
1341int fL_Builder::countSticksInSequence(std::list<fL_Word *> sequence)
1342{
1343        int count = 0;
1344        for (std::list<fL_Word *>::iterator it = sequence.begin(); it != sequence.end(); it++)
1345        {
1346                if ((*it)->builtin && (*it)->name == "S")
1347                {
1348                        count++;
1349                }
1350        }
1351        return count;
1352}
1353
1354int fL_Builder::countDefinedWords()
1355{
1356        return words.size() - builtincount;
1357}
1358
1359int fL_Builder::countWordsInLSystem()
1360{
1361        int count = genotype.size();
1362        for (fL_Rule *rul: rules)
1363        {
1364                count += rul->objsucc.size();
1365        }
1366        count += words.size();
1367        return count;
1368}
1369
1370void fL_Builder::removeRedundantRules()
1371{
1372        for (std::vector<fL_Rule *>::iterator it = rules.begin();
1373                        it != rules.end(); it++)
1374        {
1375                std::vector<fL_Rule *>::iterator it2 = it;
1376                it2++;
1377                while (it2 != rules.end())
1378                {
1379                        bool todelete = false;
1380                        if ((*it)->objpred->name == (*it2)->objpred->name)
1381                        {
1382                                if ((*it)->condeval == NULL && (*it2)->condeval == NULL)
1383                                {
1384                                        todelete = true;
1385                                }
1386                                else if ((*it)->condeval == NULL && (*it2)->condeval != NULL)
1387                                {
1388                                        std::iter_swap(it, it2);
1389                                }
1390                                else if ((*it)->condeval != NULL && (*it2)->condeval != NULL)
1391                                {
1392                                        if ((*it)->condeval->getStringifiedRPN() ==
1393                                                        (*it2)->condeval->getStringifiedRPN())
1394                                        {
1395                                                todelete = true;
1396                                        }
1397                                }
1398                        }
1399                        if (todelete)
1400                        {
1401                                delete (*it2);
1402                                it2 = rules.erase(it2);
1403                        }
1404                        else
1405                        {
1406                                it2++;
1407                        }
1408                }
1409        }
1410}
Note: See TracBrowser for help on using the repository browser.