//Node.cpp // //neural network node class template // #ifndef NODE_C #define NODE_C #include "Node.h" template Node::Node () {} template Node::~Node () { delete []inputWeight; delete []outputWeight; } //copy constructor template Node::Node (const Node &node) { numberInputConnections = node.numberInputConnections; numberOutputConnections = node.numberOutputConnections; nextInputConnection = node.nextInputConnection; nextOutputConnection = node.nextOutputConnection; inputWeight = new Weight[numberInputConnections]; outputWeight = new Weight[numberOutputConnections]; for (int counter = 0; counter < numberInputConnections; ++counter) { inputWeight[counter] = node.inputWeight[counter]; } for (int counter = 0; counter < numberOutputConnections; ++counter) { outputWeight[counter] = node.outputWeight[counter]; } output = node.output; } //assignment operator template Node& Node::operator= (const Node &node) { if (this != &node) { numberInputConnections = node.numberInputConnections; numberOutputConnections = node.numberOutputConnections; delete []inputWeight; delete []outputWeight; inputWeight = new Weight[numberInputConnections]; outputWeight = new Weight[numberOutputConnections]; for (int counter = 0; counter < numberInputConnections; ++counter) { inputWeight[counter] = node.inputWeight[counter]; } for (int counter = 0; counter < numberOutputConnections; ++counter) { outputWeight[counter] = node.outputWeight[counter]; } } return *this; } //equality operator //NOTE: this checks only for the same connectivity (same number of connections // to the same nodes), not for the same weights template bool Node::operator== (const Node &node) { if (this != &node) { if (numberInputConnections != node.numberInputConnections) return false; if (numberOutputConnections != node.numberOutputConnections) return false; bool found; for (int counter = 0; counter < numberInputConnections; ++counter) { for (int counter2 = 0; counter2 < numberInputConnections; ++counter2) { found = false; if (inputWeight[counter].readNodePointer() == node.inputWeight[counter2].readNodePointer()) { found = true; break; } } if (found == false) return false; } for (int counter = 0; counter < numberOutputConnections; ++counter) { for (int counter2 = 0; counter2 < numberOutputConnections; ++counter2) { found = false; if (outputWeight[counter].readNodePointer() == node.outputWeight[counter2].readNodePointer()) { found = true; break; } } if (found == false) return false; } } return true; } //"less than" operator //A < B means that node A has no input nodes from node B, and so its output may be calculated first. //(Doing one comparison won't be definitive, but using this comparison for a sort will be.) template bool Node::operator< (const Node &node) { for (int counter = 0; counter < numberInputConnections; ++counter) { if (inputWeight[counter].readNodePointer() == &node) return false; } return true; } //initialize a node: //create arrays of empty input and output connections template void Node::initialize (int numberInputs, int numberOutputs) { numberInputConnections = numberInputs; numberOutputConnections = numberOutputs; inputWeight = new Weight[numberInputConnections]; outputWeight = new Weight[numberOutputConnections]; nextInputConnection = -1; nextOutputConnection = -1; } //initialize an input connection from a node //use the next free empty weight for the connection //return the index number of the connection, or -1 if no connection made // because we've made all the allowed connections, or because we already have a // connection to that node //NOTE: WHEN YOU INITIALIZE AN INPUT CONNECTION TO A NODE, YOU ALSO MUST // INITIALIZE THE RECIPROCAL OUTPUT CONNECTION FROM THAT NODE // template int Node::initializeInputConnection (Node &node) { //make sure that there is still a connection left to initialize if (++nextInputConnection == numberInputConnections) return -1; //make sure that there is no connection to that node already for (int counter = 0; counter < nextInputConnection; ++counter) { if (inputWeight[counter].readNodePointer() == &node) { if (&node !=0) return -1; //special case: multiple null connections allowed } } //now set up the connection inputWeight[nextInputConnection].writeNodePointer (&node); inputWeight[nextInputConnection].randomizeWeight(); inputWeight[nextInputConnection].writeWeightChange (0); return nextInputConnection; } //initialize an output connection to a node //return the index number of the connection, or -1 if no connection made template int Node::initializeOutputConnection (Node &node) { //make sure that there is still a connection left to inialize if (++nextOutputConnection == numberOutputConnections) return -1; //make sure that there is no connection to that node already for (int counter = 0; counter < nextOutputConnection; ++counter) { if (outputWeight[counter].readNodePointer () == &node) return -1; } //now set up the connection outputWeight[nextOutputConnection].writeNodePointer (&node); outputWeight[nextOutputConnection].randomizeWeight(); outputWeight[nextOutputConnection].writeWeightChange (0); return nextOutputConnection; } //---------functions for pruning and adding connections--------- //four functions (removeInputConnections, removeOutputConnection, //addInputConnection, and addOutputConnection) are PUBLIC, and take the Node to //which a connection is to be added/removed as an argument; these functions //return the new count of that type of connection, or -1 for an error //N.B.: these functions also add/remove the corresponding output/input connection, // and so should not be called twice //four PRIVATE functions do the actual work, and DO NOT also add/remove the //corresponding input/output function; these exist so that the user doesn't //have to call two functions to add/remove one node //. . . remove an input connection template int Node::removeInputConnection (Node &node) { //remove the corresponding output connection removeOutputConnectionOnly (*this); //remove the desired input connection return removeInputConnectionOnly (node); } template int Node::removeInputConnectionOnly (const Node &node) { //find the connection; return -1 if we can't find it int index; bool found; for (index = 0; index < numberInputConnections; ++index) { if (inputWeight[index].readNodePointer () == &node) { found = true; break; } } if (found != true) return -1; //temporarily store the weights, delete the old weight array, and then // write the stored weights into a new, smaller weight array Weight *tempWeight; tempWeight = new Weight[numberInputConnections -1]; for (int counter = 0; counter < index; ++counter) { tempWeight[counter] = inputWeight[counter]; } for (int counter = index+1; counter < numberInputConnections; ++counter) { tempWeight[counter-1] = inputWeight[counter]; } delete []inputWeight; inputWeight = new Weight[numberInputConnections -1]; for (int counter = 0; counter < (numberInputConnections-1); ++counter) { inputWeight[counter] = tempWeight[counter]; } //decrement and return the number of connections return --numberInputConnections; } //. . . remove an output connection template int Node::removeOutputConnection (Node &node) { removeInputConnectionOnly (*this); return removeOutputConnectionOnly (node); } template int Node::removeOutputConnectionOnly (const Node &node) { //find the connection; return -1 if we can't find it int index; bool found; for (index = 0; index < numberOutputConnections; ++index) { if (outputWeight[index].readNodePointer () == &node) { found = true; break; } } if (found != true) return -1; //temporarily store the weights, delete the old weight array, and then // write the stored weights into a new, smaller weight array Weight *tempWeight; tempWeight = new Weight[numberOutputConnections -1]; for (int counter = 0; counter < index; ++counter) { tempWeight[counter] = outputWeight[counter]; } for (int counter = index+1; counter < numberOutputConnections; ++counter) { tempWeight[counter-1] = outputWeight[counter]; } delete []outputWeight; outputWeight = new Weight[numberOutputConnections -1]; for (int counter = 0; counter < (numberOutputConnections-1); ++counter) { outputWeight[counter] = tempWeight[counter]; } //decrement and return the number of connections return --numberOutputConnections; } //. . . add a new input connection template int Node::addInputConnection (Node &node) { addOutputConnectionOnly (*this); return addInputConnectionOnly (node); } template int Node::addInputConnectionOnly (Node &node) { //first make sure that the connection does not already exist for (int counter = 0; counter < numberInputConnections; ++counter) { if (inputWeight[counter].readNodePointer () == &node) return -1; } //temporarily store the existing weights, delete the old weight array, and then // write the stored weights into a new, larger weight array Weight *tempWeight; tempWeight = new Weight[numberInputConnections]; for (int counter = 0; counter < numberInputConnections; ++counter) { tempWeight[counter] = inputWeight[counter]; } delete []inputWeight; inputWeight = new Weight[numberInputConnections+1]; for (int counter = 0; counter < (numberInputConnections); ++counter) { inputWeight[counter] = tempWeight[counter]; } //now add the new connection inputWeight[numberInputConnections].writeNodePointer (&node); inputWeight[numberInputConnections].randomizeWeight (); inputWeight[numberInputConnections].writeWeightChange (0); return ++numberInputConnections; } //. . . add a new output connection template int Node::addOutputConnection (Node &node) { addInputConnectionOnly (*this); return addOutputConnectionOnly (node); } template int Node::addOutputConnectionOnly (Node &node) { //first make sure that the connection does not already exist for (int counter = 0; counter < numberOutputConnections; ++counter) { if (outputWeight[counter].readNodePointer () == &node) return -1; } //temporarily store the existing weights, delete the old weight array, and then // write the stored weights into a new, larger weight array Weight *tempWeight; tempWeight = new Weight[numberOutputConnections]; for (int counter = 0; counter < numberOutputConnections; ++counter) { tempWeight[counter] = outputWeight[counter]; } delete []outputWeight; outputWeight = new Weight[numberOutputConnections+1]; for (int counter = 0; counter < (numberOutputConnections); ++counter) { outputWeight[counter] = tempWeight[counter]; } //now add the new connection outputWeight[numberOutputConnections].writeNodePointer (&node); outputWeight[numberOutputConnections].randomizeWeight (); outputWeight[numberOutputConnections].writeWeightChange (0); return ++numberOutputConnections; } //-----------readers and writers--------------- //how many input connections? template inline int Node::readNumberInputConnections () { return numberInputConnections; } //how many output connections? template inline int Node::readNumberOutputConnections () { return numberOutputConnections; } //return the weight of an input connection referred to by . . . //. . . index number template inline userType Node::readInputConnectionWeight (int connectionNumber) { if (connectionNumber >= numberInputConnections) return -128; //in range? else return inputWeight[connectionNumber].readWeight (); } //. . . connected node identity template userType Node::readInputConnectionWeight (const Node &node) { for (int counter = 0; counter < numberInputConnections; ++counter) { if (inputWeight[counter].readNodePointer() == &node) return inputWeight[counter].readWeight(); } return -128; //error - connection does not exist } //return the weight of an output connection referred to by . . . //. . . index number template inline userType Node::readOutputConnectionWeight (int connectionNumber) { if (connectionNumber >= numberOutputConnections) return -128; //in range? else return outputWeight[connectionNumber].readWeight(); } //. . . connected node identity template userType Node::readOutputConnectionWeight (const Node &node) { for (int counter = 0; counter < numberOutputConnections; ++counter) { if (outputWeight[counter].readNodePointer() == &node) return outputWeight[counter].readWeight(); } return -128; //error - connection does not exit } //write a weight into an input connection template void Node::writeInputConnectionWeight (Node &node, userType value) { for (int counter = 0; counter < numberInputConnections; ++counter) { if (inputWeight[counter].readNodePointer() == &node) { inputWeight[counter].writeWeight (value); break; } } } //write a weight into an output connection template void Node::writeOutputConnectionWeight (Node &node, userType value) { for (int counter = 0; counter < numberOutputConnections; ++counter) { if (outputWeight[counter].readNodePointer() == &node) { outputWeight[counter].writeWeight (value); break; } } } //return the output value of the node //USE WITH CARE!!! the value may not be current as weights or inputs //may have changed since it was calculated template inline userType Node::readActivation () { return output; } //-----calculate the activation (output) of the node------- //calculate the net output of the node, then determine its activation //default activation function //redefine this if you would like a different function template inline userType Node::activationFunction (userType net, userType threshold) { if (net >= threshold) return 1; else return 0; } //this function determines the net output, then calls the preceding activation function template userType Node::activation (userType threshold) { userType net = 0; for (int counter = 0; counter < numberInputConnections; ++counter) { net += inputWeight[counter].readWeight () * (inputWeight[counter].readNodePointer())->output; } output = activationFunction(net, threshold); return output; } //this is here as a placeholder only template userType Node::readError () { return 0; } #endif