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

Last change on this file since 780 was 780, checked in by Maciej Komosinski, 6 years ago

Added sources for genetic encodings fB, fH, fL

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