source: cpp/gdk/neuroimpl.cpp @ 72

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

neural noise parameter in neurons

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