Network

A NETWORK is a LIST of input NODEs and a LIST of output NODEs

////////////////////////////////////////////////////////////////////////////////////////

#ifndef _NETWORK_H_

#define _NETWORK_H_

#include <algorithm>

#include <vector>

#include "neat.h"

#include "nnode.h"

namespace NEAT {

    class Genome;

// ----------------------------------------------------------------------- 

// A NETWORK is a LIST of input NODEs and a LIST of output NODEs           

//   The point of the network is to define a single entity which can evolve

//   or learn on its own, even though it may be part of a larger framework 

class Network {

friend class Genome;

//protected:

public:

int numnodes; // The number of nodes in the net (-1 means not yet counted)

int numlinks; //The number of links in the net (-1 means not yet counted)

std::vector<NNode*> all_nodes;  // A list of all the nodes

std::vector<NNode*>::iterator input_iter;  // For GUILE network inputting  

void destroy();  // Kills all nodes and links within

void destroy_helper(NNode *curnode,std::vector<NNode*> &seenlist); // helper for above

void nodecounthelper(NNode *curnode,int &counter,std::vector<NNode*> &seenlist);

void linkcounthelper(NNode *curnode,int &counter,std::vector<NNode*> &seenlist);

public:

Genome *genotype;  // Allows Network to be matched with its Genome

char *name; // Every Network or subNetwork can have a name

std::vector<NNode*> inputs;  // NNodes that input into the network

std::vector<NNode*> outputs; // Values output by the network

int net_id; // Allow for a network id

double maxweight; // Maximum weight in network for adaptation purposes

bool adaptable; // Tells whether network can adapt or not

// This constructor allows the input and output lists to be supplied

// Defaults to not using adaptation

Network(std::vector<NNode*> in,std::vector<NNode*> out,std::vector<NNode*> all,int netid);

//Same as previous constructor except the adaptibility can be set true or false with adaptval

Network(std::vector<NNode*> in,std::vector<NNode*> out,std::vector<NNode*> all,int netid, bool adaptval);

// This constructs a net with empty input and output lists

Network(int netid);

//Same as previous constructor except the adaptibility can be set true or false with adaptval

Network(int netid, bool adaptval);

// Copy Constructor

Network(const Network& network);

~Network();

// Puts the network back into an inactive state

void flush();


// Verify flushedness for debugging

void flush_check();

// Activates the net such that all outputs are active

bool activate();

// Prints the values of its outputs

void show_activation();

void show_input();

// Add a new input node

void add_input(NNode*);

// Add a new output node

void add_output(NNode*);

// Takes an array of sensor values and loads it into SENSOR inputs ONLY

void load_sensors(double*);

void load_sensors(const std::vector<float> &sensvals);

// Takes and array of output activations and OVERRIDES the outputs' actual 

// activations with these values (for adaptation)

void override_outputs(double*);

// Name the network

void give_name(char*);

// Counts the number of nodes in the net if not yet counted

int nodecount();

// Counts the number of links in the net if not yet counted

int linkcount();

// This checks a POTENTIAL link between a potential in_node

// and potential out_node to see if it must be recurrent 

// Use count and thresh to jump out in the case of an infinite loop 

bool is_recur(NNode *potin_node,NNode *potout_node,int &count,int thresh); 

// Some functions to help GUILE input into Networks   

int input_start();

int load_in(double d);

// If all output are not active then return true

bool outputsoff();

// Just print connections weights with carriage returns

void print_links_tofile(char *filename);

int max_depth();

};

} // namespace NEAT

#endif

////////////////////////////////////////////////////////////////////////////////////////

#include "network.h"

#include <iostream>

#include <sstream>

using namespace NEAT;

Network::Network(std::vector<NNode*> in,std::vector<NNode*> out,std::vector<NNode*> all,int netid) {

  inputs=in;

  outputs=out;

  all_nodes=all;

  name=0;   //Defaults to no name  ..NOTE: TRYING TO PRINT AN EMPTY NAME CAN CAUSE A CRASH

  numnodes=-1;

  numlinks=-1;

  net_id=netid;

  adaptable=false;

}

Network::Network(std::vector<NNode*> in,std::vector<NNode*> out,std::vector<NNode*> all,int netid, bool adaptval) {

  inputs=in;

  outputs=out;

  all_nodes=all;

  name=0;   //Defaults to no name  ..NOTE: TRYING TO PRINT AN EMPTY NAME CAN CAUSE A CRASH                                    

  numnodes=-1;

  numlinks=-1;

  net_id=netid;

  adaptable=adaptval;

}

Network::Network(int netid) {

name=0; //Defaults to no name

numnodes=-1;

numlinks=-1;

net_id=netid;

adaptable=false;

}

Network::Network(int netid, bool adaptval) {

  name=0; //Defaults to no name                                                                                               

  numnodes=-1;

  numlinks=-1;

  net_id=netid;

  adaptable=adaptval;

}

Network::Network(const Network& network)

{

std::vector<NNode*>::const_iterator curnode;

// Copy all the inputs

for(curnode = network.inputs.begin(); curnode != network.inputs.end(); ++curnode) {

NNode* n = new NNode(**curnode);

inputs.push_back(n);

all_nodes.push_back(n);

}

// Copy all the outputs

for(curnode = network.outputs.begin(); curnode != network.outputs.end(); ++curnode) {

NNode* n = new NNode(**curnode);

outputs.push_back(n);

all_nodes.push_back(n);

}

if(network.name)

name = strdup(network.name);

else

name = 0;

numnodes = network.numnodes;

numlinks = network.numlinks;

net_id = network.net_id;

adaptable = network.adaptable;

}

Network::~Network() {

if (name!=0)

delete [] name;

destroy();  // Kill off all the nodes and links

}

// Puts the network back into an initial state

void Network::flush() {

std::vector<NNode*>::iterator curnode;

for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

(*curnode)->flushback();

}

}

// Debugger: Checks network state

void Network::flush_check() {

std::vector<NNode*>::iterator curnode;

std::vector<NNode*>::iterator location;

std::vector<NNode*> seenlist;  //List of nodes not to doublecount

for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {    

        location= std::find(seenlist.begin(),seenlist.end(),(*curnode));

if (location==seenlist.end()) {

seenlist.push_back(*curnode);

(*curnode)->flushback_check(seenlist);

}

}

}

// If all output are not active then return true

bool Network::outputsoff() {

std::vector<NNode*>::iterator curnode;

for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

if (((*curnode)->activation_count)==0) return true;

}

return false;

}

// Print the connections weights to a file separated by only carriage returns

void Network::print_links_tofile(char *filename) {

std::vector<NNode*>::iterator curnode;

std::vector<Link*>::iterator curlink;

    std::ofstream oFile(filename);

//Make sure it worked

//if (!oFile) {

// cerr<<"Can't open "<<filename<<" for output"<<endl;

//return 0;

//}

for(curnode=all_nodes.begin();curnode!=all_nodes.end();++curnode) {

if (((*curnode)->type)!=SENSOR) {

for(curlink=((*curnode)->incoming).begin(); curlink!=((*curnode)->incoming).end(); ++curlink) {

                oFile << (*curlink)->in_node->node_id << " -> " <<( *curlink)->out_node->node_id << " : " << (*curlink)->weight << std::endl;

} // end for loop on links

} //end if

} //end for loop on nodes

oFile.close();

} //print_links_tofile

// Activates the net such that all outputs are active

// Returns true on success;

bool Network::activate() {

std::vector<NNode*>::iterator curnode;

std::vector<Link*>::iterator curlink;

double add_amount;  //For adding to the activesum

bool onetime; //Make sure we at least activate once

int abortcount=0;  //Used in case the output is somehow truncated from the network

//cout<<"Activating network: "<<this->genotype<<endl;

//Keep activating until all the outputs have become active 

//(This only happens on the first activation, because after that they

// are always active)

onetime=false;

while(outputsoff()||!onetime) {

++abortcount;

if (abortcount==20) {

return false;

//cout<<"Inputs disconnected from output!"<<endl;

}

//std::cout<<"Outputs are off"<<std::endl;

// For each node, compute the sum of its incoming activation 

for(curnode=all_nodes.begin();curnode!=all_nodes.end();++curnode) {

//Ignore SENSORS

//cout<<"On node "<<(*curnode)->node_id<<endl;

if (((*curnode)->type)!=SENSOR) {

(*curnode)->activesum=0;

(*curnode)->active_flag=false;  //This will tell us if it has any active inputs

// For each incoming connection, add the activity from the connection to the activesum 

for(curlink=((*curnode)->incoming).begin();curlink!=((*curnode)->incoming).end();++curlink) {

//Handle possible time delays

if (!((*curlink)->time_delay)) {

add_amount=((*curlink)->weight)*(((*curlink)->in_node)->get_active_out());

if ((((*curlink)->in_node)->active_flag)||

(((*curlink)->in_node)->type==SENSOR)) (*curnode)->active_flag=true;

(*curnode)->activesum+=add_amount;

//std::cout<<"Node "<<(*curnode)->node_id<<" adding "<<add_amount<<" from node "<<((*curlink)->in_node)->node_id<<std::endl;

}

else {

//Input over a time delayed connection

add_amount=((*curlink)->weight)*(((*curlink)->in_node)->get_active_out_td());

(*curnode)->activesum+=add_amount;

}

} //End for over incoming links

} //End if (((*curnode)->type)!=SENSOR) 

} //End for over all nodes

// Now activate all the non-sensor nodes off their incoming activation 

for(curnode=all_nodes.begin();curnode!=all_nodes.end();++curnode) {

if (((*curnode)->type)!=SENSOR) {

//Only activate if some active input came in

if ((*curnode)->active_flag) {

//cout<<"Activating "<<(*curnode)->node_id<<" with "<<(*curnode)->activesum<<": ";

//Keep a memory of activations for potential time delayed connections

(*curnode)->last_activation2=(*curnode)->last_activation;

(*curnode)->last_activation=(*curnode)->activation;

//If the node is being overrided from outside,

//stick in the override value

if ((*curnode)->overridden()) {

//Set activation to the override value and turn off override

(*curnode)->activate_override();

}

else {

//Now run the net activation through an activation function

if ((*curnode)->ftype==SIGMOID)

(*curnode)->activation=NEAT::fsigmoid((*curnode)->activesum,4.924273,2.4621365);  //Sigmoidal activation- see comments under fsigmoid

}

//cout<<(*curnode)->activation<<endl;

//Increment the activation_count

//First activation cannot be from nothing!!

(*curnode)->activation_count++;

}

}

}

onetime=true;

}

if (adaptable) {

 //std::cout << "ADAPTING" << std:endl;

 // ADAPTATION:  Adapt weights based on activations 

 for(curnode=all_nodes.begin();curnode!=all_nodes.end();++curnode) {

   //Ignore SENSORS

   

   //cout<<"On node "<<(*curnode)->node_id<<endl;

   

   if (((*curnode)->type)!=SENSOR) {

     

     // For each incoming connection, perform adaptation based on the trait of the connection 

     for(curlink=((*curnode)->incoming).begin();curlink!=((*curnode)->incoming).end();++curlink) {


if (((*curlink)->trait_id==2)||

   ((*curlink)->trait_id==3)||

   ((*curlink)->trait_id==4)) {

 

 //In the recurrent case we must take the last activation of the input for calculating hebbian changes

 if ((*curlink)->is_recurrent) {

   (*curlink)->weight=

     hebbian((*curlink)->weight,maxweight,

     (*curlink)->in_node->last_activation, 

     (*curlink)->out_node->get_active_out(),

     (*curlink)->params[0],(*curlink)->params[1],

     (*curlink)->params[2]);

   

   

 }

 else { //non-recurrent case

   (*curlink)->weight=

     hebbian((*curlink)->weight,maxweight,

     (*curlink)->in_node->get_active_out(), 

     (*curlink)->out_node->get_active_out(),

     (*curlink)->params[0],(*curlink)->params[1],

     (*curlink)->params[2]);

 }

}


     }

     

   }

   

 }

 

} //end if (adaptable)

return true;  

}

// THIS WAS NOT USED IN THE FINAL VERSION, AND NOT FULLY IMPLEMENTED,   

// BUT IT SHOWS HOW SOMETHING LIKE THIS COULD BE INITIATED

// Note that checking networks for loops in general in not necessary

// and therefore I stopped writing this function

// Check Network for loops.  Return true if its ok, false if there is a loop.

//bool Network::integrity() {

//  std::vector<NNode*>::iterator curnode;

//  std::vector<std::vector<NNode*>*> paths;

//  int count;

//  std::vector<NNode*> *newpath;

//  std::vector<std::vector<NNode*>*>::iterator curpath;

//  for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

//    newpath=new std::vector<NNode*>();

//    paths.push_back(newpath);

//    if (!((*curnode)->integrity(newpath))) return false;

//  }

//Delete the paths now that we are done

//  curpath=paths.begin();

//  for(count=0;count<paths.size();count++) {

//    delete (*curpath);

//    curpath++;

//  }

//  return true;

//}

// Prints the values of its outputs

void Network::show_activation() {

std::vector<NNode*>::iterator curnode;

int count;

//if (name!=0)

//  cout<<"Network "<<name<<" with id "<<net_id<<" outputs: (";

//else cout<<"Network id "<<net_id<<" outputs: (";

count=1;

for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

//cout<<"[Output #"<<count<<": "<<(*curnode)<<"] ";

count++;

}

//cout<<")"<<endl;

}

void Network::show_input() {

std::vector<NNode*>::iterator curnode;

int count;

//if (name!=0)

//  cout<<"Network "<<name<<" with id "<<net_id<<" inputs: (";

//else cout<<"Network id "<<net_id<<" outputs: (";

count=1;

for(curnode=inputs.begin();curnode!=inputs.end();++curnode) {

//cout<<"[Input #"<<count<<": "<<(*curnode)<<"] ";

count++;

}

//cout<<")"<<endl;

}

// Add an input

void Network::add_input(NNode *in_node) {

inputs.push_back(in_node);

}

// Add an output

void Network::add_output(NNode *out_node) {

outputs.push_back(out_node);

}

// Takes an array of sensor values and loads it into SENSOR inputs ONLY

void Network::load_sensors(double *sensvals) {

//int counter=0;  //counter to move through array

std::vector<NNode*>::iterator sensPtr;

for(sensPtr=inputs.begin();sensPtr!=inputs.end();++sensPtr) {

//only load values into SENSORS (not BIASes)

if (((*sensPtr)->type)==SENSOR) {

(*sensPtr)->sensor_load(*sensvals);

sensvals++;

}

}

}

void Network::load_sensors(const std::vector<float> &sensvals) {

//int counter=0;  //counter to move through array

std::vector<NNode*>::iterator sensPtr;

std::vector<float>::const_iterator valPtr;

for(valPtr = sensvals.begin(), sensPtr = inputs.begin(); sensPtr != inputs.end() && valPtr != sensvals.end(); ++sensPtr, ++valPtr) {

//only load values into SENSORS (not BIASes)

if (((*sensPtr)->type)==SENSOR) {

(*sensPtr)->sensor_load(*valPtr);

//sensvals++;

}

}

}

// Takes and array of output activations and OVERRIDES 

// the outputs' actual activations with these values (for adaptation)

void Network::override_outputs(double* outvals) {

std::vector<NNode*>::iterator outPtr;

for(outPtr=outputs.begin();outPtr!=outputs.end();++outPtr) {

(*outPtr)->override_output(*outvals);

outvals++;

}

}

void Network::give_name(char *newname) {

char *temp;

char *temp2;

temp=new char[strlen(newname)+1];

strcpy(temp,newname);

if (name==0) name=temp;

else {

temp2=name;

delete temp2;

name=temp;

}

}

// The following two methods recurse through a network from outputs

// down in order to count the number of nodes and links in the network.

// This can be useful for debugging genotype->phenotype spawning 

// (to make sure their counts correspond)

int Network::nodecount() {

int counter=0;

std::vector<NNode*>::iterator curnode;

std::vector<NNode*>::iterator location;

std::vector<NNode*> seenlist;  //List of nodes not to doublecount

for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

        location = std::find(seenlist.begin(),seenlist.end(),(*curnode));

if (location==seenlist.end()) {

counter++;

seenlist.push_back(*curnode);

nodecounthelper((*curnode),counter,seenlist);

}

}

numnodes=counter;

return counter;

}

void Network::nodecounthelper(NNode *curnode,int &counter,std::vector<NNode*> &seenlist) {

std::vector<Link*> innodes=curnode->incoming;

std::vector<Link*>::iterator curlink;

std::vector<NNode*>::iterator location;

if (!((curnode->type)==SENSOR)) {

for(curlink=innodes.begin();curlink!=innodes.end();++curlink) {

            location= std::find(seenlist.begin(),seenlist.end(),((*curlink)->in_node));

if (location==seenlist.end()) {

counter++;

seenlist.push_back((*curlink)->in_node);

nodecounthelper((*curlink)->in_node,counter,seenlist);

}

}

}

}

int Network::linkcount() {

int counter=0;

std::vector<NNode*>::iterator curnode;

std::vector<NNode*> seenlist;  //List of nodes not to doublecount

for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

linkcounthelper((*curnode),counter,seenlist);

}

numlinks=counter;

return counter;

}

void Network::linkcounthelper(NNode *curnode,int &counter,std::vector<NNode*> &seenlist) {

std::vector<Link*> inlinks=curnode->incoming;

std::vector<Link*>::iterator curlink;

std::vector<NNode*>::iterator location;

    location = std::find(seenlist.begin(),seenlist.end(),curnode);

if ((!((curnode->type)==SENSOR))&&(location==seenlist.end())) {

seenlist.push_back(curnode);

for(curlink=inlinks.begin();curlink!=inlinks.end();++curlink) {

counter++;

linkcounthelper((*curlink)->in_node,counter,seenlist);

}

}

}

// Destroy will find every node in the network and subsequently

// delete them one by one.  Since deleting a node deletes its incoming

// links, all nodes and links associated with a network will be destructed

// Note: Traits are parts of genomes and not networks, so they are not

//       deleted here

void Network::destroy() {

std::vector<NNode*>::iterator curnode;

std::vector<NNode*>::iterator location;

std::vector<NNode*> seenlist;  //List of nodes not to doublecount

// Erase all nodes from all_nodes list 

for(curnode=all_nodes.begin();curnode!=all_nodes.end();++curnode) {

delete (*curnode);

}

// ----------------------------------- 

//  OLD WAY-the old way collected the nodes together and then deleted them

//for(curnode=outputs.begin();curnode!=outputs.end();++curnode) {

//cout<<seenstd::vector<<endl;

//cout<<curnode<<endl;

//cout<<curnode->node_id<<endl;

//  location=find(seenlist.begin(),seenlist.end(),(*curnode));

//  if (location==seenlist.end()) {

//    seenlist.push_back(*curnode);

//    destroy_helper((*curnode),seenlist);

//  }

//}

//Now destroy the seenlist, which is all the NNodes in the network

//for(curnode=seenlist.begin();curnode!=seenlist.end();++curnode) {

//  delete (*curnode);

//}

}

void Network::destroy_helper(NNode *curnode,std::vector<NNode*> &seenlist) {

std::vector<Link*> innodes=curnode->incoming;

std::vector<Link*>::iterator curlink;

std::vector<NNode*>::iterator location;

if (!((curnode->type)==SENSOR)) {

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);

destroy_helper((*curlink)->in_node,seenlist);

}

}

}

}

// This checks a POTENTIAL link between a potential in_node and potential out_node to see if it must be recurrent 

bool Network::is_recur(NNode *potin_node,NNode *potout_node,int &count,int thresh) {

std::vector<Link*>::iterator curlink;

++count;  //Count the node as visited

if (count>thresh) {

//cout<<"returning false"<<endl;

return false;  //Short out the whole thing- loop detected

}

if (potin_node==potout_node) return true;

else {

//Check back on all links...

for(curlink=(potin_node->incoming).begin();curlink!=(potin_node->incoming).end();curlink++) {

//But skip links that are already recurrent

//(We want to check back through the forward flow of signals only

if (!((*curlink)->is_recurrent)) {

if (is_recur((*curlink)->in_node,potout_node,count,thresh)) return true;

}

}

return false;

}

}

int Network::input_start() {

input_iter=inputs.begin();

return 1;

}

int Network::load_in(double d) {

(*input_iter)->sensor_load(d);

input_iter++;

if (input_iter==inputs.end()) return 0;

else return 1;

}

//Find the maximum number of neurons between an ouput and an input

int Network::max_depth() {

  std::vector<NNode*>::iterator curoutput; //The current output we are looking at

  int cur_depth; //The depth of the current node

  int max=0; //The max depth

  

  for(curoutput=outputs.begin();curoutput!=outputs.end();curoutput++) {

    cur_depth=(*curoutput)->depth(0,this);

    if (cur_depth>max) max=cur_depth;

  }

  return max;

}