source: cpp/gdk/neuroimpl.cpp @ 70

Last change on this file since 70 was 69, checked in by sz, 14 years ago

removed unnecessary files. all GDK samples can be built again (including neurotest). TODO: add #define GDK_WITHOUT_FRAMS to all VS projects! Note: read Makefile before syncing frams<->GDK!

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