Node
genomestart 1
trait 1 0.1 0 0 0 0 0 0 0
trait 2 0.2 0 0 0 0 0 0 0
trait 3 0.3 0 0 0 0 0 0 0
node 1 0 1 3
node 2 0 1 1
node 3 0 1 1
node 4 0 0 2
gene 1 1 4 0.0 0 1 0 1
gene 2 2 4 0.0 0 2 0 1
gene 3 3 4 0.0 0 3 0 1
genomeend 1
////////////////////////////////////////////////////////////////////////////////////////
#ifndef _NNODE_H_
#define _NNODE_H_
#include <algorithm>
#include <vector>
#include "neat.h"
#include "trait.h"
#include "link.h"
namespace NEAT {
enum nodetype {
NEURON = 0,
SENSOR = 1
};
enum nodeplace {
HIDDEN = 0,
INPUT = 1,
OUTPUT = 2,
BIAS = 3
};
enum functype {
SIGMOID = 0
};
class Link;
class Network;
// -----------------------------------------------------------------------
// A NODE is either a NEURON or a SENSOR.
// - If it's a sensor, it can be loaded with a value for output
// - If it's a neuron, it has a list of its incoming input signals (List<Link> is used)
// Use an activation count to avoid flushing
class NNode {
friend class Network;
friend class Genome;
protected:
int activation_count; // keeps track of which activation the node is currently in
double last_activation; // Holds the previous step's activation for recurrency
double last_activation2; // Holds the activation BEFORE the prevous step's
// This is necessary for a special recurrent case when the innode
// of a recurrent link is one time step ahead of the outnode.
// The innode then needs to send from TWO time steps ago
Trait *nodetrait; // Points to a trait of parameters
int trait_id; // identify the trait derived by this node
NNode *dup; // Used for Genome duplication
NNode *analogue; // Used for Gene decoding
bool override; // The NNode cannot compute its own output- something is overriding it
double override_value; // Contains the activation value that will override this node's activation
// Pointer to the Sensor corresponding to this Body.
//Sensor* mySensor;
public:
bool frozen; // When frozen, cannot be mutated (meaning its trait pointer is fixed)
functype ftype; // type is either SIGMOID ..or others that can be added
nodetype type; // type is either NEURON or SENSOR
double activesum; // The incoming activity before being processed
double activation; // The total activation entering the NNode
bool active_flag; // To make sure outputs are active
// NOT USED IN NEAT - covered by "activation" above
double output; // Output of the NNode- the value in the NNode
// ************ LEARNING PARAMETERS ***********
// The following parameters are for use in
// neurons that learn through habituation,
// sensitization, or Hebbian-type processes
double params[NEAT::num_trait_params];
std::vector<Link*> incoming; // A list of pointers to incoming weighted signals from other nodes
std::vector<Link*> outgoing; // A list of pointers to links carrying this node's signal
// These members are used for graphing with GTK+/GDK
std::vector<double> rowlevels; // Depths from output where this node appears
int row; // Final row decided upon for drawing this NNode in
int ypos;
int xpos;
int node_id; // A node can be given an identification number for saving in files
nodeplace gen_node_label; // Used for genetic marking of nodes
NNode(nodetype ntype,int nodeid);
NNode(nodetype ntype,int nodeid, nodeplace placement);
// Construct a NNode off another NNode for genome purposes
NNode(NNode *n,Trait *t);
// Construct the node out of a file specification using given list of traits
NNode (const char *argline, std::vector<Trait*> &traits);
// Copy Constructor
NNode (const NNode& nnode);
~NNode();
// Just return activation for step
double get_active_out();
// Return activation from PREVIOUS time step
double get_active_out_td();
// Returns the type of the node, NEURON or SENSOR
const nodetype get_type();
// Allows alteration between NEURON and SENSOR. Returns its argument
nodetype set_type(nodetype);
// If the node is a SENSOR, returns true and loads the value
bool sensor_load(double);
// Adds a NONRECURRENT Link to a new NNode in the incoming List
void add_incoming(NNode*,double);
// Adds a Link to a new NNode in the incoming List
void add_incoming(NNode*,double,bool);
// Recursively deactivate backwards through the network
void flushback();
// Verify flushing for debugging
void flushback_check(std::vector<NNode*> &seenlist);
// Print the node to a file
void print_to_file(std::ostream &outFile);
void print_to_file(std::ofstream &outFile);
// Have NNode gain its properties from the trait
void derive_trait(Trait *curtrait);
// Returns the gene that created the node
NNode *get_analogue();
// Force an output value on the node
void override_output(double new_output);
// Tell whether node has been overridden
bool overridden();
// Set activation to the override value and turn off override
void activate_override();
// Writes back changes weight values into the genome
// (Lamarckian trasnfer of characteristics)
void Lamarck();
//Find the greatest depth starting from this neuron at depth d
int depth(int d,Network *mynet);
};
} // namespace NEAT
#endif
////////////////////////////////////////////////////////////////////////////////////////
#include "nnode.h"
#include <iostream>
#include <sstream>
using namespace NEAT;
NNode::NNode(nodetype ntype,int nodeid) {
active_flag=false;
activesum=0;
activation=0;
output=0;
last_activation=0;
last_activation2=0;
type=ntype; //NEURON or SENSOR type
activation_count=0; //Inactive upon creation
node_id=nodeid;
ftype=SIGMOID;
nodetrait=0;
gen_node_label=HIDDEN;
dup=0;
analogue=0;
frozen=false;
trait_id=1;
override=false;
}
NNode::NNode(nodetype ntype,int nodeid, nodeplace placement) {
active_flag=false;
activesum=0;
activation=0;
output=0;
last_activation=0;
last_activation2=0;
type=ntype; //NEURON or SENSOR type
activation_count=0; //Inactive upon creation
node_id=nodeid;
ftype=SIGMOID;
nodetrait=0;
gen_node_label=placement;
dup=0;
analogue=0;
frozen=false;
trait_id=1;
override=false;
}
NNode::NNode(NNode *n,Trait *t) {
active_flag=false;
activation=0;
output=0;
last_activation=0;
last_activation2=0;
type=n->type; //NEURON or SENSOR type
activation_count=0; //Inactive upon creation
node_id=n->node_id;
ftype=SIGMOID;
nodetrait=0;
gen_node_label=n->gen_node_label;
dup=0;
analogue=0;
nodetrait=t;
frozen=false;
if (t!=0)
trait_id=t->trait_id;
else trait_id=1;
override=false;
}
NNode::NNode (const char *argline, std::vector<Trait*> &traits) {
int traitnum;
std::vector<Trait*>::iterator curtrait;
activesum=0;
std::stringstream ss(argline);
//char curword[128];
//char delimiters[] = " \n";
//int curwordnum = 0;
//Get the node parameters
//strcpy(curword, NEAT::getUnit(argline, curwordnum++, delimiters));
//node_id = atoi(curword);
//strcpy(curword, NEAT::getUnit(argline, curwordnum++, delimiters));
//traitnum = atoi(curword);
//strcpy(curword, NEAT::getUnit(argline, curwordnum++, delimiters));
//type = (nodetype)atoi(curword);
//strcpy(curword, NEAT::getUnit(argline, curwordnum++, delimiters));
//gen_node_label = (nodeplace)atoi(curword);
int nodety, nodepl;
ss >> node_id >> traitnum >> nodety >> nodepl;
type = (nodetype)nodety;
gen_node_label = (nodeplace)nodepl;
// Get the Sensor Identifier and Parameter String
// mySensor = SensorRegistry::getSensor(id, param);
frozen=false; //TODO: Maybe change
//Get a pointer to the trait this node points to
if (traitnum==0) nodetrait=0;
else {
curtrait=traits.begin();
while(((*curtrait)->trait_id)!=traitnum)
++curtrait;
nodetrait=(*curtrait);
trait_id=nodetrait->trait_id;
}
override=false;
}
// This one might be incomplete
NNode::NNode (const NNode& nnode)
{
active_flag = nnode.active_flag;
activesum = nnode.activesum;
activation = nnode.activation;
output = nnode.output;
last_activation = nnode.last_activation;
last_activation2 = nnode.last_activation2;
type = nnode.type; //NEURON or SENSOR type
activation_count = nnode.activation_count; //Inactive upon creation
node_id = nnode.node_id;
ftype = nnode.ftype;
nodetrait = nnode.nodetrait;
gen_node_label = nnode.gen_node_label;
dup = nnode.dup;
analogue = nnode.dup;
frozen = nnode.frozen;
trait_id = nnode.trait_id;
override = nnode.override;
}
NNode::~NNode() {
std::vector<Link*>::iterator curlink;
//Kill off all incoming links
for(curlink=incoming.begin();curlink!=incoming.end();++curlink) {
delete (*curlink);
}
//if (nodetrait!=0) delete nodetrait;
}
//Returns the type of the node, NEURON or SENSOR
const nodetype NNode::get_type() {
return type;
}
//Allows alteration between NEURON and SENSOR. Returns its argument
nodetype NNode::set_type(nodetype newtype) {
type=newtype;
return newtype;
}
//If the node is a SENSOR, returns true and loads the value
bool NNode::sensor_load(double value) {
if (type==SENSOR) {
//Time delay memory
last_activation2=last_activation;
last_activation=activation;
activation_count++; //Puts sensor into next time-step
activation=value;
return true;
}
else return false;
}
// Note: NEAT keeps track of which links are recurrent and which
// are not even though this is unnecessary for activation.
// It is useful to do so for 2 other reasons:
// 1. It makes networks visualization of recurrent networks possible
// 2. It allows genetic control of the proportion of connections
// that may become recurrent
// Add an incoming connection a node
void NNode::add_incoming(NNode *feednode,double weight,bool recur) {
Link *newlink=new Link(weight,feednode,this,recur);
incoming.push_back(newlink);
(feednode->outgoing).push_back(newlink);
}
// Nonrecurrent version
void NNode::add_incoming(NNode *feednode,double weight) {
Link *newlink=new Link(weight,feednode,this,false);
incoming.push_back(newlink);
(feednode->outgoing).push_back(newlink);
}
// Return activation currently in node, if it has been activated
double NNode::get_active_out() {
if (activation_count>0)
return activation;
else return 0.0;
}
// Return activation currently in node from PREVIOUS (time-delayed) time step,
// if there is one
double NNode::get_active_out_td() {
if (activation_count>1)
return last_activation;
else return 0.0;
}
// This recursively flushes everything leading into and including this NNode, including recurrencies
void NNode::flushback() {
std::vector<Link*>::iterator curlink;
//A sensor should not flush black
if (type!=SENSOR) {
if (activation_count>0) {
activation_count=0;
activation=0;
last_activation=0;
last_activation2=0;
}
//Flush back recursively
for(curlink=incoming.begin();curlink!=incoming.end();++curlink) {
//Flush the link itself (For future learning parameters possibility)
(*curlink)->added_weight=0;
if ((((*curlink)->in_node)->activation_count>0))
((*curlink)->in_node)->flushback();
}
}
else {
//Flush the SENSOR
activation_count=0;
activation=0;
last_activation=0;
last_activation2=0;
}
}
// This recursively checks everything leading into and including this NNode,
// including recurrencies
// Useful for debugging
void NNode::flushback_check(std::vector<NNode*> &seenlist) {
std::vector<Link*>::iterator curlink;
//int pause;
std::vector<Link*> innodes=incoming;
std::vector<NNode*>::iterator location;
if (!(type==SENSOR)) {
//std::cout<<"ALERT: "<<this<<" has activation count "<<activation_count<<std::endl;
//std::cout<<"ALERT: "<<this<<" has activation "<<activation<<std::endl;
//std::cout<<"ALERT: "<<this<<" has last_activation "<<last_activation<<std::endl;
//std::cout<<"ALERT: "<<this<<" has last_activation2 "<<last_activation2<<std::endl;
if (activation_count>0) {
std::cout<<"ALERT: "<<this<<" has activation count "<<activation_count<<std::endl;
}
if (activation>0) {
std::cout<<"ALERT: "<<this<<" has activation "<<activation<<std::endl;
}
if (last_activation>0) {
std::cout<<"ALERT: "<<this<<" has last_activation "<<last_activation<<std::endl;
}
if (last_activation2>0) {
std::cout<<"ALERT: "<<this<<" has last_activation2 "<<last_activation2<<std::endl;
}
for(curlink=innodes.begin();curlink!=innodes.end();++curlink) {
location = std::find(seenlist.begin(),seenlist.end(),((*curlink)->in_node));
if (location==seenlist.end()) {
seenlist.push_back((*curlink)->in_node);
((*curlink)->in_node)->flushback_check(seenlist);
}
}
}
else {
//Flush_check the SENSOR
std::cout<<"sALERT: "<<this<<" has activation count "<<activation_count<<std::endl;
std::cout<<"sALERT: "<<this<<" has activation "<<activation<<std::endl;
std::cout<<"sALERT: "<<this<<" has last_activation "<<last_activation<<std::endl;
std::cout<<"sALERT: "<<this<<" has last_activation2 "<<last_activation2<<std::endl;
if (activation_count>0) {
std::cout<<"ALERT: "<<this<<" has activation count "<<activation_count<<std::endl;
}
if (activation>0) {
std::cout<<"ALERT: "<<this<<" has activation "<<activation<<std::endl;
}
if (last_activation>0) {
std::cout<<"ALERT: "<<this<<" has last_activation "<<last_activation<<std::endl;
}
if (last_activation2>0) {
std::cout<<"ALERT: "<<this<<" has last_activation2 "<<last_activation2<<std::endl;
}
}
}
// Reserved for future system expansion
void NNode::derive_trait(Trait *curtrait) {
if (curtrait!=0) {
for (int count=0;count<NEAT::num_trait_params;count++)
params[count]=(curtrait->params)[count];
}
else {
for (int count=0;count<NEAT::num_trait_params;count++)
params[count]=0;
}
if (curtrait!=0)
trait_id=curtrait->trait_id;
else trait_id=1;
}
// Returns the gene that created the node
NNode *NNode::get_analogue() {
return analogue;
}
// Force an output value on the node
void NNode::override_output(double new_output) {
override_value=new_output;
override=true;
}
// Tell whether node has been overridden
bool NNode::overridden() {
return override;
}
// Set activation to the override value and turn off override
void NNode::activate_override() {
activation=override_value;
override=false;
}
void NNode::print_to_file(std::ofstream &outFile) {
outFile<<"node "<<node_id<<" ";
if (nodetrait!=0) outFile<<nodetrait->trait_id<<" ";
else outFile<<"0 ";
outFile<<type<<" ";
outFile<<gen_node_label<<std::endl;
}
void NNode::print_to_file(std::ostream &outFile) {
//outFile<<"node "<<node_id<<" ";
//if (nodetrait!=0) outFile<<nodetrait->trait_id<<" ";
//else outFile<<"0 ";
//outFile<<type<<" ";
//outFile<<gen_node_label<<std::endl;
char tempbuf[128];
sprintf(tempbuf, "node %d ", node_id);
outFile << tempbuf;
if (nodetrait != 0) {
char tempbuf2[128];
sprintf(tempbuf2, "%d ", nodetrait->trait_id);
outFile << tempbuf2;
}
else outFile << "0 ";
char tempbuf2[128];
sprintf(tempbuf2, "%d %d\n", type, gen_node_label);
outFile << tempbuf2;
}
//Find the greatest depth starting from this neuron at depth d
int NNode::depth(int d, Network *mynet) {
std::vector<Link*> innodes=this->incoming;
std::vector<Link*>::iterator curlink;
int cur_depth; //The depth of the current node
int max=d; //The max depth
if (d>100) {
//std::cout<<mynet->genotype<<std::endl;
//std::cout<<"** DEPTH NOT DETERMINED FOR NETWORK WITH LOOP"<<std::endl;
return 10;
}
//Base Case
if ((this->type)==SENSOR)
return d;
//Recursion
else {
for(curlink=innodes.begin();curlink!=innodes.end();++curlink) {
cur_depth=((*curlink)->in_node)->depth(d+1,mynet);
if (cur_depth>d) max=cur_depth;
}
return max;
} //end else
}