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

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