source: cpp/frams/genetics/f4/f4_conv.cpp @ 1249

Last change on this file since 1249 was 1249, checked in by Maciej Komosinski, 10 months ago

Multiple issues fixed when calculating bending range for the '|' bending muscle (earlier it was always 1.0 despite the code that supposedly calculated this)

  • Property svn:eol-style set to native
File size: 8.5 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[1212]2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[193]4
[196]5// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
[760]6// Copyright (C) since 2001 Maciej Komosinski
[1236]7// 2018, Grzegorz Latosinski, added development checkpoints and support for new API for neuron types
[196]8
[779]9#include "f4_conv.h"
[375]10#include <common/log.h>
[779]11#include "../genooperators.h" //for GENOPER_OK constant
[193]12
13#ifdef DMALLOC
14#include <dmalloc.h>
15#endif
16
17
[196]18GenoConv_f40::GenoConv_f40()
[193]19{
[196]20        name = "Developmental encoding";
21        in_format = '4';
22        out_format = '0';
23        mapsupport = 1;
[193]24}
25
26
[774]27SString GenoConv_f40::convert(SString &in, MultiMap *map, bool using_checkpoints)
[193]28{
[774]29        f4_Model *model = new f4_Model();
[1231]30        int res = model->buildFromF4(in, using_checkpoints);
31        if (res != GENOPER_OK)
[804]32        {
33                delete model;
34                return SString();  // oops
35        }
[196]36        if (NULL != map)
37                // generate to-f0 conversion map
38                model->getCurrentToF0Map(*map);
[534]39        SString out = model->getF0Geno().getGenes();
[196]40        delete model;
[1240]41
42        /* quick debugging test - print an approximate f1 conversion of every genotype converted to f0:
43        GenoConv_F41_TestOnly conv41;
44        SString f1 = conv41.convert(in, NULL, false);
45        printf("f1 = %s\n", f1.c_str());
46        */
47
[196]48        return out;
[193]49}
50
51
52GenoConv_F41_TestOnly::GenoConv_F41_TestOnly()
53{
[1240]54        name = "Only for testing, approximate f4->f1 converter";
55        // Why approximate? for example, f1 does not allow to continue after branching: X(X,X)X  <-- the last X
56        // Some modifier genes are also not perfectly converted.
57        // And neuron properties are ignored...
[196]58        in_format = '4';
59        out_format = '1';
60        mapsupport = 0;
[193]61}
62
63
[774]64SString GenoConv_F41_TestOnly::convert(SString &in, MultiMap *map, bool using_checkpoints)
[193]65{
[774]66        f4_Model *model = new f4_Model();
[1231]67        int res = model->buildFromF4(in, using_checkpoints);
68        if (res != GENOPER_OK)
[804]69        {
70                delete model;
71                return SString();  // oops
72        }
[196]73        SString out;
74        model->toF1Geno(out);
75        delete model;
76        return out;
[193]77}
78
79
80f4_Model::f4_Model() : Model()
81{
[196]82        cells = NULL;
[193]83}
84
85f4_Model::~f4_Model()
86{
[196]87        if (cells) delete cells;
[193]88}
89
[760]90int f4_Model::buildFromF4(SString &geno, bool using_checkpoints)
[193]91{
[196]92        error = GENOPER_OK;
93        errorpos = -1;
[193]94
[1231]95        // transform geno from string to nodes
96        f4_Node f4rootnode;
97        int res = f4_process(geno.c_str(), &f4rootnode);
98        if (res || (f4rootnode.childCount() != 1)) //consider any error fatal, preventing building a model
99        {
100                error = GENOPER_OPFAIL;
101                errorpos = res;
102                return error;
103        }
104
[196]105        // build cells, and simulate
106        if (cells) delete cells;
[1231]107        cells = new f4_Cells(f4rootnode.child, false);
108        if (cells->getErrorCode() != GENOPER_OK)
[196]109        {
[1227]110                error = cells->getErrorCode();
111                errorpos = cells->getErrorPos();
[196]112                //delete cells;
113                return error;
114        }
[193]115
[196]116        cells->simulate();
[1231]117        if (cells->getErrorCode() != GENOPER_OK)
[196]118        {
[1227]119                error = cells->getErrorCode();
120                errorpos = cells->getErrorPos();
[196]121                return error;
122        }
[193]123
[196]124        // reset recursive traverse flags
[1231]125        for (int i = 0; i < cells->cell_count; i++)
[1249]126                cells->C[i]->recurProcessedFlag = false;
[193]127
[760]128        open(using_checkpoints); // begin model build
[193]129
[196]130        // process every cell
[1231]131        for (int i = 0; i < cells->cell_count; i++)
[196]132        {
[1249]133                int res = buildModelRecur(cells->C[i]);
[196]134                if (res)
135                {
[1231]136                        logPrintf("f4_Model", "buildFromF4", LOG_ERROR, "Error %d when building a Model", res);
[196]137                        error = res;
138                        break;
139                }
140        }
[193]141
[1231]142        int res_close = close();
143        if (res_close == 0) // invalid
144        {
145                logPrintf("f4_Model", "buildFromF4", LOG_ERROR, "Error %d when closing a Model", res_close);
[196]146                error = -10;
[1231]147        }
[193]148
[196]149        return error;
[193]150}
151
152
[774]153f4_Cell* f4_Model::getStick(f4_Cell *C)
[193]154{
[1227]155        if (C->type == CELL_STICK) return C;
[196]156        if (NULL != C->dadlink)
157                return getStick(C->dadlink);
158        // we have no more dadlinks, find any stick
[1227]159        for (int i = 0; i < cells->cell_count; i++)
160                if (cells->C[i]->type == CELL_STICK)
[196]161                        return cells->C[i];
162        // none!
[1227]163        logMessage("f4_Model", "getStick", LOG_ERROR, "Not a single stick");
[196]164        return NULL;
[193]165}
166
167
[1249]168int f4_Model::buildModelRecur(f4_Cell *C)
[193]169{
[1249]170        if (C->recurProcessedFlag)
[196]171                // already processed
172                return 0;
[193]173
[196]174        // mark it processed
[1249]175        C->recurProcessedFlag = true;
[193]176
[196]177        // make sure parent is a stick
[1249]178        if (C->dadlink != NULL)
[1227]179                if (C->dadlink->type != CELL_STICK)
[196]180                {
[1227]181                        C->dadlink = getStick(C->dadlink);
[196]182                }
[193]183
[196]184        // make sure its parent is processed first
[1249]185        if (C->dadlink != NULL)
[196]186        {
[1249]187                int res = buildModelRecur(C->dadlink);
[196]188                if (res) return res;
189        }
[193]190
[196]191        char tmpLine[100];
[1249]192        MultiRange range = C->genoRange;
[193]193
[1227]194        if (C->type == CELL_STICK)
[196]195        {
196                int jj_p1_refno;  // save for later
197                // first end is connected to dad, or new
198                if (C->dadlink == NULL)
199                {
200                        // new part object for firstend
201                        // coordinates are left to be computed by Model
[726]202                        sprintf(tmpLine, "fr=%g,ing=%g,as=%g",
[671]203                                /*1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
[196]204                                //C->firstend.x, C->firstend.y, C->firstend.z
[1227]205                        );
[1249]206                        jj_p1_refno = addFromString(PartType, tmpLine, &range);
207                        if (jj_p1_refno < 0) return -1;
[760]208                        this->checkpoint();
[196]209                }
210                else {
211                        // adjust mass/vol of first endpoint
212                        jj_p1_refno = C->dadlink->p2_refno;
[774]213                        Part *p1 = getPart(jj_p1_refno);
[196]214                        p1->mass += 1.0;
215                        //      p1->volume += 1.0/C->P.mass;
216                }
217                // new part object for lastend
[726]218                sprintf(tmpLine, "fr=%g,ing=%g,as=%g",
[196]219                        //C->lastend.x, C->lastend.y, C->lastend.z
[671]220                        /*"vol=" 1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
[1227]221                );
[1249]222                C->p2_refno = addFromString(PartType, tmpLine, &range);
223                if (C->p2_refno < 0) return -2;
[193]224
[196]225                // new joint object
226                // check that the part references are valid
227                int jj_p2_refno = C->p2_refno;
228                if ((jj_p1_refno < 0) || (jj_p1_refno >= getPartCount())) return -11;
229                if ((jj_p2_refno < 0) || (jj_p2_refno >= getPartCount())) return -12;
[830]230                sprintf(tmpLine, "p1=%d,p2=%d,dx=%g,dy=0,dz=0,rx=%g,ry=0,rz=%g"\
[196]231                        ",stam=%g",
232                        jj_p1_refno, jj_p2_refno,
233                        // relative position -- always (len, 0, 0), along the stick
234                        // this is optional!
[671]235                        C->P.length,
[196]236                        // relative rotation
237                        C->xrot, C->zrot,
238                        //C->P.ruch,   // rotstif
[671]239                        C->P.stamina
[1227]240                );
[1249]241                C->joint_refno = addFromString(JointType, tmpLine, &range);
242                if (C->joint_refno < 0) return -13;
[760]243                this->checkpoint();
[196]244        }
[193]245
[1227]246        if (C->type == CELL_NEURON)
[196]247        {
[774]248                const char* nclass = C->neuclass->name.c_str();
[760]249                if (C->neuclass->getPreferredLocation() == 0)
250                {
[1249]251                        if (strcmp(nclass, "N") == 0) //special case just to specify the only neuron properties supported by f4, i.e., the properties for neuron class 'N'
252                                sprintf(tmpLine, "d=\"N:in=%g,fo=%g,si=%g\"", C->inertia, C->force, C->sigmo);
[760]253                        else
254                                sprintf(tmpLine, "d=\"%s\"", nclass);
[1249]255
256                        C->neuro_refno = addFromString(NeuronType, tmpLine, &range);
257                        if (C->neuro_refno < 0) return -22;
[760]258                        this->checkpoint();
259                }
260                else if (C->neuclass->getPreferredLocation() == 1) // attached to Part or have no required attachment - also part
261                {
[1249]262                        int partno = C->dadlink->p2_refno;
[760]263                        if ((partno < 0) || (partno >= getPartCount())) return -21;
[1227]264
[1249]265                        sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
[1227]266
[1249]267                        C->neuro_refno = addFromString(NeuronType, tmpLine, &range);
268                        if (C->neuro_refno < 0) return -22;
[760]269                        this->checkpoint();
270                }
[1212]271                else // attached to Joint, assume there are only three possibilities of getPreferredLocation()
[760]272                {
[1249]273                        int jointno = C->dadlink->joint_refno;
[1227]274
275                        if (strcmp(nclass, "@") == 0)
276                                sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", jointno, C->P.muscle_power);
277                        else if (strcmp(nclass, "|") == 0)
[1249]278                        {
279                                sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", jointno, C->P.muscle_power, C->dadlink->P.muscle_bend_range); //Macko 2023-05 change: we take muscle_bend_range from dadlink, not from C, because we also assign this neuron to C->dadlink->joint_refno. Without this, for example in /*4*/<<X><<<<X>N:|>X>X>X>X the muscle is attached to the junction with 3 sticks, but gets range=33% as in a four-stick junction. f1 correctly sets range=0.5 for the analogous phenotype: X(X[|],X(X,X,X))
280                        }
[1227]281                        else
282                                sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
283
[1249]284                        C->neuro_refno = addFromString(NeuronType, tmpLine, &range);
285                        if (C->neuro_refno < 0) return -32;
[760]286                        this->checkpoint();
287                }
[1249]288                for (int j = 0; j < C->conns_count; j++)
[196]289                {
[1227]290                        if (C->conns[j]->from != NULL)
[1249]291                                buildModelRecur(C->conns[j]->from);
[193]292
[196]293                        tmpLine[0] = 0;
[1227]294                        if (C->conns[j]->from == NULL)
[760]295                        {
[1227]296                                logMessage("f4_Model", "buildModelRec", LOG_ERROR, "Old code for sensors as inputs embedded in [connection]: C->conns[j]->from == NULL");
[760]297                        }
[196]298                        int from = -1;
[1227]299                        if (C->conns[j]->from != NULL) // input from another neuron
300                                from = C->conns[j]->from->neuro_refno;
[196]301                        if (from >= 0)
302                        {
[1249]303                                sprintf(tmpLine, "%d,%d,%g", C->neuro_refno, from, C->conns[j]->weight);
[726]304                                if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -35;
[760]305                                this->checkpoint();
[196]306                        }
307                }
308        }
309        return 0;
[193]310}
311
312
313void f4_Model::toF1Geno(SString &out)
314{
[196]315        cells->toF1Geno(out);
[193]316}
Note: See TracBrowser for help on using the repository browser.