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

}