source: cpp/frams/genetics/fS/fS_general.h @ 1006

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

Improved the fS encoding

File size: 12.3 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 2019-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#ifndef _FS_GENERAL_H_
6#define _FS_GENERAL_H_
7
8#include <iostream>
9#include <vector>
10#include <map>
11#include <unordered_map>
12#include <exception>
13#include "frams/model/model.h"
14#include "frams/util/multirange.h"
15
16/** @name Values of constants used in encoding */
17//@{
18#define MODE_SEPARATOR ':'
19#define BRANCH_START '('
20#define BRANCH_END ')'
21#define BRANCH_SEPARATOR '^'
22#define PARAM_START '{'
23#define PARAM_END '}'
24const char PARAM_SEPARATOR = ';';
25const char PARAM_KEY_VALUE_SEPARATOR = '=';
26#define NEURON_START '['
27const char NEURON_END = ']';
28const char NEURON_SEPARATOR = ';';
29const SString NEURON_INTERNAL_SEPARATOR("'");
30#define NEURON_I_W_SEPARATOR ':'
31//@}
32
33enum class SHIFT
34{
35        LEFT = -1,
36        RIGHT = 1
37};
38
39
40/** @name Names of node parameters and modifiers*/
41//@{
42#define INGESTION "i"
43#define FRICTION "f"
44#define STIFFNESS "st"
45#define SIZE "s"
46#define SIZE_X "x"
47#define SIZE_Y "y"
48#define SIZE_Z "z"
49#define ROT_X "tx"
50#define ROT_Y "ty"
51#define ROT_Z "tz"
52#define RX "rx"
53#define RY "ry"
54#define RZ "rz"
55//@}
56/** @name Macros and values used in collision detection */
57//@{
58#define DISJOINT 0
59#define COLLISION 1
60#define ADJACENT 2
61//@}
62
63#define HINGE_X 'b'
64#define HINGE_XY 'c'
65
66const double DEFAULT_NEURO_CONNECTION_WEIGHT = 1.0;
67
68const char ELLIPSOID = 'E';
69const char CUBOID = 'C';
70const char CYLINDER = 'R';
71const std::unordered_map<Part::Shape, char> SHAPE_TO_GENE = {
72                {Part::Shape::SHAPE_ELLIPSOID, ELLIPSOID},
73                {Part::Shape::SHAPE_CUBOID,    CUBOID},
74                {Part::Shape::SHAPE_CYLINDER,  CYLINDER},
75};
76
77// This map is inverse to SHAPE_TO_SYMBOL. Those two should be compatible
78const std::unordered_map<char, Part::Shape> GENE_TO_SHAPE = {
79                {ELLIPSOID, Part::Shape::SHAPE_ELLIPSOID},
80                {CUBOID,    Part::Shape::SHAPE_CUBOID},
81                {CYLINDER,  Part::Shape::SHAPE_CYLINDER},
82};
83const int SHAPE_COUNT = 3;    // This should be the count of SHAPE_TO_GENE and GENE_TO_SHAPE
84
85const char DEFAULT_JOINT = 'a';
86const string JOINTS = "bc";
87const string ALL_JOINTS = "abc";
88const int JOINT_COUNT = JOINTS.length();
89const string MODIFIERS = "IFST";
90const char SIZE_MODIFIER = 's';
91const vector<string> PARAMS {INGESTION, FRICTION, ROT_X, ROT_Y, ROT_Z, RX, RY, RZ, SIZE, SIZE_X, SIZE_Y, SIZE_Z,
92                                                         STIFFNESS};
93const vector<string> SIZE_PARAMS {SIZE, SIZE_X, SIZE_Y, SIZE_Z};
94
95/** @name Default values of node parameters*/
96const std::map<Part::Shape, double> volumeMultipliers = {
97                {Part::Shape::SHAPE_CUBOID, 8.0},
98                {Part::Shape::SHAPE_CYLINDER, 2.0 * M_PI},
99                {Part::Shape::SHAPE_ELLIPSOID, (4.0 / 3.0) * M_PI},
100};
101
102/** @name Number of tries of performing a mutation before GENOPER_FAIL is returned */
103#define mutationTries  20
104
105class fS_Exception : public std::exception
106{
107        string msg;
108public:
109
110        int errorPosition;
111        virtual const char *what() const throw()
112        {
113                return msg.c_str();
114        }
115
116        fS_Exception(string _msg, int _errorPosition)
117        {
118                msg = _msg;
119                errorPosition = _errorPosition;
120        }
121};
122
123/**
124 * Draws an integer value from given range
125 * @param to maximal value
126 * @param from minimal value
127 * @return Drawn value
128 */
129int randomFromRange(int to, int from);
130
131/**
132 * Represents a substring of a larger string.
133 * The reference to the original string is stored along with indexes of beginning end length of the substring.
134 */
135class Substring
136{
137public:
138        char *str;        // Pointer to the beginning of the substring
139        int start;        // The beginning index of substring
140        int len;        // The length of substring
141
142        Substring(const char *_str, int _start, int _len)
143        {
144                str = (char *) _str + _start;
145                start = _start;
146                len = _len;
147        }
148
149        Substring(const Substring &other)
150        {
151                str = other.str;
152                start = other.start;
153                len = other.len;
154        }
155
156        const char *c_str()
157        {
158                return str;
159        }
160
161        SString substr(int relativeStart, int len)
162        {
163                const char *substrStart = str + relativeStart;
164                return SString(substrStart, len);
165        }
166
167        int indexOf(char ch)
168        {
169                for (int i = 0; i < len; i++)
170                        if (str[i] == ch)
171                                return i;
172                return -1;
173        }
174
175        void startFrom(int index)
176        {
177                str += index;
178                start += index;
179                len -= index;
180        }
181
182        void shortenBy(int charCount)
183        {
184                len = std::max(len - charCount, 0);
185        }
186
187        char at(int index)
188        {
189                return str[index];
190        }
191
192        /**
193         * Create a new instance of multirange, corresponding to the substring
194         * @return a created multirange
195         */
196        MultiRange toMultiRange()
197        {
198                int end = start + len - 1;
199                return MultiRange(IRange(start, end));
200        }
201};
202
203/**
204 * Stores the state of the node.
205 * The state consists od current location, the direction in which the branch develops
206 * and the current default values of the parameters (default values can be changed by modifiers).
207 */
208class State
209{
210public:
211        Pt3D location;  /// Location of the node
212        Pt3D v;         /// The normalised vector in which current branch develops
213        double fr = 1.0;      /// Friction multiplier
214        double ing = 1.0;      /// Ingestion multiplier
215        double s = 1.0;      /// Size multipliers
216        double stif = 1.0;      /// Stiffness multipliers
217
218        State(State *_state); /// Derive the state from parent
219
220        State(Pt3D _location, Pt3D _v); /// Create the state from parameters
221
222        /**
223         * Add the vector of specified length to location
224         * @param length the length of the vector
225         */
226        void addVector(const double length);
227
228        /**
229         * Rotate the vector by specified values
230         * @param rx rotation by x axis
231         * @param ry rotation by y axis
232         * @param rz rotation by z axis
233         */
234        void rotate(const Pt3D &rotation);
235};
236
237/**
238 * Represent a neuron and its inputs
239 */
240class fS_Neuron: public Neuro
241{
242public:
243        int start, end;
244        std::map<int, double> inputs;
245
246        fS_Neuron(const char *str, int start, int length);
247
248        bool acceptsInputs()
249        {
250                return getClass()->prefinputs < int(inputs.size());
251        }
252};
253
254struct GenotypeParams{
255        double modifierMultiplier;      // Every modifier changes the underlying value by this multiplier
256};
257
258/**
259 * Represents a node in the graph that represents a genotype.
260 * A node corresponds to a single part.
261 * However, it also stores attributes that are specific to fS encoding, such as modifiers and joint types.
262 */
263class Node
264{
265        friend class fS_Genotype;
266
267        friend class GenoOper_fS;
268
269private:
270        Substring *partDescription = nullptr;
271        Node *parent;
272        Part *part;     /// A part object built from node. Used in building the Model
273        int partCodeLen; /// The length of substring that directly describes the corresponding part
274        std::map<string, double> defaultValues;
275        GenotypeParams genotypeParams;
276
277        vector<Node *> children;    /// Vector of all direct children
278        std::map<char, int> modifiers;     /// Vector of all modifiers
279        vector<fS_Neuron *> neurons;    /// Vector of all the neurons
280
281        void prepareParams();
282
283        double getDistance();
284
285        void cleanUp();
286
287        Pt3D getRotation();
288
289        Pt3D getVectorRotation();
290
291        bool isPartSizeValid();
292
293        bool hasPartSizeParam();
294
295        /**
296         * Get the position of part type in genotype
297         *
298         * @return the position of part type
299         */
300        int getPartPosition(Substring &restOfGenotype);
301
302        /**
303         * Extract modifiers from the rest of genotype
304         * @return the remainder of the genotype
305         */
306        void extractModifiers(Substring &restOfGenotype);
307
308        /**
309         * Extract part type from the rest of genotype
310         * @return the remainder of the genotype
311         */
312        void extractPartType(Substring &restOfGenotype);
313
314        /**
315         * Extract neurons from the rest of genotype
316         * @return the remainder of the genotype
317         */
318        void extractNeurons(Substring &restOfGenotype);
319
320        /**
321         * Extract params from the rest of genotype
322         * @return the length og the remainder of the genotype
323         */
324        void extractParams(Substring &restOfGenotype);
325
326        /**
327         * Extract child branches from the rest of genotype
328         * @return vector of child branches
329         */
330        vector<Substring> getBranches(Substring &restOfGenotype);
331
332        /**
333         * Get phenotypic state that derives from ancestors.
334         * Used when building model
335         * @param _state state of the parent
336         */
337        void getState(State *_state);
338
339        /**
340         * Build children internal representations from fS genotype
341         * @param restOfGenotype part of genotype that describes the subtree
342         */
343        void getChildren(Substring &restOfGenotype);
344
345        /**
346         * Create part object from internal representation
347         */
348        void createPart();
349
350        /**
351         * Add joints between current node and the specified child
352         * Used in building model
353         * @param mode pointer to build model
354         * @param child pointer to the child
355         */
356        void addJointsToModel(Model &model, Node *parent);
357
358        /**
359         * Get all the nodes from the subtree that starts in this node
360         * @param reference to vector which contains nodes
361         */
362        void getAllNodes(vector<Node *> &allNodes);
363
364
365        /**
366         * Build model from the subtree that starts in this node
367         * @param pointer to model
368         */
369        void buildModel(Model &model, Node *parent);
370
371public:
372        char joint = DEFAULT_JOINT;           /// Set of all joints
373        Part::Shape partType;  /// The type of the part
374        State *state = nullptr; /// The phenotypic state that inherits from ancestors
375        std::map<string, double> params; /// The map of all the node params
376
377        Node(Substring &genotype, Node *parent, GenotypeParams genotypeParams);
378
379        ~Node();
380
381        /**
382         * Get fS representation of the subtree that starts from this node
383         * @param result the reference to an object which is used to contain fS genotype
384         */
385        void getGeno(SString &result);
386
387        /**
388         * Calculate the effective size of the part (after applying all multipliers and params)
389         * @return The effective size
390         */
391        Pt3D calculateSize();
392
393        /**
394         * Calculate the effective volume of the part
395         * @return The effective volume
396         */
397        double calculateVolume();
398
399        /**
400         * Counts all the nodes in subtree
401         * @return node count
402         */
403        int getNodeCount();
404
405        /**
406         * Extract the value of parameter or return default if parameter not exists
407         * @return the param value
408         */
409        double getParam(string key);
410};
411
412/**
413 * Represents an fS genotype.
414 */
415class fS_Genotype
416{
417        friend class Node;
418
419        friend class GenoOper_fS;
420
421private:
422        /**
423         * Draws a node that has an index greater that specified
424         * @param fromIndex minimal index of the node
425         * @return pointer to drawn node
426         */
427        Node *chooseNode(int fromIndex=0);
428
429        /**
430         * Draws a value from defined distribution
431         * @return Drawn value
432         */
433        void randomFromDistribution();
434
435        /**
436         * Find a node that is nearest (euclidean distance to specified node) and is not a child of specified node
437         * @return Nearest node
438         */
439        Node *getNearestNode(vector<Node *> allNodes, Node *node);
440
441public:
442        Node *startNode = nullptr;    /// The start (root) node. All other nodes are its descendants
443
444
445        static int precision;
446        static bool TURN_WITH_ROTATION;
447
448        /**
449         * Build internal representation from fS format
450         * @param genotype in fS format
451         */
452        fS_Genotype(const string &genotype);
453
454        ~fS_Genotype();
455
456        void getState();
457
458        /**
459         * Get all existing nodes
460         * @return vector of all nodes
461         */
462        vector<Node *> getAllNodes();
463
464        /**
465         * Get all the neurons from the subtree that starts in given node
466         * @param node The beginning of subtree
467         * @return The vector of neurons
468         */
469        static vector<fS_Neuron *> extractNeurons(Node *node);
470
471        /**
472         * Get the index of the neuron in vector of neurons
473         * @param neurons
474         * @param changedNeuron
475         * @return
476         */
477        static int getNeuronIndex(vector<fS_Neuron *> neurons, fS_Neuron *changedNeuron);
478
479        /**
480         * Left- or right- shift the indexes of neuro connections by the given range
481         * @param neurons
482         * @param start The beginning of the range
483         * @param end The end of the range
484         * @param shift
485         */
486        static void shiftNeuroConnections(vector<fS_Neuron *> &neurons, int start, int end, SHIFT shift);
487
488        /**
489         * Get all existing neurons
490         * @return vector of all neurons
491         */
492        vector<fS_Neuron *> getAllNeurons();
493
494        /**
495         * Counts all the nodes in genotype
496         * @return node count
497         */
498        int getNodeCount();
499
500        /**
501         * Check if sizes of all parts in genotype are valid
502        \retval error_position 1-based
503        \retval 0 when all part sizes are valid
504         */
505        int checkValidityOfPartSizes();
506
507        void validateNeuroInputs();
508
509        /**
510         * Builds Model object from internal representation
511         * @param a reference to a model that will contain a built model
512         */
513        Model buildModel(bool using_checkpoints);
514
515        /**
516         * Adds neuro connections to model
517         * @param a reference to a model where the connections will be added
518         */
519        void buildNeuroConnections(Model &model);
520
521        /**
522         * @return genotype in fS format
523         */
524        SString getGeno();
525
526        /**
527         * After creating or deleting a new neuron, rearrange other neurons so that the inputs match
528         */
529        void rearrangeNeuronConnections(fS_Neuron *newNeuron, SHIFT shift);
530
531};
532
533
534#endif
Note: See TracBrowser for help on using the repository browser.