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