| 1 | // This file is a part of Framsticks SDK. http://www.framsticks.com/ |
|---|
| 2 | // Copyright (C) 1999-2020 Maciej Komosinski and Szymon Ulatowski. |
|---|
| 3 | // See LICENSE.txt for details. |
|---|
| 4 | |
|---|
| 5 | #ifndef _NEUROIMPL_H_ |
|---|
| 6 | #define _NEUROIMPL_H_ |
|---|
| 7 | |
|---|
| 8 | #include <frams/model/model.h> |
|---|
| 9 | #include <frams/param/param.h> |
|---|
| 10 | #include <common/log.h> |
|---|
| 11 | #ifdef NEURO_SIGNALS |
|---|
| 12 | #include <frams/simul/signals.h> |
|---|
| 13 | #endif |
|---|
| 14 | |
|---|
| 15 | class NeuroImpl; |
|---|
| 16 | extern ParamEntry neuroimpl_tab[]; |
|---|
| 17 | |
|---|
| 18 | class Creature; |
|---|
| 19 | class NeuroLibrary; |
|---|
| 20 | class NeuroFactory; |
|---|
| 21 | |
|---|
| 22 | class NeuroNetConfig |
|---|
| 23 | { |
|---|
| 24 | public: |
|---|
| 25 | NeuroNetConfig(NeuroFactory *fac); |
|---|
| 26 | |
|---|
| 27 | Param par; |
|---|
| 28 | double randominit; |
|---|
| 29 | double nnoise; |
|---|
| 30 | double touchrange; |
|---|
| 31 | |
|---|
| 32 | NeuroFactory *factory; |
|---|
| 33 | //static NeuroNetConfig& getGlobalConfig(); |
|---|
| 34 | }; |
|---|
| 35 | |
|---|
| 36 | #ifdef NEURO_SIGNALS |
|---|
| 37 | class NeuroSignals : public SignalSet |
|---|
| 38 | { |
|---|
| 39 | protected: |
|---|
| 40 | Creature *cr; |
|---|
| 41 | NeuroImpl *owner; |
|---|
| 42 | Creature *getCreature(); |
|---|
| 43 | public: |
|---|
| 44 | |
|---|
| 45 | NeuroSignals(NeuroImpl *n) :cr(0), owner(n) {} |
|---|
| 46 | |
|---|
| 47 | #define STATRICKCLASS NeuroSignals |
|---|
| 48 | PARAMPROCDEF(p_add); |
|---|
| 49 | PARAMPROCDEF(p_get); |
|---|
| 50 | PARAMGETDEF(size); |
|---|
| 51 | PARAMPROCDEF(p_receive); |
|---|
| 52 | PARAMPROCDEF(p_receiveSet); |
|---|
| 53 | PARAMPROCDEF(p_receiveFilter); |
|---|
| 54 | PARAMPROCDEF(p_receiveSingle); |
|---|
| 55 | #undef STATRICKCLASS |
|---|
| 56 | |
|---|
| 57 | static Param& getStaticParam(); |
|---|
| 58 | }; |
|---|
| 59 | #endif |
|---|
| 60 | |
|---|
| 61 | /// Neuro net implementation |
|---|
| 62 | class NeuroNetImpl |
|---|
| 63 | { |
|---|
| 64 | CallbackNode *cnode; |
|---|
| 65 | Model &mod; |
|---|
| 66 | SList neurons[4]; |
|---|
| 67 | NeuroNetConfig& config; |
|---|
| 68 | int isbuilt, errorcount; |
|---|
| 69 | STCALLBACKDEFC(NeuroNetImpl, destroyNN); |
|---|
| 70 | int minorder, maxorder; |
|---|
| 71 | |
|---|
| 72 | public: |
|---|
| 73 | #ifdef NEURO_SIGNALS |
|---|
| 74 | ChannelSpace *channels; |
|---|
| 75 | #endif |
|---|
| 76 | static int mytags_id; |
|---|
| 77 | static double getStateFromNeuro(Neuro *n); |
|---|
| 78 | int getErrorCount() { return errorcount; } |
|---|
| 79 | NeuroNetConfig &getConfig() { return config; } |
|---|
| 80 | NeuroNetImpl(Model& model, NeuroNetConfig& conf |
|---|
| 81 | #ifdef NEURO_SIGNALS |
|---|
| 82 | , ChannelSpace *ch = 0 |
|---|
| 83 | #endif |
|---|
| 84 | ); |
|---|
| 85 | ~NeuroNetImpl(); |
|---|
| 86 | void simulateNeuroNet(); |
|---|
| 87 | void simulateNeuroPhysics(); |
|---|
| 88 | |
|---|
| 89 | static NeuroImpl *getImpl(Neuro* n) { return (NeuroImpl*)n->userdata[mytags_id]; } |
|---|
| 90 | static void getLiveNeuroObject(Neuro *n, ExtValue *ret); |
|---|
| 91 | }; |
|---|
| 92 | |
|---|
| 93 | |
|---|
| 94 | /** |
|---|
| 95 | Neuro implementation - this object calculates the Neuron's state |
|---|
| 96 | (Neuro::state) in each simulation step. |
|---|
| 97 | |
|---|
| 98 | SUBCLASSING TUTORIAL |
|---|
| 99 | ==================== |
|---|
| 100 | |
|---|
| 101 | 1.Derive your custom neuron from NeuroImpl class. The name must be prefixed with NI_ |
|---|
| 102 | |
|---|
| 103 | class NI_MyNeuron: public NeuroImpl |
|---|
| 104 | { ... }; |
|---|
| 105 | |
|---|
| 106 | 2.Public parameters |
|---|
| 107 | Create any number of public fields, they will be adjustable from the genotype level. |
|---|
| 108 | 3 datatypes are supported: long, double and SString |
|---|
| 109 | |
|---|
| 110 | public: |
|---|
| 111 | paInt intParameter; |
|---|
| 112 | double fpParameter; |
|---|
| 113 | SString txtParameter; |
|---|
| 114 | |
|---|
| 115 | |
|---|
| 116 | 3.Required method: "instantiator". |
|---|
| 117 | It is always the same, just create a new instance of your neuron. |
|---|
| 118 | public: |
|---|
| 119 | NeuroImpl* makeNew() { return new NI_MyNeuron(); }; |
|---|
| 120 | |
|---|
| 121 | |
|---|
| 122 | 4.Required method: default constructor |
|---|
| 123 | Set the "paramentries" variable if you need public parameters in your neuron. |
|---|
| 124 | NI_..._tab is created automatically and should be declared as: extern ParamEntry NI_..._tab[]; |
|---|
| 125 | At this stage the parameter values are not yet available. |
|---|
| 126 | |
|---|
| 127 | public: |
|---|
| 128 | NI_MyNeuron() // no parameters! |
|---|
| 129 | { |
|---|
| 130 | paramentries=NI_MyNeuron_tab; |
|---|
| 131 | // you add here: some general initialization |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | |
|---|
| 135 | 5.Optional method: initialization |
|---|
| 136 | This method is called once before the neuron is actually used in the simulation. |
|---|
| 137 | The parameter values are already initialized (according to the genotype) and the neuron is bound to the creature (i.e. this->neuro is valid). |
|---|
| 138 | Return 0 if the neuron cannot be initialized. |
|---|
| 139 | |
|---|
| 140 | int lateinit() |
|---|
| 141 | { |
|---|
| 142 | // you add here: initialization using full neuron context |
|---|
| 143 | // example: if (!neuro->joint) return 0; //this neuron must be attached to joint |
|---|
| 144 | return 1;//OK |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | |
|---|
| 148 | 6.Required method: simulation step |
|---|
| 149 | If it has output: calculate the next neuron state and call setState() |
|---|
| 150 | If it is an effector: do anything else |
|---|
| 151 | |
|---|
| 152 | void go() |
|---|
| 153 | { |
|---|
| 154 | // you add here: things called every simulation step |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | Note: You can make your neuron fire before or after "regular" neurons by changing its "simorder" property (during initialization). The default value is 1, whereas receptors have simorder=0 and effectors have simorder=2. |
|---|
| 158 | |
|---|
| 159 | |
|---|
| 160 | 7.Neuron definition |
|---|
| 161 | In order to incorporate the new neuron into Framsticks you need to provide some additional information (to be added to "f0.def" file). |
|---|
| 162 | |
|---|
| 163 | NEUROCLASS(MyNeuron,MN,This is the name,`Neuron description',-1,1,0) |
|---|
| 164 | NEUROPROP(int,0,0,name of the int,d,,,,intParameter) |
|---|
| 165 | NEUROPROP(fp,0,0,name of the floating point,f,,,,fpParameter) |
|---|
| 166 | NEUROPROP(txt,0,0,name of the text,s,,,,txtParameter) |
|---|
| 167 | ENDNEUROCLASS |
|---|
| 168 | |
|---|
| 169 | NEUROCLASS: |
|---|
| 170 | - MyNeuron: neuron class name (without the NI_ prefix) |
|---|
| 171 | - MN: neuron symbol (used in genotypes) |
|---|
| 172 | - full name and description |
|---|
| 173 | - -1: preferred number of inputs (special case: -1=any) |
|---|
| 174 | - 1: provides output: 1=yes/0=no |
|---|
| 175 | - 0: preferred location: 0=none, 1=part, 2=joint |
|---|
| 176 | |
|---|
| 177 | NEUROPROP: |
|---|
| 178 | - int/fp/txt: parameter names as visible in genotypes and scripting |
|---|
| 179 | - "name of the ...": descriptive name |
|---|
| 180 | - d/f/s: type (int/floating point/string) |
|---|
| 181 | - intParameter/fpParameter/txtParameter: C++ field names |
|---|
| 182 | |
|---|
| 183 | |
|---|
| 184 | */ |
|---|
| 185 | class NeuroImpl |
|---|
| 186 | { |
|---|
| 187 | protected: |
|---|
| 188 | int simorder; |
|---|
| 189 | int channels; |
|---|
| 190 | SListTempl<double> chstate; |
|---|
| 191 | SListTempl<double> chnewstate; |
|---|
| 192 | Param *fields_param; |
|---|
| 193 | ExtObject *fields_object; |
|---|
| 194 | public: |
|---|
| 195 | static const int ENDDRAWING; |
|---|
| 196 | static const int MAXDRAWINGXY; |
|---|
| 197 | |
|---|
| 198 | enum NeuroImplStats { BeforeInit = 0, InitError = 1, InitOk = 2 }; |
|---|
| 199 | NeuroImplStats status; |
|---|
| 200 | /** originating neuron object (from the model) */ |
|---|
| 201 | Neuro *neuro; |
|---|
| 202 | NeuroClass *neuroclass; |
|---|
| 203 | /** don't access directly */ |
|---|
| 204 | double newstate; |
|---|
| 205 | NeuroNetImpl *owner; |
|---|
| 206 | /** will be used by readParam() method, if not null */ |
|---|
| 207 | ParamEntry *paramentries; // no extra properties if ==0 |
|---|
| 208 | |
|---|
| 209 | #ifdef NEURO_SIGNALS |
|---|
| 210 | NeuroSignals sigs; |
|---|
| 211 | ExtObject sigs_obj; |
|---|
| 212 | #endif |
|---|
| 213 | |
|---|
| 214 | /** "virtual constructor" - NeuroFactory uses this method to create the proper implementation object. |
|---|
| 215 | subclasses must return new object here. */ |
|---|
| 216 | virtual NeuroImpl* makeNew() { return 0; } // |
|---|
| 217 | /** read additional properties from "moredata" field of the originating Neuro */ |
|---|
| 218 | void readParam(); |
|---|
| 219 | /** called when all other neuro objects were already created and "moredata" transferred to |
|---|
| 220 | object fields. |
|---|
| 221 | useful for initialization that cannot be performed in the constructor. |
|---|
| 222 | @return 1=ok 0=failure |
|---|
| 223 | */ |
|---|
| 224 | virtual int lateinit() { return 1; } |
|---|
| 225 | /** calculate 'newstate - implementation dependent */ |
|---|
| 226 | virtual void go() {} |
|---|
| 227 | /** for neurons doing some physical actions (called each simulation step when nnspeed!=1.0) */ |
|---|
| 228 | virtual void goPhysics() {} |
|---|
| 229 | |
|---|
| 230 | int getSimOrder() { return simorder; } |
|---|
| 231 | virtual int getNeedPhysics() { return 0; } |
|---|
| 232 | |
|---|
| 233 | void setChannelCount(int c); |
|---|
| 234 | int getChannelCount() { return channels; } |
|---|
| 235 | |
|---|
| 236 | int getInputCount() { return neuro->getInputCount(); } |
|---|
| 237 | int getInputChannelCount(int i); |
|---|
| 238 | double getInputState(int i, int channel = 0); |
|---|
| 239 | double getWeightedInputState(int i, int channel = 0); |
|---|
| 240 | double getInputSum(int startwith = 0); |
|---|
| 241 | double getWeightedInputSum(int startwith = 0); |
|---|
| 242 | double getInputWeight(int i) { return neuro->getInputWeight(i); } |
|---|
| 243 | void setState(double st, int channel); |
|---|
| 244 | void setState(double st) { validateNeuroState(st); newstate = st; } |
|---|
| 245 | double getState(int channel); |
|---|
| 246 | double getState() { return neuro->state; } |
|---|
| 247 | |
|---|
| 248 | virtual int getDrawingCount() { return 0; } |
|---|
| 249 | virtual int* getDrawing(int i) { return 0; } |
|---|
| 250 | |
|---|
| 251 | /** is this implementation current? script neurons retain their original implementation when reloading *.neuro */ |
|---|
| 252 | virtual bool isCurrent() { return true; } |
|---|
| 253 | |
|---|
| 254 | void commit(); |
|---|
| 255 | void validateNeuroState(double& st) { if (st <= -1e10) st = -1e10; else if (st > 1e10) st = 1e10; } |
|---|
| 256 | |
|---|
| 257 | NeuroImpl() :simorder(1), channels(1), fields_param(0), fields_object(0), status(BeforeInit), neuro(0), newstate(0), owner(0), paramentries(0) |
|---|
| 258 | #ifdef NEURO_SIGNALS |
|---|
| 259 | , sigs(this), sigs_obj(&NeuroSignals::getStaticParam(), &sigs) |
|---|
| 260 | #endif |
|---|
| 261 | {} |
|---|
| 262 | virtual ~NeuroImpl(); |
|---|
| 263 | virtual void createFieldsObject(); |
|---|
| 264 | |
|---|
| 265 | /** usually == "newstate" but will obey the "hold state" */ |
|---|
| 266 | double getNewState(int channel = 0); |
|---|
| 267 | |
|---|
| 268 | /** don't use! */ |
|---|
| 269 | void setCurrentState(double st, int channel = 0); |
|---|
| 270 | |
|---|
| 271 | bool getPosition(Pt3D &pos); |
|---|
| 272 | Creature* getCreature(); |
|---|
| 273 | |
|---|
| 274 | virtual Pt3D getRelativePosition() { return Pt3D_0; } |
|---|
| 275 | virtual Orient getRelativeOrientation() { return Orient_1; } |
|---|
| 276 | |
|---|
| 277 | #define STATRICKCLASS NeuroImpl |
|---|
| 278 | PARAMGETDEF(count) { arg1->setInt(getInputCount()); } |
|---|
| 279 | PARAMPROCDEF(p_get) { arg2->setDouble(getInputState(arg1->getInt())); } |
|---|
| 280 | PARAMPROCDEF(p_getweight) { arg2->setDouble(getInputWeight(arg1->getInt())); } |
|---|
| 281 | PARAMPROCDEF(p_getw) { arg2->setDouble(getWeightedInputState(arg1->getInt())); } |
|---|
| 282 | PARAMPROCDEF(p_getsum) { arg2->setDouble(getInputSum(arg1->getInt())); } |
|---|
| 283 | PARAMPROCDEF(p_getwsum) { arg2->setDouble(getWeightedInputSum(arg1->getInt())); } |
|---|
| 284 | PARAMGETDEF(sum) { arg1->setDouble(getInputSum(0)); } |
|---|
| 285 | PARAMGETDEF(wsum) { arg1->setDouble(getWeightedInputSum(0)); } |
|---|
| 286 | PARAMPROCDEF(p_getchancount) { arg2->setInt(getInputChannelCount(arg1->getInt())); } |
|---|
| 287 | PARAMPROCDEF(p_getchan) { arg2->setDouble(getInputState(arg1[1].getInt(), arg1[0].getInt())); } |
|---|
| 288 | PARAMPROCDEF(p_getwchan) { arg2->setDouble(getWeightedInputState(arg1[1].getInt(), arg1[0].getInt())); } |
|---|
| 289 | PARAMGETDEF(state) { arg1->setDouble(getState()); } |
|---|
| 290 | PARAMSETDEF(state) { setState(arg1->getDouble()); return 0; } |
|---|
| 291 | PARAMGETDEF(cstate) { arg1->setDouble(neuro->state); } |
|---|
| 292 | PARAMSETDEF(cstate) { setCurrentState(arg1->getDouble()); return 0; } |
|---|
| 293 | PARAMGETDEF(hold) { arg1->setInt((neuro->flags & (Neuro::HoldState)) ? 1 : 0); } |
|---|
| 294 | PARAMSETDEF(hold) { neuro->flags = (neuro->flags & ~Neuro::HoldState) | (arg1->getInt() ? Neuro::HoldState : 0); return 0; } |
|---|
| 295 | PARAMGETDEF(channels) { arg1->setInt(getChannelCount()); } |
|---|
| 296 | PARAMSETDEF(channels) { setChannelCount(arg1->getInt()); return 0; } |
|---|
| 297 | PARAMPROCDEF(p_getstate) { arg2->setDouble(getState(arg1->getInt())); } |
|---|
| 298 | PARAMPROCDEF(p_setstate) { setState(arg1[0].getDouble(), arg1[1].getInt()); } |
|---|
| 299 | PARAMPROCDEF(p_setcstate) { setCurrentState(arg1[0].getDouble(), arg1[1].getInt()); } |
|---|
| 300 | PARAMGETDEF(creature); |
|---|
| 301 | PARAMGETDEF(part); |
|---|
| 302 | PARAMGETDEF(joint); |
|---|
| 303 | PARAMGETDEF(position_x); |
|---|
| 304 | PARAMGETDEF(position_y); |
|---|
| 305 | PARAMGETDEF(position_z); |
|---|
| 306 | PARAMGETDEF(relative_pos); |
|---|
| 307 | PARAMGETDEF(relative_orient); |
|---|
| 308 | PARAMGETDEF(fields); |
|---|
| 309 | PARAMGETDEF(neurodef); |
|---|
| 310 | PARAMGETDEF(classObject); |
|---|
| 311 | #undef STATRICKCLASS |
|---|
| 312 | |
|---|
| 313 | static Param& getStaticParam(); |
|---|
| 314 | }; |
|---|
| 315 | |
|---|
| 316 | #endif |
|---|