//*******************************************************************************
// CSC 143 Computer Programming II Spring 1999 Instructor: Keith Hughes
//
// Homework 3
// File : \\Venus\katy\CIS143\Homework3\AdalineNetwork\NeuralNet.cpp
//
// Purpose : Inplementation file for class NeuralNet.
//
// Author : Hsin-yi F. Berg
// Date : 5/9/1999 Sun.
// Last Update: 5/28/1999 Fri.
// Update Note: Implementation for NeuralNet class has been moved here
//                 from Layer.h.
//*******************************************************************************

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

// weight of the link, defined in base.h
extern const double WEIGHT;

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

int NeuralNet::id_count = 0;

/*
    NeuralNet::NeuralNet()

    Default constructor for an NeuralNet. NeuralNet is created empty.
*/

NeuralNet::NeuralNet()

{
    id = id_count++;
    numNeuronLayer = 0;
    neuronLayers = NULL;
    linkLayers = NULL;                 
}

/*
    NeuralNet::NeuralNet()

    Copy constructor for an NeuralNet.
*/

NeuralNet::NeuralNet(const NeuralNet &otherNeuralNet)

{
    // call private utility
    clone(otherNeuralNet);
}

/*
    NeuralNet::~NeuralNet()

    Destructor for an NeuralNet.
*/

NeuralNet::~NeuralNet()

{
    // call private utility
    destroy();
}

/*
    NeuralNet::SetHowManyNeuronLayers(int size)

    Set how many neuron layers there are in the NeuralNet.
    Cannot set the number of Neuron Layers to 0.
    The number of LinkLayers will automatically be set to "size - 1".
*/

void NeuralNet::SetHowManyNeuronLayers(int size)

{
    assert(size != 0);             // cannot set 0 Layer
    assert(numNeuronLayer == 0);// This function can only be called once

    numNeuronLayer = size;
    // allocate an array of NeuranLayers with "size" elements
    neuronLayers = new NeuronLayer[numNeuronLayer];
    assert(neuronLayers);
    // allocate an array of LinkLayers with "size - 1" elements
    linkLayers = new LinkLayer[numNeuronLayer - 1];
    assert(linkLayers);
}

/*
    NeuralNet::SetSizeOfNeuronLayer(int whichLayer, int size)

    Set how many neurons a paticular NeuronLayer contains.
    Cannot set the number of Neurons to 0.
*/

void NeuralNet::SetSizeOfNeuronLayer(
    int whichLayer,
    int size
)

{
    assert(size != 0);    // cannot set the size of Layer to 0
    // This function can only get called once                                     
    assert(neuronLayers[whichLayer].GetNumNeuron() == 0);
    // there should be at least one Layer in the NeuralNet.
    // also check if the Layer asking for exists (not out of bound).
    assert(numNeuronLayer > 0 && whichLayer < numNeuronLayer);
    
    // allocate Neurons in the specific NeuronLayer
    neuronLayers[whichLayer].SetHowManyNeurons(size);

    // size of links each LinkLayer contains is "size * size" for fully 
    // connected network, so we don't need to set it.
    // Links in LinkLayers will be allocated in the InterConnect function
}

/*
    void NeuralNet::SetInputValue(int whichNeuron, double Value)

    Set the value of a specific Neuron in input NeuronLayer.
*/

void NeuralNet::SetInputValue(
    int whichNeuron,
    double value
)

{
    // neuronLayers[0] is the input Layer.

    // there should be at least one Neuron in the input Layer
    // also check if the Neuron asking for exists (not out of bound).
    int sizeOfInputLayer = neuronLayers[0].GetNumNeuron();
    assert( sizeOfInputLayer > 0 && whichNeuron < sizeOfInputLayer);

    // We only need to set the value of the input Layer
    neuronLayers[0].SetNeuronValue(whichNeuron, value);
}

/*
    double NeuralNet::GetOutputValue(int whichNeuron)

    Get the value of a specific Neuron in the output NeuronLayer
*/

double NeuralNet::GetOutputValue(int whichNeuron) const

{
    // NeuronLayers[numNeuronLayer - 1] is the output Layer

    // there should be at least one Neuron in the output Layer
    // also check if the Neuron asking for exists (not out of bound).
    int sizeOfOutputLayer = neuronLayers[numNeuronLayer - 1].GetNumNeuron();
    assert( sizeOfOutputLayer > 0 && whichNeuron < sizeOfOutputLayer);

    // We only care about the value in the output Layer
    return neuronLayers[numNeuronLayer - 1].GetNeuronValue(whichNeuron);
}

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

    Print out the internal state of the NeuralNet to client supplied ostream
*/

void NeuralNet::Print(ostream &out) const

{
    if(numNeuronLayer == 0)
        out << "\tNeuron Network is empty." << endl;

    // Print out all the NeuronLayers
    for(int i = 0; i < numNeuronLayer; i ++)
        neuronLayers[i].Print(out);
    // Print out all the LinkLayers
    for(i = 0; i < numNeuronLayer - 1; i++)
        linkLayers[i].Print(out);
}

/*
    void NeuralNet::Fire(void)

    Fire the whole NeuralNet layer by layer.
*/

void NeuralNet::Fire(void)

{
    // Fire each and every Layer
    for(int i = 1; i < numNeuronLayer; i++)
        neuronLayers[i].Fire();
}

/*
    void NeuralNet::InterConnect(void)

    Create a fully connected Network - 
    Eevery neuron in one NeuronLayer is connected to every neuron in
    adjecent NeuronLayer through the link Layer in between.

*/

void NeuralNet::InterConnect(void)

{
    // Go through all the NeuronLayer and LinkLayer
    for(int n = 0; n < numNeuronLayer - 1; n++) {
        // sizein is the size of the previous layer
        int sizein = neuronLayers[n].GetNumNeuron();
        // sizeout is the size of the next layer
        int sizeout = neuronLayers[n+1].GetNumNeuron();
        // allocate right amount of Links in each linkLayer
        // size of LinkLayer between two NeuronLayers is sizein * sizeout
        linkLayers[n].SetHowManyLinks(sizein * sizeout);
        // for each Link of the LinkLayer, connect one Neuron in
        // the previous NeuronLayer and one Neuron in the next NeuronLayer
        // until every Neuron in the previous NeuronLayer is connected with
        // the next NeuronLayer
        for(int i = 0; i < sizein; i++)
            for(int j = 0; j < sizeout; j++)
                Connect(neuronLayers[n].GetNeurons()[i], neuronLayers[n+1].GetNeurons()[j],
                        linkLayers[n].GetLinks()[i * sizeout + j], WEIGHT);
                // the usage of "i*sizeout+j" is a way to treat the link array
                // as a two-dimentional array
    }
}

/*
    NeuralNet &NeuralNet::operator=(const NeuralNet &otherNeuralNet)

    Assignment operator for this NeuralNet.
*/

NeuralNet &NeuralNet::operator=(const NeuralNet &otherNeuralNet)

{
    // Cannot assign ourself to ourself.
    if (this != &otherNeuralNet) {
        // destroy old stuff (if we had any)
        destroy();
        // check if the old stuff is really gone
        assert(neuronLayers == NULL && linkLayers == NULL);
        clone(otherNeuralNet);
    }
    // Give back a reference to ourselves (so we can say a = b = c)
    // in other words - allow cascading
    return *this;
}

/*
    void NeuralNet::clone(const NeuralNet &otherNeuralNet)

    Private Utility: Deep copy of otherNeuralNet.
    This NeuralNet must be cleaned up before calling clone.
*/

void NeuralNet::clone(const NeuralNet &otherNeuralNet)

{
    // make sure this NeuralNet has been cleaned up
    assert(neuronLayers == NULL && linkLayers == NULL);
    // allocate same numbers of NeuronLayers
    SetHowManyNeuronLayers(otherNeuralNet.numNeuronLayer);
    // allocate same numbers of Neurons of each layer
    for(int i = 0; i < numNeuronLayer; i++)
        SetSizeOfNeuronLayer(i, otherNeuralNet.neuronLayers[i].GetNumNeuron());
    // connect the layers, which will allocate same number of LinkLayers and links
    InterConnect();
    // set the weight of each link to WEIGHT
    // SetLinkWeight will set the weight of all Links in a LinkLayer to a certain weight
    for(i = 0; i < numNeuronLayer - 1; i++) {
        linkLayers[i].SetLinkWeight(WEIGHT);
    }
}

/*
    void NeuralNet::destroy(void)

    Private Utility: Clean up this NeuralNet.
    Deallocate all dynamic memory, reinitialize members
*/

void NeuralNet::destroy(void)

{
    // clean up all layers
    if(neuronLayers != NULL)
        delete[] neuronLayers;
    if(linkLayers != NULL)
        delete[] linkLayers;

    // Since this may be called by things other than a destructor,
    // reinitialize the members.
    numNeuronLayer = 0;
    neuronLayers = NULL;
    linkLayers = NULL;
}

/*
    void NeuralNet::Connect(Neuron &input_Neuron,
                            Neuron &output_Neuron,
                            Link &link, double weight)

    Private Utility:
    Connecting a link to its input and output neurons with a specific weight.
*/

void NeuralNet::Connect(
    Neuron &input_Neuron,
    Neuron &output_Neuron,
    Link &link,
    double weight
)

{
    input_Neuron.AddOutput(link);
    output_Neuron.AddInput(link);
    link.SetNeurons(input_Neuron, output_Neuron);
    link.SetWeight(weight);
}

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

    Read data into an empty (newly created) NeuralNet instance
    from file specified by "in".
*/

void NeuralNet::ReadFromFile(istream& in)

{
    // make sure this NeuralNet is empty when it calls this function
    assert(numNeuronLayer == 0);

    // read in how many NeuronLayers there are in the NeuralNet
    int inNumNeuronLayer = 0;
    in >> inNumNeuronLayer;

    // allocate right amount of NeuronLayers according
    // to inNumNeuronLayer
    SetHowManyNeuronLayers(inNumNeuronLayer);

    // now, get the sizes of each NeuronLayer and allocate the right
    // amount of Neurons for each NeuronLayer
    int inNumNeuron = 0;
    for(int i = 0; i < numNeuronLayer; i++) {
        in >> inNumNeuron;
        SetSizeOfNeuronLayer(i, inNumNeuron);
    }

    // interconnect will allocate right amount of LinkLayers and Links
    // and build a fully connected network.
    InterConnect();

    // loop through all the NeuronLayers, call the ReadFromFile
    // function within each NeuronLayer to read in the NeuronLayer
    // information
    for(i = 0; i < numNeuronLayer; i++)
        neuronLayers[i].ReadFromFile(in);

    // loop through all the LinkLayers, call the ReadFromFile
    // function within each LinkLayer to update the weight information
    for(i = 0; i < numNeuronLayer - 1; i++)
        linkLayers[i].ReadFromFile(in);
}
    
/*
    void NeuralNet::WriteToFile(ostream& out)

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

void NeuralNet::WriteToFile(ostream& out)

{
    // first write out how many NeuronLayer there are in the network
    out << numNeuronLayer << " ";
    // then write out the number of Neurons in each NeuronLayer
    for(int i = 0; i < numNeuronLayer; i++)
        out << neuronLayers[i].GetNumNeuron() << " ";

    // loop through all the NeuronLayers, call the WriteToFile
    // function within each NeuronLayer to write out the NeuronLayer
    // information
    for(i = 0; i < numNeuronLayer; i++)
        neuronLayers[i].WriteToFile(out);

    // loop through all the LinkLayers, call the WriteToFile
    // function within each LinkLayer to write out the weight
    // information
    for(i = 0; i < numNeuronLayer - 1; i++)
        linkLayers[i].WriteToFile(out);

}
Back    Top