source: cpp/gdk/neuroimpl.cpp @ 68

Last change on this file since 68 was 68, checked in by Maciej Komosinski, 13 years ago

added missing sources; updated sources for compatibility with vs2008 and added project files

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