source: cpp/frams/genetics/fH/fH_general.h @ 821

Last change on this file since 821 was 821, checked in by Maciej Komosinski, 3 years ago

Performance and stability improvements in fB, fH, and fL; improved parsing and math evaluations in fL

File size: 18.2 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#ifndef _FH_GENERAL_H_
6#define _FH_GENERAL_H_
7
8#include <vector>
9#include "../../model/modelparts.h"
10#include <frams/util/sstring.h>
11#include <frams/param/mutableparam.h>
12#include <frams/param/param.h>
13#include <frams/model/model.h>
14#include <frams/param/paramobj.h>
15
16/** @name Constants used in fH methods */
17//@{
18#define HANDLE_VECTOR_TYPE "f -1.0 1.0 0.0" ///<Vector values type definition
19#define STICKH_LENGTH_TYPE "f 0.001 1.999 1.0" ///<Length of stick handle. Minimum should not be equal to 0, because calculating direction of next part from current stick with length 0 would be impossible
20#define FH_PART_PROPS_COUNT   4 ///<Count of part properties
21#define FH_JOINT_PROPS_COUNT  3 ///<Count of joint properties
22#define FH_PE_NEURO_DET       "d" ///<Id of details type definition in f0_neuro_paramtab
23#define FH_PE_CONN_WEIGHT     "w" ///<Id of weight type definition in f0_neuroconn_paramtab
24//@}
25
26extern const char *fL_part_names[];
27
28extern const char *fL_joint_names[];
29
30class Geno_fH; // from oper.fH
31
32/// Determines to which class Handle belongs.
33enum fHBodyType
34{
35        JOINT, ///<joint type
36        NEURON, ///<neuron type
37        CONNECTION ///<connection type
38};
39
40/**
41 * Base class for all kinds of handles in fH encoding.Each line of fH genotype
42 * describes a handle. Handles can be divided into joints, neurons and
43 * connections. Every handle is described with 2 vectors of 'dimensions' values.
44 * During process of body development those vectors are compared in order to
45 * connect sticks of creature, connect neurons to body parts, and create connections
46 * between neurons.
47 */
48class fH_Handle
49{
50private:
51        int dimensions; ///< number of elements in each vector of a handle. This is read-only field
52protected:
53        static double dist(std::vector<double> left, std::vector<double> right);
54public:
55        fHBodyType type; ///< type of handle, can be stick, neural connection or neuron
56        std::vector<double> first; ///< first vector of handle
57        std::vector<double> second; ///< second vector of handle
58
59        int begin; ///<begin of handle definition in genotype, used in mapping
60        int end; ///<end of handle definition in genotype, used in mapping
61
62        void *obj; ///<pointer to all properties that can be accessed via Param object
63
64        /**
65         * Prepares memory for both handle vectors, according to value in 'dimensions'
66         * variable.
67         * @param type type of handle
68         * @param dimensions number of elements in each vector of handle
69         */
70        fH_Handle(fHBodyType type, int dimensions, int begin, int end) :
71                dimensions(dimensions),
72                type(type),
73                first(dimensions, 0),
74                second(dimensions, 0),
75                begin(begin),
76                end(end),
77                obj(NULL)
78        {}
79
80        int getDimensions() { return dimensions; }
81
82        virtual ~fH_Handle() { if (obj) ParamObject::freeObject(obj); }
83
84        /**
85         * Loads properties of handle after parsing them from genotype.
86         * All properties are available from Param object. This base method only
87         * loads values for handle vectors. Other properties, like stiffness for
88         * stick require overloading this method. The method presumes that values
89         * for vectors are first in ParamTab.
90         * @param par Param object holding parsed properties from genotype
91         */
92        void loadProperties(Param par);
93
94        /**
95         * Saves properties from handle into ParamObject selected by Param.
96         * @param par Param object that has needed paramtab configured and selected ParamObject
97         */
98        void saveProperties(Param &par);
99
100        /**
101         * Calculates distance between current and given handle. Method should
102         * be reimplemented in inheriting classes in order to provide valid values.
103         * @param right second handle to which distance is calculated
104         * @return distance between this and second handle
105         */
106        virtual double distance(fH_Handle * right) = 0;
107
108
109        /**
110         * Computes average of first and second vector of Handle.
111         * @return vector with average values from both handle vectors
112         */
113        std::vector<double> getVectorsAverage();
114};
115
116/**
117 * Class representing stick handle of fH encoding. Every stick has following properties:
118 *  - double stif -  joint property of a stick representing stiffness
119 *  - double rotstif -  joint property of a stick representing rotation stiffness
120 *  - double stamina -  joint property of a stick representing stamina
121 *  - double density -  parts property of a stick representing density
122 *  - double friction -  parts property of a stick representing friction
123 *  - double ingest -  parts property of a stick representing ingestion
124 *  - double assimilation -  parts property of a stick representing assimilation
125 *  - double length -  length of a stick
126 */
127class fH_StickHandle : public fH_Handle
128{
129public:
130        Part *firstpart; ///< first part of created stick
131        Part *secondpart; ///< second part of created stick
132        Joint *joint; ///< joint connecting both parts of created stick
133
134        // vectors used for connecting neurons to parts
135        vector<double> firstparthandle; ///< vector for first part. Used in connecting neurons to parts. Inherited from previous stick
136        vector<double> secondparthandle; ///< vector for second part. Used in connecting neurons to parts. Vector is calculated as average of second vector in current stick handle and first vector of all sticks connected to this part
137
138        /**
139         * This constructor initializes each property of joint and parts of a stick
140         * with default values represented in f0 genotype.
141         * @param dimensions number of elements in each vector of handle
142         */
143        fH_StickHandle(int dimensions, int begin, int end) :
144                fH_Handle(fHBodyType::JOINT, dimensions, begin, end),
145                firstparthandle(dimensions, 0),
146                secondparthandle(dimensions, 0)
147        {
148                firstpart = secondpart = NULL;
149                joint = NULL;
150        }
151
152        /**
153         * Calculates distance between current and given handle. Distance from
154         * StickHandle to StickHandle is calculated as distance between second
155         * vector of current handle and first vector of given handle.
156         * Distance from StickHandle to NeuronHandle is designed only for neurons
157         * that need to be attached to joints and is computed as distance between
158         * average of first and second vector for each handle.
159         * Distance between StickHandle and ConnectionHandle cannot be calculated.
160         * @param right second handle to which distance is calculated
161         * @return Euclidean distance between two vectors
162         */
163        double distance(fH_Handle *right);
164        /**
165         * Creates new part from current stick and its children. Part properties
166         * are calculated as mean of properties of current stick and properties
167         * of other sticks that share this part. Method requires model to add
168         * created part and map genotype responsible for this part.
169         * @param tab Param tab object for access to variables
170         * @param children stick handles that share created part
171         * @param model pointer to model that is being developed in buildModel method
172         * @param createmapping true if mapping should be added to created Model element
173         * @return created part
174         */
175        Part *createPart(ParamEntry *tab, std::vector<fH_StickHandle *> *children, Model *model, bool createmapping);
176
177        /**
178         * Creates new joint from current stick. Method should be called after
179         * creating parts with createPart method. Method sets properties of joint
180         * according to genotype values and connects both parts of stick.
181         * @param tab Param tab object for access to variables
182         * @param model pointer to model that is being developed in buildModel method
183         * @param createmapping true if mapping should be added to created Model element
184         * @return pointer to created joint or NULL if one or both of stick parts were not created
185         */
186        Joint *createJoint(ParamEntry *tab, Model *model, bool createmapping);
187
188        ~fH_StickHandle() {}
189};
190
191/**
192 * Class representing neuron handle of fH encoding. It has one property, called
193 * details.
194 */
195class fH_NeuronHandle : public fH_Handle
196{
197public:
198        Neuro *neuron; ///< pointer to created neuron from handle
199
200        fH_NeuronHandle(int dimensions, int begin, int end) :
201                fH_Handle(fHBodyType::NEURON, dimensions, begin, end)
202        {
203                neuron = NULL;
204        }
205
206        /**
207         * Calculates distance between current and given handle.
208         * Distance from StickHandle to NeuronHandle is designed only for neurons
209         * that need to be attached to joints and is computed as distance between
210         * average of first and second vector for each handle.
211         * Distance from current NeuronHandle to a given ConnectionHandle is used
212         * only for neurons that should act as inputs. Distance is calculated
213         * as Euclidean distance between second vector of NeuronHandle and first
214         * vector of ConnectionHandle. In order to calculate distance between
215         * connection and output neuron use distance function implemented in
216         * ConnectionHandle class.
217         * Distance between two NeuronHandles cannot be calculated
218         * @param right second handle to which distance is calculated
219         * @return Euclidean distance between two vectors
220         */
221        double distance(fH_Handle *right);
222
223        /**
224         * Calculate distance between neuron and given part.
225         * In this case distance between neuron and part is calculated as Euclidean
226         * distance between average of two vectors of NeuronHandle and average of
227         * second vector of given StickHandle and all first vectors of StickHandles
228         * that share this part with given StickHandle. First parameter determines
229         * if method should calculate distance for first or second part.
230         * @param right second handle to which distance is calculated
231         * @param first true if method should calculate distance between current neuron and first part of StickHandle, false if method should calculate distance between current neuron and second part
232         * @return Euclidean distance between two vectors
233         */
234        double distance(fH_StickHandle *right, bool first);
235        ~fH_NeuronHandle() {}
236};
237
238/**
239 * Class representing connection handle of fH encoding. It has one property, called
240 * weight.
241 */
242class fH_ConnectionHandle : public fH_Handle
243{
244public:
245
246        fH_ConnectionHandle(int dimensions, int begin, int end) :
247                fH_Handle(fHBodyType::CONNECTION, dimensions, begin, end) {}
248
249        /**
250         * This method can calculate distance only between current connection and
251         * NeuronHandle.
252         * Distance from current ConnectionHandle to a given NeuronHandle is used
253         * only for neurons that should act as outputs. Distance is calculated
254         * as Euclidean distance between second vector of ConnectionHandle and first
255         * vector of NeuronHandle. In order to calculate distance between
256         * input neuron and connection use distance function implemented in
257         * NeuronHandle class.
258         * @param right second handle to which distance is calculated
259         * @return Euclidean distance between two vectors
260         */
261        double distance(fH_Handle *right);
262
263        ~fH_ConnectionHandle() {}
264};
265
266/**
267 * Main class for parsing fH genotypes. It reads all genotype, prepares
268 * all objects required for parsing it and holds prepared handles for further
269 * development of a creature.
270 */
271class fH_Builder
272{
273private:
274        friend class ::Geno_fH; // Geno_fH class requires access to private fields of Builder for performing mutations and cross-over
275        int dimensions; ///<number of dimensions for this genotype
276        bool createmapping; ///<determines if mapping should be created or not
277
278        std::vector<fH_StickHandle*> sticks; ///<vector holding sticks of a genotype
279        std::vector<fH_NeuronHandle*> neurons; ///<vector holding neurons of a genotype
280        std::vector<fH_ConnectionHandle*> connections; ///<vector holding connections of a genotype
281
282        MutableParam stickmut; ///<object holding all generated properties for sticks in genotype
283        MutableParam neuronmut; ///<object holding all generated properties for neurons in genotype
284        MutableParam connectionmut; ///<object holding all generated properties for connections in genotype
285
286        ParamEntry *stickparamtab; ///< ParamTab generated from stickmut
287        ParamEntry *neuronparamtab; ///< ParamTab generated from neuronmut
288        ParamEntry *connectionparamtab; ///< ParamTab generated from connectionmut
289
290        std::vector<std::pair<fH_StickHandle *, fH_StickHandle *>> stickconnections; ///< this vector holds all connections determined by development of fH body in method buildBody
291        std::vector<int> sticksorder; ///< vector holds ids of StickHandles in 'sticks' vector in order of adding sticks to a body during buildBody
292
293        /**
294         * Processes single line of genotype. This line should begin with
295         * "j:", "n:" or "c:". Otherwise it will be considered as error.
296         * @param line line of a genotype
297         * @param linenumber number of line in genotype, required for error messages
298         * @return 0 if processing was successful, 1 if type of handle could not be determined or loading of properties failed
299         */
300        int processLine(SString line, int linenumber, int begin, int end);
301
302        /**
303         * Prepares ParamTab objects according to a given number of dimensions.
304         */
305        void prepareParams();
306
307        /**
308         * Connects floating sticks into consistent body. It does not create Model,
309         * it only fills 'stickconnections' with pairs of connected sticks and
310         * 'stickorder' with order of adding sticks to body.
311         */
312        void buildBody();
313
314        /**
315         * Creates neurons and neural connections between them, according to
316         * NeuronHandles and ConnectionHandles. Needs to be called after developing
317         * body with buildBody function and adding parts and joints to Model given
318         * in input parameter of method. Method connects neurons, attaches them
319         * to parts or joints and save mappings from genotype to model.
320         * @param model currently created model by Builder with parts and joints created by sticks in buildBody
321         * @param createmapping true if mapping should be added to created Model element
322         * @return 0 if brain was developed successfully, -1 if one of neurons had invalid class
323         */
324        int developBrain(Model *model, bool createmapping);
325
326        /**
327         * Calculates direction in which new joint should develop. When there
328         * is more than one joint coming out from current part, all parts with
329         * joints starting from the same point should be as far away from each
330         * other as possible. This problem can be defined as even distribution
331         * of points on a sphere. This method, called improved RSZ method offers
332         * non-iterative and fast calculation of such directions. Name of method
333         * comes from creators Rakhmanov, Saff and Zhou.
334         * Method works partially on spherical coordinates (r and theta is used).
335         * Z coordinate is from Cartesian coordinate system. Golden angle is used
336         * to "iterate" along spiral, while Z coordinate is used to move down the
337         * sphere.
338         * Method requires two parameters - count of all joints that share same
339         * part, and number of current joint to calculate.
340         *
341         * Full description of algorithm is in article:
342         * Mary K. Arthur, "Point Picking and Distributing on the Disc and Sphere",
343         * Army Research Laboratory, July 2015
344         *
345         * @param count number of joints that share same sphere
346         * @param number number of joint in array of joints
347         * @return direction where new joint should point
348         */
349        static Pt3D getNextDirection(int count, int number);
350
351        /** TODO move to the Orient class?
352         * Creates rotation matrix that aligns current vector to some expected
353         * vector. Method is used with RSZ method. When method calculates n+1
354         * evenly distributed points (that will act as ends of vectors representing
355         * n joints directions), 0 point is acting as direction of previous joint.
356         * In order to properly use directions of new joints they need to be
357         * multiplied by rotation matrix that aligns direction represented by
358         * 0 point to direction pointing from shared part to second part of parent
359         * joint.
360         * Let:
361         * - cross product of a and b
362         * \f[ v = a \times b \f]
363         * - sine of angle
364         * \f[ s = ||v|| \f]
365         * - cosine of angle
366         * \f[ c = a \cdot b \f]
367         * - skew-symmetric cross-product matrix of \f$ v \f$
368         * \f[
369         * [v]_\times =
370         * \begin{bmatrix}
371         * 0 & -v_3 & v_2 \\
372         * v_3 & 0 & -v_1 \\
373         * -v_2 & v_1 & 0
374         * \end{bmatrix}
375         * \f]
376         *
377         * Then rotation matrix is following:
378         * \f[ R = I + [v]_{\times} + [v]_{\times}^{2}\frac{1}{1+c} \f]
379         * @param currdir current direction of vector
380         * @param expecteddir where vector should point
381         * @return rotation matrix
382         */
383        static Orient getRotationMatrixToFitVector(Pt3D currdir, Pt3D expecteddir);
384
385public:
386        fH_Builder(int dimensions = 0, bool createmapping = true) : dimensions(dimensions),
387                createmapping(createmapping),
388                stickmut("Stick handle", "Properties"),
389                neuronmut("Neuron handle", "Properties"),
390                connectionmut("Connection handle", "Properties")
391        {
392                stickparamtab = neuronparamtab = connectionparamtab = NULL;
393                if (dimensions > 0)
394                {
395                        prepareParams();
396                }
397        }
398
399        ~fH_Builder();
400
401        /**
402         * Parses fH genotype, starting with line holding number of dimensions for
403         * this genotype. Secondly, it prepares ParamTabs for sticks, neurons and
404         * connections in order to parse following lines. During parsing method
405         * creates handle for each nonempty line and stores in vector matching
406         * its type.
407         * @param genotype fH genotype
408         * @return 0 if processing was successful, 1 if parsing of dimensions went wrong or one of genotype lines could not be parsed
409         */
410        int parseGenotype(const SString &genotype);
411
412        /**
413         * Removes neurons with invalid class names. This method is used in
414         * repairing the genotype. It should be called before building Model,
415         * otherwise Model will return failure and validation will show error.
416         * @return number of neurons removed from genotype
417         */
418        int removeNeuronsWithInvalidClasses();
419
420        /**
421         * Builds model for current fH genotype. Should be called after parseGenotype
422         * method.
423         * @param using_checkpoints true if checkpoints should be generated, false otherwise
424         */
425        Model* buildModel(bool using_checkpoints);
426
427        /**
428         * Converts Handle objects into strings with validated properties by
429         * Paramtab helper classes. To ensure toString method will return repaired
430         * genotype methods parseGenotype(...), remoeNeuronsWithInvalidClasses()
431         * and buildModel(...) should be called. If buildModel will return NULL
432         * then toString method will not return proper output.
433         * @return genotype held in Builder, with minor repairs of properties and removed neurons with invalid class names
434         */
435        SString toString();
436
437        /**
438         * Adds handle to proper handles vector.
439         * @param handle handle to add
440         */
441        void addHandle(fH_Handle *handle);
442
443        /**
444         * Checks if there is any stick in creature.
445         * @return true if there is at least one stick, false otherwise
446         */
447        bool sticksExist() { return sticks.size() > 0; }
448
449        /**
450         * Returns Param for a given handle type.
451         * @param type type of handle
452         * @return proper Param object
453         */
454        ParamEntry* getParamTab(fHBodyType type);
455};
456
457#endif //_FH_GENERAL_H_
Note: See TracBrowser for help on using the repository browser.