source: cpp/gdk/neuroimpl.cpp @ 97

Last change on this file since 97 was 81, checked in by Maciej Komosinski, 12 years ago

improved parsing of properties (e.g. in f0 genotypes)

  • Property svn:eol-style set to native
File size: 16.7 KB
Line 
1// This file is a part of the Framsticks GDK library.
2// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
3// Refer to http://www.framsticks.com/ for further information.
4
5#include "neuroimpl.h"
6#include "neurofactory.h"
7#include "rndutil.h"
8#include "nonstd_math.h"
9#ifndef GDK_WITHOUT_FRAMS
10#include "creature.h"
11#include "creatmechobj.h"
12#include "livegroups.h"
13#include "simul.h"
14#endif
15
16const int NeuroImpl::ENDDRAWING=-9999;
17const int NeuroImpl::MAXDRAWINGXY=0xffff;
18
19int NeuroNetImpl::mytags_id=0;
20
21/////////////////////////////////////////////////////////
22
23#define FIELDSTRUCT NeuroNetConfig
24static ParamEntry nncfg_paramtab[]=
25{
26{"Creature: Neurons",1,3,"nnsim",},
27{"randinit",1,0,"Random initialization","f 0 10 0.01",FIELD(randominit),"Allowed range for initializing all neuron states with uniform distribution random numbers and zero mean. Set to 0 for deterministic initialization."},
28{"nnoise",1,0,"Noise","f 0 1 0",FIELD(nnoise),"Gaussian neural noise: a random value is added to each neural output in each simulation step. Set standard deviation here to add random noise, or 0 for deterministic simulation."},
29{"touchrange",1,0,"T receptor range","f 0 100 1",FIELD(touchrange),},
30{0,0,0,},
31};
32#undef FIELDSTRUCT
33
34NeuroNetConfig::NeuroNetConfig()
35        :par(nncfg_paramtab,this),
36         randominit(0.01),
37         nnoise(0),
38         touchrange(1)
39{}
40
41NeuroNetConfig& NeuroNetConfig::getGlobalConfig()
42{
43static NeuroNetConfig globalconfig;
44return globalconfig;
45}
46
47/////////////////////////////////////////////////////////////////
48
49NeuroNetImpl::NeuroNetImpl(Model& model, NeuroNetConfig& conf
50#ifdef NEURO_SIGNALS
51, ChannelSpace *ch
52#endif
53)
54        :mod(model),config(conf),
55         isbuilt(1),errorcount(0)
56#ifdef NEURO_SIGNALS
57,channels(ch)
58#endif
59{
60if (!mytags_id) mytags_id=mod.userdata.newID();
61
62Neuro *n;
63NeuroImpl *ni;
64Joint *j;
65int i;
66DB(printf("makeNeuroNet(%p)\n",&mod));
67
68minorder=3; maxorder=0;
69errorcount=0;
70
71for (i=0;j=mod.getJoint(i);i++)
72        j->flags&=~(4+8); // todo: !!!neuroitems shouldn't use model fields!!!
73
74for (i=0;n=mod.getNeuro(i);i++)
75        {
76        ni=NeuroFactory::createNeuroImpl(n);
77        n->userdata[mytags_id]=ni;
78        if (!ni) { errorcount++;
79                FMprintf("NeuroNetImpl","create",FMLV_WARN,"neuron #%d (%s) implementation not available",
80                         i,(const char*)n->getClassName());
81                continue; } // implementation not available?!
82        ni->owner=this;
83        ni->neuro=n;
84        ni->readParam();
85        }
86
87for (i=0;n=mod.getNeuro(i);i++)
88        {
89        n->state+=(rnd01-0.5)*config.randominit;
90        ni=(NeuroImpl*)n->userdata[mytags_id];
91        if (!ni) continue;
92        if (!ni->lateinit())
93                { ni->status=NeuroImpl::InitError; errorcount++;
94                FMprintf("NeuroNetImpl","create",FMLV_WARN,"neuron #%d (%s) initialization failed",
95                         i,(const char*)n->getClassName());
96                continue; }
97        ni->status=NeuroImpl::InitOk;
98        int order=ni->getSimOrder();
99        if (order<0) order=0; else if (order>2) order=2;
100        if (order<minorder) minorder=order;
101        if (order>maxorder) maxorder=order;
102        neurons[order]+=ni;
103        if (ni->getNeedPhysics())
104                neurons[3]+=ni;
105        }
106cnode=mod.delmodel_list.add(STATRICKCALLBACK(this,&NeuroNetImpl::destroyNN,0));
107}
108
109void NeuroNetImpl::destroyNN(CALLBACKARGS)
110{
111if (!isbuilt) return;
112DB(printf("destroyNeuroNet(%p)\n",&mod));
113NeuroImpl *ni;
114Neuro *n;
115for (int i=0;n=mod.getNeuro(i);i++)
116        {
117        ni=(NeuroImpl*)n->userdata[mytags_id];
118        delete ni;
119        n->userdata[mytags_id]=0;
120        }
121mod.delmodel_list.remove(cnode);
122isbuilt=0; errorcount=0;
123delete this;
124}
125
126NeuroNetImpl::~NeuroNetImpl()
127{
128destroyNN(0,0);
129}
130
131void NeuroNetImpl::simulateNeuroNet()
132{
133NeuroImpl *ni;
134for (int order=minorder;order<=maxorder;order++)
135        {
136        int i;
137        SList &nlist=neurons[order];
138        for (i=0;ni=(NeuroImpl*)nlist(i);i++)
139                ni->go();
140        for (i=0;ni=(NeuroImpl*)nlist(i);i++)
141                ni->commit();
142        }
143}
144
145void NeuroNetImpl::simulateNeuroPhysics()
146{
147NeuroImpl *ni;
148int i;
149SList &nlist=neurons[3];
150for (i=0;ni=(NeuroImpl*)nlist(i);i++)
151        ni->goPhysics();
152}
153
154///////////////////////////////////////////////
155
156void NeuroImpl::setChannelCount(int c)
157{
158if (c<1) c=1;
159if (c==channels) return;
160if (c<channels) {channels=c; chstate.trim(c-1); chnewstate.trim(c-1); return;}
161double s=getState(channels-1);
162chnewstate.setSize(c-1);
163chstate.setSize(c-1);
164for(int i=channels;i<c;i++)
165        {chstate(i-1)=s; chnewstate(i-1)=s;}
166channels=c;
167}
168
169void NeuroImpl::setState(double st,int channel)
170{
171validateNeuroState(st);
172if (channel>=channels) channel=channels-1;
173if (channel<=0) {newstate=st;return;}
174chnewstate(channel-1)=st;
175}
176
177void NeuroImpl::setCurrentState(double st,int channel)
178{
179validateNeuroState(st);
180if (channel>=channels) channel=channels-1;
181if (channel<=0) {neuro->state=st; return;}
182chstate(channel-1)=st;
183}
184
185double NeuroImpl::getNewState(int channel)
186{
187if (neuro->flags&Neuro::HoldState) return getState(channel);
188if (channel>=channels) channel=channels-1;
189if (channel<=0) {return newstate;}
190return chnewstate(channel-1);
191}
192
193double NeuroImpl::getState(int channel)
194{
195if (channel>=channels) channel=channels-1;
196if (channel<=0) return neuro->state;
197return chstate(channel-1);
198}
199
200void NeuroImpl::commit()
201{
202if (!(neuro->flags&Neuro::HoldState))
203        {
204        if (channels>1)
205                chstate=chnewstate;
206        neuro->state=newstate;
207        if (NeuroNetConfig::getGlobalConfig().nnoise>0.0)
208                {
209                neuro->state+=RndGen.GaussStd()*NeuroNetConfig::getGlobalConfig().nnoise;
210                if (channels>1)
211                        for(int i=0;i<chstate.size();i++)
212                                chstate(0)+=RndGen.GaussStd()*NeuroNetConfig::getGlobalConfig().nnoise;
213                }
214        }
215}
216
217int NeuroImpl::getInputChannelCount(int i)
218{
219if ((i<0)||(i >= neuro->getInputCount())) return 1;
220Neuro *nu=neuro->getInput(i);
221NeuroImpl *ni=NeuroNetImpl::getImpl(nu);
222if (!ni) return 1;
223return ni->channels;
224}
225
226double NeuroImpl::getInputState(int i,int channel)
227{
228if ((i<0)||(i >= neuro->getInputCount())) return 0;
229Neuro *nu=neuro->getInput(i);
230if (channel<=0) return nu->state;
231NeuroImpl *ni=NeuroNetImpl::getImpl(nu);
232if (!ni) return nu->state;
233if (channel>=ni->channels) channel=ni->channels-1;
234if (!channel) return nu->state;
235return ni->chstate(channel-1);
236}
237
238double NeuroImpl::getWeightedInputState(int i, int channel)
239{
240if ((i<0)||(i >= neuro->getInputCount())) return 0;
241float w;
242Neuro *nu=neuro->getInput(i,w);
243if (channel<=0) return nu->state * w;
244NeuroImpl *ni=NeuroNetImpl::getImpl(nu);
245if (!ni) return nu->state * w;
246if (channel>=ni->channels) channel=ni->channels-1;
247if (!channel) return w * nu->state;
248return w * ni->chstate(channel-1);
249}
250
251double NeuroImpl::getInputSum(int startwith)
252{
253if (startwith<0) return 0;
254Neuro *inp;
255double sum=0.0;
256while(inp=neuro->getInput(startwith++))
257        sum+=inp->state;
258return sum;
259}
260
261double NeuroImpl::getWeightedInputSum(int startwith)
262{
263if (startwith<0) return 0;
264Neuro *inp;
265double sum=0.0;
266float w;
267while(inp=neuro->getInput(startwith++,w))
268        sum+=inp->state*w;
269return sum;
270}
271
272void NeuroImpl::readParam()
273{
274static Param par;
275if (!paramentries) return;
276par.setParamTab(paramentries);
277par.select(this);
278par.setDefault();
279int zero=0;
280par.load2(neuro->getClassParams(),zero);
281}
282
283Param& NeuroImpl::getStaticParam()
284{
285static Param p(neuroimpl_tab,0,"Neuro");
286return p;
287}
288
289/////////////////////////////
290
291#ifdef NEURO_SIGNALS
292#define NEUROIMPL_SIGNAL_PROPS 1
293#else
294#define NEUROIMPL_SIGNAL_PROPS 0
295#endif
296
297#define FIELDSTRUCT NeuroImpl
298ParamEntry neuroimpl_tab[]=
299{
300{"Neuro",1,27+NEUROIMPL_SIGNAL_PROPS,"Neuro","Live Neuron object."},
301
302{"getInputState",0,0,"Get input signal","p f(d input)",PROCEDURE(p_get),},
303{"getInputWeight",0,0,"Get input weight","p f(d input)",PROCEDURE(p_getweight),},
304{"getWeightedInputState",0,0,"Get weighted input signal","p f(d input)",PROCEDURE(p_getw),},
305{"getInputSum",0,0,"Get signal sum","p f(d input)",PROCEDURE(p_getsum),},
306{"getWeightedInputSum",0,0,"Get weighted signal sum","p f(d input)",PROCEDURE(p_getwsum),"Uses any number of inputs starting with the specified input. getWeightedInputSum(0)=weightedInputSum"},
307{"getInputCount",0,0,"Get input count","d",GETONLY(count),},
308{"inputSum",0,0,"Full signal sum","f",GETONLY(sum),},
309{"weightedInputSum",0,0,"Full weighted signal sum","f",GETONLY(wsum),},
310{"getInputChannelCount",0,0,"Get channel count for input","p d(d input)",PROCEDURE(p_getchancount),},
311{"getInputStateChannel",0,0,"Get input signal from channel","p f(d input,d channel)",PROCEDURE(p_getchan),},
312{"getWeightedInputStateChannel",0,0,"Get weighted input signal from channel","p f(d input,d channel)",PROCEDURE(p_getwchan),},
313{"state",0,0,"Neuron state (channel 0)","f",GETSET(state),"When read, returns the current neuron state.\nWhen written, sets the 'internal' neuron state that will become current in the next step.\nTypically you should use this field, and not currState."},
314{"channelCount",0,0,"Number of output channels","d",GETSET(channels),},
315{"getStateChannel",0,0,"Get state for channel","p f(d channel)",PROCEDURE(p_getstate),},
316{"setStateChannel",0,0,"Set state for channel","p(d channel,f value)",PROCEDURE(p_setstate),},
317{"hold",0,0,"Hold state","d 0 1",GETSET(hold),"\"Holding\" means keeping the neuron state as is, blocking the regular neuron operation. This is useful when your script needs to inject some control signals into the NN. Without \"holding\", live neurons would be constantly overwriting your changes, and the rest of the NN could see inconsistent states, depending on the connections. Setting hold=1 ensures the neuron state will be only set by you, and not by the neuron. The enforced signal value can be set using Neuro.currState before or after setting hold=1. Set hold=0 to resume normal operation.",},
318{"currState",0,0,"Current neuron state (channel 0)","f",GETSET(cstate),"When read, it behaves just like the 'state' field.\nWhen written, changes the current neuron state immediately, which disturbs the regular synchronous NN operation.\nThis feature should only be used while controlling the neuron 'from outside' (like a neuro probe) and not in the neuron definition. See also: Neuro.hold",},
319{"setCurrStateChannel",0,0,"Set current neuron state for channel","p(d channel,f value)",PROCEDURE(p_setcstate),"Analogous to \"currState\"."},
320{"position_x",0,0,"Position x","f",GETONLY(position_x),},
321{"position_y",0,0,"Position y","f",GETONLY(position_y),},
322{"position_z",0,0,"Position z","f",GETONLY(position_z),},
323{"creature",0,0,"Gets owner creature","o Creature",GETONLY(creature),},
324{"part",0,0,"The Part object where this neuron is located","o MechPart",GETONLY(part),},
325{"joint",0,0,"The Joint object where this neuron is located","o MechJoint",GETONLY(joint),},
326{"fields",0,0,"Custom neuron fields","o Fields",GETONLY(fields),
327"Neurons can have different fields depending on their class. Script neurons have their fields defined using the \"prop:\" syntax. If you develop a custom neuron script you should use the Fields object for accessing your own neuron fields. The Neuro.fields property is meant for accessing the neuron fields from the outside script.\n"
328"Examples:\n"
329"var c=Populations.createFromString(\"X[N]\");\n"
330"Simulator.print(\"standard neuron inertia=\"+c.getNeuro(0).fields.in);\n"
331"c=Populations.createFromString(\"X[Nn,e:0.1]\");\n"
332"Simulator.print(\"noisy neuron error rate=\"+c.getNeuro(0).fields.e);\n"
333"\n"
334"The Interface object can be used to discover which fields are available for a certain neuron object:\n"
335"c=Populations.createFromString(\"X[N]\");\n"
336"var iobj=Interface.makeFrom(c.getNeuro(0).fields);\n"
337"var i;\n"
338"for(i=0;i<iobj.properties;i++)\n"
339" Simulator.print(iobj.getId(i)+\" (\"+iobj.getName(i)+\")\");",},
340{"def",0,0,"Neuron definition from which this live neuron was built","o NeuroDef",GETONLY(neurodef),},
341{"classObject",0,0,"Neuron class for this neuron","o NeuroClass",GETONLY(classObject),},
342#ifdef NEURO_SIGNALS
343{"signals",0,PARAM_READONLY,"Signals","o NeuroSignals",FIELD(sigs_obj),},
344#endif
345
346{0,0,0,},
347};
348#undef FIELDSTRUCT
349
350#ifdef NEURO_SIGNALS
351ParamEntry neurosignals_paramtab[]=
352 {
353{"NeuroSignals",1,8,"NeuroSignals","Signals attached to a neuron.\nSee also: Signal, WorldSignals, CreatureSignals.\nscripts/light.neuro and scripts/seelight.neuro are simple custom neuron examples demonstrating how to send/receive signals between creatures.",},
354
355#define FIELDSTRUCT NeuroSignals
356SIGNPAR_ADD(""),
357SIGNPAR_RECEIVE(""),
358SIGNPAR_RECEIVESET(""),
359SIGNPAR_RECEIVEFILTER(""),
360SIGNPAR_RECEIVESINGLE(""),
361#undef FIELDSTRUCT
362
363#define FIELDSTRUCT SignalSet
364SIGNSETPAR_GET,
365SIGNSETPAR_SIZE,
366SIGNSETPAR_CLEAR,
367#undef FIELDSTRUCT
368{0,0,0,},
369 };
370
371Param& NeuroSignals::getStaticParam()
372{
373static Param p(neurosignals_paramtab,0);
374return p;
375}
376#endif
377
378#ifdef NEURO_SIGNALS
379class NeuroSigSource: public SigSource
380{
381  protected:
382NeuroImpl* owner;
383  public:
384NeuroSigSource(NeuroImpl *n,Creature *c):SigSource(0,c),owner(n) {}
385bool update();
386};
387
388bool NeuroSigSource::update()
389{
390Pt3D p;
391if (owner->getPosition(p))
392        {
393        setLocation(p);
394        return true;
395        }
396return false;
397}
398
399Creature *NeuroSignals::getCreature()
400{
401if (!cr)
402        {
403        cr=owner->getCreature();
404        }
405return cr;
406}
407
408void NeuroSignals::p_add(PARAMPROCARGS)
409{
410SigSource *s=new NeuroSigSource(owner,getCreature());
411if (owner->owner->channels)
412        {
413        SigChannel *ch=owner->owner->channels->getChannel(args->getString(),true);
414        ch->addSource(s);
415        }
416else
417        SigChannel::dummy_channel.addSource(s);
418sigs+=s;
419s->setupObject(ret);
420}
421
422void NeuroSignals::p_receive(PARAMPROCARGS)
423{
424SigChannel *ch; Pt3D p;
425if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args->getString(),false)) && owner->getPosition(p))
426        ret->setDouble(ch->receive(&p,getCreature()));
427else
428        ret->setDouble(0);
429}
430
431void NeuroSignals::p_receiveFilter(PARAMPROCARGS)
432{
433SigChannel *ch; Pt3D p;
434if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args[3].getString(),false)) && owner->getPosition(p))
435        ret->setDouble(ch->receive(&p,getCreature(),args[2].getDouble(),args[1].getDouble(),args[0].getDouble()));
436else
437        ret->setDouble(0);
438}
439
440void NeuroSignals::p_receiveSet(PARAMPROCARGS)
441{
442SigChannel *ch; Pt3D p;
443SigVector *vec=new SigVector();
444if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args[1].getString(),false)) && owner->getPosition(p))
445        ch->receiveSet(vec,&p,getCreature(),args[0].getDouble());
446ret->setObject(vec->makeObject());
447}
448
449void NeuroSignals::p_receiveSingle(PARAMPROCARGS)
450{
451SigChannel *ch; Pt3D p;
452if (owner->owner->channels && (ch=owner->owner->channels->getChannel(args[1].getString(),false)) && owner->getPosition(p))
453        {
454        SigSource *src=ch->receiveSingle(&p,getCreature(),args[0].getDouble(),0,1e99);
455        if (src)
456                {
457                src->setupObject(ret);
458                return;
459                }
460        }
461ret->setEmpty();
462}
463#endif
464
465extern ParamEntry creature_paramtab[];
466static Param creature_param(creature_paramtab,0);
467
468Creature* NeuroImpl::getCreature()
469{
470#ifndef GDK_WITHOUT_FRAMS
471CreatMechObject *cmo=(CreatMechObject *)neuro->owner->userdata[CreatMechObject::modeltags_id];
472return cmo->creature;
473#else
474return 0;
475#endif
476}
477
478void NeuroImpl::get_creature(ExtValue *ret)
479{
480#ifndef GDK_WITHOUT_FRAMS
481ret->setObject(ExtObject(&creature_param,getCreature()));
482#endif
483}
484
485void NeuroImpl::get_part(ExtValue *ret)
486{
487#ifndef GDK_WITHOUT_FRAMS
488Part *pa;
489if (pa=neuro->getPart())
490        ret->setObject(ExtObject(&MechPart::getStaticParam(),((MechPart *)pa->userdata[CreatMechObject::modeltags_id])));
491else
492        ret->setEmpty();
493#endif
494}
495
496void NeuroImpl::get_joint(ExtValue *ret)
497{
498#ifndef GDK_WITHOUT_FRAMS
499Joint *jo;
500if (jo=neuro->getJoint())
501        ret->setObject(ExtObject(&MechJoint::getStaticParam(),((MechJoint*)jo->userdata[CreatMechObject::modeltags_id])));
502else
503        ret->setEmpty();
504#endif
505}
506
507bool NeuroImpl::getPosition(Pt3D &pos)
508{
509#ifndef GDK_WITHOUT_FRAMS
510Part *pa; Joint *jo;
511if (pa=neuro->getPart())
512        {pos=((MechPart *)pa->userdata[CreatMechObject::modeltags_id])->p; return true;}
513if (jo=neuro->getJoint())
514        {
515        if (neuro->getClass()->getVisualHints() & NeuroClass::AtFirstPart)
516                pos=((MechPart*)jo->part1->userdata[CreatMechObject::modeltags_id])->p;
517        else if (neuro->getClass()->getVisualHints() & NeuroClass::AtSecondPart)
518                pos=((MechPart*)jo->part2->userdata[CreatMechObject::modeltags_id])->p;
519        else pos=(((MechPart*)jo->part1->userdata[CreatMechObject::modeltags_id])->p
520                   +((MechPart*)jo->part2->userdata[CreatMechObject::modeltags_id])->p)/2;
521        return true;
522        }
523#endif
524return false;
525}
526
527void NeuroImpl::get_position_x(ExtValue *ret)
528{Pt3D pos; if (getPosition(pos)) ret->setDouble(pos.x); else ret->setEmpty();}
529void NeuroImpl::get_position_y(ExtValue *ret)
530{Pt3D pos; if (getPosition(pos)) ret->setDouble(pos.y); else ret->setEmpty();}
531void NeuroImpl::get_position_z(ExtValue *ret)
532{Pt3D pos; if (getPosition(pos)) ret->setDouble(pos.z); else ret->setEmpty();}
533
534
535void NeuroImpl::createFieldsObject()
536{
537fields_param=new Param(paramentries?paramentries:(ParamEntry*)&empty_paramtab,this,"Fields");
538fields_object=new ExtObject(fields_param);
539}
540
541void NeuroImpl::get_fields(ExtValue *ret)
542{
543if (!fields_object)
544        createFieldsObject();
545ret->setObject(*fields_object);
546}
547
548void NeuroImpl::get_neurodef(ExtValue *ret)
549{
550ret->setObject(ExtObject(&Neuro::getStaticParam(),neuro));
551}
552
553void NeuroImpl::get_classObject(ExtValue *ret)
554{
555#ifndef GDK_WITHOUT_FRAMS
556NeuroClassExt::makeStaticObject(ret,neuroclass);
557#endif
558}
559
560NeuroImpl::~NeuroImpl()
561{
562if (fields_param)
563        {
564        delete fields_param;
565        delete fields_object;
566        }
567}
Note: See TracBrowser for help on using the repository browser.