//*******************************************************************************
// CSC 143 Computer Programming II Spring 1999 Instructor: Keith Hughes
//
// Homework 3
// File : \\Venus\katy\CIS143\Homework3\AdalineNetwork\base.cpp
//
// Purpose : Inplementation file for Neuron and Link classes
//
// Author : Hsin-yi F. Berg
// Date : 5/9/1999 Sun.
// Last Update: 5/28/1999 Fri.
// Update Note: Error has been moved form Link class to Neuron class
//                 Replace the dynamically allocated arrays of pointers to Links in
//                 the Neuron class with singly-linked lists of pointers to Links
//*******************************************************************************

#include "base.h"
#include <iostream.h>
#include <assert.h>

// Singly-linked list node that has a Link pointer and a next pointer
struct LinkNode {
    // Data item for this node which is a pointer to a Link
    Link *pLink;

    // Next item in the list.
    LinkNode *next;
};

/*
    Static member initialization and function definitions for class Neuron
*/

int Neuron::id_count = 0;

/*
    Neuron::Neuron()

    Default constructor for an Neuron. Not connected to any link.
*/

Neuron::Neuron()

{
    id = id_count++;
    input_link = NULL;
    size_input_link = 0;
    output_link = NULL;
    size_output_link = 0;
    threshold = 0.0;
    high = 1.0;
    low = -1.0;
    value = -1.0;
    error = 0.0;
}

/*
    Neuron::~Neuron()

    Destructor for the Neuron.
    Goes through the whole list and delete every node on the way.
*/

Neuron::~Neuron()
{
    LinkNode *temp = NULL;    // temp points to the node to be deleted
    
    // trasverse through the list, deleting each node along the way
    while (input_link != NULL) {
        temp = input_link;
        input_link = input_link->next;
        delete temp;
    }
    // No need to set input_link to NULL because it is now NULL.
    
    while (output_link != NULL) {
        temp = output_link;
        output_link = output_link->next;
        delete temp;
    }
    // No need to set output_link to NULL because it is now NULL.

    assert (input_link == NULL);    // double check if input_link is really NULL
    assert (output_link == NULL);    // double check if output_link is really NULL
}

/*
    void Neuron::Print(ostream &out) const

    Print the internal state of the Neuron to the client_supplied ostream.
*/

void Neuron::Print(ostream &out) const

{
    out << "Neuron " << id << endl;
    out << "\tThreshold = " << threshold << endl;
    out << "\tLow Value = " << low << endl;
    out << "\tHigh Value = " << high << endl;
    out << "\tValue = " << value << endl;
    out << "\tError = " << error << endl;

    // we'll print out the Links backwards because of the way we add new links
    // (we added the newLinkNode to the front of the list).
    // print all the links in the input list
    out << "\tInput Links:";
    if(size_input_link == 0)
        out << " None" << endl;
    else {
        out << endl;
        // temp trasverse the list, prints out every Link along the way
        for(LinkNode *temp = input_link; temp != NULL; temp = temp->next) {
            out << "\tID : " << temp->pLink->GetID();
            out << "\tWeight : " << temp->pLink->GetWeight();
            out << endl;
        }
    }

    // print all the links in the output list
    out << "\tOutput Links:";
    if(size_output_link == 0)
        out << " None" << endl;
    else {
        out << endl;
        // temp trasverse the list, prints out every Link along the way
        for(LinkNode *temp = output_link; temp != NULL; temp = temp->next) {
            out << "\tID : " << temp->pLink->GetID();
            out << "\tWeight : " << temp->pLink->GetWeight();
            out << endl;
        }
    }
}

/*
    bool Neuron::InputRoom(void) const

    Returns true if there is room for a new input link, false otherwise.
*/

bool Neuron::InputRoom(void) const

{
    return true;
}

/*
    void Neuron::AddInput(const Link &newLink)

    Add an input Link to the Neuron.
    There should be room in the Neuron for another input Link.
*/

void Neuron::AddInput(Link &newLink)

{
    // create the new linkNode first
    LinkNode *newLinkNode = new LinkNode;
    assert(newLinkNode);
    // initialize it's pLink field points to the newLink, next field to NULL
    newLinkNode->pLink = &newLink;
    newLinkNode->next = NULL;

    // we'll add the newLink in front of the list (order does not matter)
    // if input_link is originally empty, just set input_link to the newLinkNode
    if(input_link == NULL)
        input_link = newLinkNode;
    // otherwise put it in front of the list
    else {
        // add the newLinkNode
        newLinkNode->next = input_link;
        input_link = newLinkNode;
    }

    size_input_link++;
}

/*
    bool Neuron::OutputRoom(void) const

    Returns true if there is room for a new output link, false otherwise.
*/

bool Neuron::OutputRoom(void) const

{
    return true;
}

/*
    void Neuron::AddOutput(const Link &newLink)

    Add an output Link to the Neuron.
    There should be room in the Neuron for another output Link.
*/

void Neuron::AddOutput(Link &newLink)

{
    // create the new linkNode first
    LinkNode *newLinkNode = new LinkNode;
    assert(newLinkNode);
    // initialize it's pLink field points to the newLink, next field to NULL
    newLinkNode->pLink = &newLink;
    newLinkNode->next = NULL;

    // we'll add the newLink in front of the list (order does not matter)
    // if output_link is originally empty, just set output_link to the newLinkNode
    if(output_link == NULL)
        output_link = newLinkNode;
    // otherwise put it in front of the list
    else {
        // add the newLinkNode
        newLinkNode->next = output_link;
        output_link = newLinkNode;
    }

    size_output_link++;
}

/*
    LinkNode *&Neuron::GetInputLink(void)

    Return the reference of input_link
*/

LinkNode *&Neuron::GetInputLink(void)

{
    return input_link;
}

/*
    LinkNode *&Neuron::GetOutputLink(void)

    Return the reference of output_link
*/

LinkNode *&Neuron::GetOutputLink(void)

{
    return output_link;
}

/*
    ID Neuron::GetID(void) const

    Return the ID of this neuron
*/

ID Neuron::GetID(void) const

{
    return id;
}

/*
    void Neuron::SetValue(double newValue)

    Set the value of the Neuron to some new value.
*/

void Neuron::SetValue(double newValue)

{
    value = newValue;
}


/*
    void Neuron::GetValue(void) const

    Return the value of the neuron.
*/

double Neuron::GetValue(void) const

{
    return value;
}

/*
    void Neuron::SetError(double newError)

    Set the error of the Neuron to new error value.
*/

void Neuron::SetError(double newError)

{
    error = newError;
}

/*
    void Neuron::GetError(void) const

    Get the error of this Neuron.
*/

double Neuron::GetError(void) const

{
    return error;
}

/*
    void Neuron::Fire(void)

    Calculating the weighted sum from the input links, and applying
    the threshold function to the resulting sum and storing the
    chosen threshold output value as the neuron's value.
*/

void Neuron::Fire(void)

{
    // weighted_sum is the sum of all the "input Neuron values * weight of the link"
    double weighted_sum = 0;
    // we need to go through the input_link linked list and calculate the weighted value
    // which is the input Neuron value * weight of the link as we go on
    for(LinkNode *temp = input_link; temp != NULL; temp = temp->next)
        weighted_sum += temp->pLink->GetWeightedInputValue();
    // after calculating the weighted_sum, we need to compare it to 
    // the threshold value to determine the thresholded value
    if(weighted_sum >= threshold)
        value = high;
    else
        value = low;
}

/*
    void Neuron::ReadFromFile(istream& in)

    Read data into this Neuron instance from file specified by "in"
*/

void Neuron::ReadFromFile(istream& in)

{
    // id_count and id are already assigned when the Neuron is created
    // from the upper level (NeuronLayer, NeuralNet...)
    // input_link and output_link are also updated in interconnect in NeuralNet
    in >> size_input_link;
    in >> size_output_link;
    in >> threshold;
    in >> high;
    in >> low;
    in >> value;
    in >> error;
}
    
/*
    void Neuron::WriteToFile(ostream& out)

    Write data of this Neuron instance to file specified by "out"
*/

void Neuron::WriteToFile(ostream& out)

{
    // put a blank after each information is essential for
    // ReadFromFile to recognize each field
    out << size_input_link << " ";
    out << size_output_link << " ";
    out << threshold << " ";
    out << high << " ";
    out << low << " ";
    out << value << " ";
    out << error << " ";
}

/*
    void Neuron::Learn(double learning_rate)

    Learn will recalculate the weight of the input Link
    according to the learning rate, its own error and its input Neuron
    value using generalized delta rule.
*/

void Neuron::Learn(double learning_rate)

{
    LinkNode *temp = input_link;
    // loop through each input link of the Neuron
    // reset the weights according to generalized delta rule
    // to reduce the error of this Neuron
    while(temp != NULL)
    {
        // input Neuron value
        double input_value = temp->pLink->GetInputNeuron().GetValue();
        // original weight of the link
        double old_weight = temp->pLink->GetWeight();
        // generalized delta rule
        double new_weight = old_weight + learning_rate * error * input_value;
        // reset the weight of the link according to the calculation above
        temp->pLink->SetWeight(new_weight);

        temp = temp->next;
    }
}


/*
    Static member initialization and function definitions for class Link
*/

int Link::id_count = 0;

/*
    Link::Link()

    Default constructor for a Link. Does not point to any Neuron.
*/
Link::Link()

{
    id = id_count++;
    input = NULL;
    output = NULL;
    weight = 0.0;
}

/*
    Link::Link(const Neuron *Input, const Neuron *Output, double Weight)

    Constructor for a Link.
    Takes Input Neuron, OutputNeuron, and Weight.
*/

Link::Link(
    Neuron *Input,
    Neuron *Output,
    double Weight
)

{
    id = id_count++;
    input = NULL;
    output = NULL;
    weight = 0.0;
}

/*
    Link::~Link()

    Destructor for a Link.
*/
Link::~Link()
{}

/*
    void Link::Print(ostream &out) const

    Print the input Neuron, output Neuron, and the Weight on
    the client_supplied ostream.
*/

void Link::Print(ostream &out) const

{
    out << "Link " << id << endl;
    if(input == NULL)
        out << "\tInput : None" << endl;
    else
        out << "\tInput = " << input->GetID() << endl;

    if(output == NULL)
        out << "\tOutput : None" << endl;
    else
        out << "\tOutput = " << output->GetID() << endl;

    out << "\tWeight = " << weight << endl;
    return;
}

/*
    ID Link::GetID(void) const

    Return the ID of this Link.
*/

ID Link::GetID(void) const

{
    return id;
}

/*
    void Link::SetNeurons(const Neuron &input_Neuron, const Neuron &output_Neuron)

    Set the input and output Neurons of this Link.
*/

void Link::SetNeurons(
    Neuron &input_Neuron,
    Neuron &output_Neuron
)

{
    input = &input_Neuron;
    output = &output_Neuron;
}

/*
    Neuron & Link::GetInputNeuron(void) const

    Return the reference of the input Neruon of this Link.
*/

Neuron &Link::GetInputNeuron(void) const

{
    return *input;
}

/*
    Neuron & Link::GetOutputNeuron(void) const

    Return the reference of the output Neruon of this Link.
*/

Neuron &Link::GetOutputNeuron(void) const

{
    return *output;
}

/*
    void Link::SetWeight(double Weight)

    Set the weight of this link to the client-supplied weight.
*/

void Link::SetWeight(double Weight)

{
    weight = Weight;
    return;
}

/*
    void Link::GetWeight(void) const

    Get the current weight of this link
*/

double Link::GetWeight(void) const

{
    return weight;
}

/*
    double Link::GetWeightedInputValue() const

    Return the value of the input node times the weight of the link.
*/

double Link::GetWeightedInputValue(void) const

{
    return (input->GetValue()) * weight;

}

/*
    void Link::ReadFromFile(istream &in)

    Read weight into this Link from file specified by "in"
*/

void Link::ReadFromFile(istream &in)

{
    in >> weight;
}
    
/*
    void Link::WriteToFile(ostream &out)

    Write weight of this link to file specified by "out"
*/

void Link::WriteToFile(ostream &out)

{
    out << weight << " ";
}

Back    Top