using System;
using SharpUtils;
using System.Collections;
using System.Xml;
namespace Neural_Net_Library
{
/// <summary>
/// Node Class for the self organizing network
/// </summary>
public class SelfOrganizingNetworkNode : BasicNode
{
private DebugLevel debugLevel;
private Logger log;
/// <summary>
/// most basic constructor just taking the logger
/// </summary>
/// <param name="log">log file</param>
public SelfOrganizingNetworkNode( Logger log ) : base( log )
{
debugLevel = new DebugLevel( DebugLevel.currentLevel );
this.log = log;
}
/// <summary>
/// constructor taking the logger and array sizes
/// </summary>
/// <param name="log">log file writer</param>
/// <param name="nNodeValueSize">initial array size for the node values</param>
/// <param name="nNodeErrorSize">initial array size for the node errors</param>
public SelfOrganizingNetworkNode( Logger log, int nNodeValueSize, int nNodeErrorSize ) : base( log, nNodeValueSize, nNodeErrorSize )
{
debugLevel = new DebugLevel( DebugLevel.currentLevel );
this.log = log;
}
/// <summary>
/// constructor taking the logger and initial learning rate
/// </summary>
/// <param name="log">log file writer</param>
/// <param name="dLearningRate">initial learning rate</param>
public SelfOrganizingNetworkNode( Logger log, double dLearningRate ) : base( log, 2, 1 )
{
debugLevel = new DebugLevel( DebugLevel.currentLevel );
this.log = log;
this.SetLearningRate( dLearningRate );
}
/// <summary>
/// set the learningrate for the node
/// </summary>
/// <param name="dLearningRate">new learning rate</param>
public void SetLearningRate( double dLearningRate )
{
this.NodeValues[ Values.LearningRate ] = dLearningRate;
}
/// <summary>
/// run the current node
/// </summary>
/// <param name="nMode"></param>
public override void Run(int nMode)
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Run function called for ", ClassName );
}
double dTotal = 0.0;
double dTemp = 0.0;
for( int i=0; i<this.InputLinks.Count; i++ )
{
dTemp = ( ( BasicLink )this.InputLinks[ i ] ).InputNode.GetValue( nMode ) - ( ( BasicLink )this.InputLinks[ i ] ).GetLinkValue( Values.Weight );
/// get the power to 2 of the nodevalue - the weight
dTotal += Math.Pow( dTemp, 2 );
}
/// store the square root of all the values
this.NodeValues[ Values.NodeValue ] = Math.Sqrt( dTotal );
}
/// <summary>
/// perform learn for the current node
/// </summary>
/// <param name="nMode"></param>
public override void Learn(int nMode)
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "learn called for ", ClassName );
}
double dDelta = 0.0;
for( int i=0; i<this.InputLinks.Count; i++ )
{
dDelta = ( ( double )this.NodeValues[ Values.LearningRate ] ) + ( ( BasicLink )this.InputLinks[ i ] ).GetLinkValue( Values.Weight );
( ( BasicLink )this.InputLinks[ i ] ).SetLinkValue( dDelta, Values.Weight );
}
}
/// <summary>
/// save this node
/// </summary>
/// <param name="xmlWriter">xml file to write to</param>
public override void Save(System.Xml.XmlWriter xmlWriter)
{
xmlWriter.WriteStartElement( "SelfOrganizingNetworkNode" );
base.Save (xmlWriter);
xmlWriter.WriteEndElement();
}
/// <summary>
/// load the saved data for this node
/// </summary>
/// <param name="xmlReader">xml file to be read from</param>
public override void Load(System.Xml.XmlReader xmlReader)
{
bool bBreak = false;
for( ;; )
{
xmlReader.Read();
switch( xmlReader.Name )
{
case "BasicNode": base.Load( xmlReader ); bBreak = true; break;
}
if( bBreak == true )
break;
}
}
public new string ClassName
{
get
{
return this.ToString();
}
}
}
/// <summary>
/// link class for the self organizing network
/// </summary>
public class SelfOrganizingNetworkLink : BasicLink
{
private DebugLevel debugLevel;
private Logger log;
/// <summary>
/// constructor for the link
/// </summary>
/// <param name="log"></param>
public SelfOrganizingNetworkLink( Logger log ) : base( log )
{
debugLevel = new DebugLevel( DebugLevel.currentLevel );
this.log = log;
this.arrayLinkValues[ Values.Weight ] = Values.Random( 0.0, 1.0 );
}
/// <summary>
/// Save the current object
/// </summary>
/// <param name="xmlWriter">xml file to write to</param>
public override void Save(System.Xml.XmlWriter xmlWriter)
{
xmlWriter.WriteStartElement( "SelfOrganizingNetworkLink" );
base.Save (xmlWriter);
xmlWriter.WriteEndElement();
}
/// <summary>
/// load the data into the current object
/// </summary>
/// <param name="xmlReader"></param>
public override void Load(System.Xml.XmlReader xmlReader)
{
bool bBreak = false;
for( ;; )
{
xmlReader.Read();
switch( xmlReader.Name )
{
case "BasicLink": base.Load( xmlReader ); bBreak = true; break;
}
if( bBreak == true )
break;
}
}
public new string ClassName
{
get
{
return this.ToString();
}
}
}
/// <summary>
/// The network class for the self organizing network
/// </summary>
public class SelfOrganizingNetwork : BasicNetwork
{
private int nHorizontalSize;
private int nVerticalSize;
private double dInitialLearningRate;
private double dFinalLearningRate;
private int nInitialNeighborhoodSize;
private int nFinalNeighborhoodSize;
private int nNeighborhoodDecrement;
private int nNeighborhoodSize;
private int nNumberOfIterations;
private int nIterations;
private int nWinningHorizontalPos;
private int nWinningVerticalPos;
private int nNumberOfNodes;
private SelfOrganizingNetworkNode[][] arrayKohonenLayer = null;
private DebugLevel debugLevel;
private Logger log;
public int HorizontalSize
{
get
{
return nHorizontalSize;
}
set
{
nHorizontalSize = value;
}
}
public int VerticalSize
{
get
{
return nVerticalSize;
}
set
{
nVerticalSize = value;
}
}
public double InitialLearningRate
{
get
{
return dInitialLearningRate;
}
set
{
dInitialLearningRate = value;
}
}
public double FinalLearningRate
{
get
{
return dFinalLearningRate;
}
set
{
dFinalLearningRate = value;
}
}
public int InitialNeighborhoodSize
{
get
{
return nInitialNeighborhoodSize;
}
set
{
nInitialNeighborhoodSize = value;
}
}
public int FinalNeighborhoodSize
{
get
{
return nFinalNeighborhoodSize;
}
set
{
nFinalNeighborhoodSize = value;
}
}
public int NeighborhoodDecrement
{
get
{
return nNeighborhoodDecrement;
}
set
{
nNeighborhoodDecrement = value;
}
}
public int NeighborhoodSize
{
get
{
return nNeighborhoodSize;
}
set
{
nNeighborhoodSize = value;
}
}
public int Iterations
{
get
{
return nIterations;
}
set
{
nIterations = value;
}
}
public int NumberOfIterations
{
get
{
return nNumberOfIterations;
}
set
{
nNumberOfIterations = value;
}
}
public int WinningHorizontalPos
{
get
{
return nWinningHorizontalPos;
}
set
{
nWinningHorizontalPos = value;
}
}
public int WinningVerticalPos
{
get
{
return nWinningVerticalPos;
}
set
{
nWinningVerticalPos = value;
}
}
public int NumberOfInputNodes
{
get
{
return nNumberOfNodes;
}
}
public new string ClassName
{
get
{
return this.ToString();
}
}
/// <summary>
/// most basic constructor
/// </summary>
/// <param name="log"></param>
public SelfOrganizingNetwork( Logger log ) : base( log )
{
debugLevel = new DebugLevel( DebugLevel.currentLevel );
this.log = log;
}
/// <summary>
/// Self organizing network constructor
/// </summary>
/// <param name="log">log file to write to</param>
/// <param name="nNumberOfInputs">The number of network inputs</param>
/// <param name="nHorizontalSize">The Horizontal size of the network</param>
/// <param name="nVerticalSize">The vertical size of the network</param>
/// <param name="dInitialLearningRate">The Initial Learning Rate for the network</param>
/// <param name="dFinalLearningRate">The final learning rate for the network</param>
/// <param name="nIntialNeighborSize">The initial neighborhood size </param>
/// <param name="nNeighborDecrement">The neighborhood size decrement</param>
/// <param name="nIterations">The number of iterations to perform for the network</param>
public SelfOrganizingNetwork( Logger log, int nNumberOfInputs, int nHorizontalSize, int nVerticalSize, double dInitialLearningRate, double dFinalLearningRate, int nInitialNeighborSize, int nFinalNeighborhoodSize, int nNeighborDecrement, int nIterations ) : base( log )
{
debugLevel = new DebugLevel( DebugLevel.currentLevel );
this.log = log;
nNumberOfNodes = nNumberOfInputs;
this.HorizontalSize = nHorizontalSize;
this.VerticalSize = nVerticalSize;
this.InitialLearningRate = dInitialLearningRate;
this.LearningRate = dInitialLearningRate;
this.FinalLearningRate = dFinalLearningRate;
this.InitialNeighborhoodSize = nInitialNeighborSize;
this.FinalNeighborhoodSize = nFinalNeighborhoodSize;
this.NeighborhoodSize = nInitialNeighborhoodSize;
this.NeighborhoodDecrement = nNeighborDecrement;
this.NumberOfIterations = nIterations;
this.Iterations = 0;
CreateNetwork();
}
/// <summary>
/// Create the self organizing network
/// </summary>
protected override void CreateNetwork()
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Create Network called for the Self Organizing Network ", ClassName );
}
/// create the array
this.arrayKohonenLayer = new SelfOrganizingNetworkNode[ this.HorizontalSize ][];
for( int i=0; i<this.HorizontalSize; i++ )
{
this.arrayKohonenLayer[ i ] = new SelfOrganizingNetworkNode[ this.VerticalSize ];
}
/// create the input nodes ( In the basic network nodes array )
for( int i=0; i<this.nNumberOfNodes; i++ )
{
this.Nodes.Add( new BasicNode( log ) );
}
int nLinks = 0;
/// loop through the horizontal
for( int i=0; i<this.HorizontalSize; i++ )
{
/// loop through the vertical
for( int n=0; n<this.VerticalSize; n++ )
{
this.arrayKohonenLayer[ i ][ n ] = new SelfOrganizingNetworkNode( log, LearningRate );
/// connect each input node to each node in the k layer
for( int k=0; k<this.nNumberOfNodes; k++ )
{
this.Links.Add( new SelfOrganizingNetworkLink( log ) );
( ( BasicNode )this.Nodes[ k ] ).CreateLink( ( BasicNode )this.arrayKohonenLayer[ i ][ n ], ( BasicLink )this.Links[ nLinks ] );
nLinks++;
}
}
}
}
/// <summary>
/// Run the Self organizing Network
/// </summary>
public void Run()
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Run called for Self Organizing network ", ClassName );
}
int nHoriz = 0;
int nVert = 0;
double dMinimum = 99999.0;
double dNodeValue = 0.0;
for( nHoriz=0; nHoriz<this.HorizontalSize; nHoriz++ )
{
for( nVert=0; nVert<this.VerticalSize; nVert++ )
{
( ( SelfOrganizingNetworkNode )this.arrayKohonenLayer[ nHoriz ][ nVert ] ).Run( Values.NodeValue );
dNodeValue = ( ( SelfOrganizingNetworkNode )this.arrayKohonenLayer[ nHoriz ][ nVert ] ).GetValue( Values.NodeValue );
if( dNodeValue < dMinimum )
{
dMinimum = dNodeValue;
this.WinningHorizontalPos = nHoriz;
this.WinningVerticalPos = nVert;
}
}
}
}
/// <summary>
/// Call Learn on the required nodes
/// </summary>
public void Learn()
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Learn called for Self Organizing network ", ClassName );
}
int nHoriz = 0;
int nVert = 0;
/// work out the neighborhood boundary
int nHorizStart = this.WinningHorizontalPos - this.NeighborhoodSize;
int nHorizStop = this.WinningHorizontalPos + this.NeighborhoodSize;
int nVertStart = this.WinningVerticalPos - this.NeighborhoodSize;
int nVertStop = this.WinningVerticalPos + this.NeighborhoodSize;
/// make sure the boundary is within the kohonen layer
if( nHorizStart < 0 )
nHorizStart = 0;
if( nHorizStop >= this.HorizontalSize )
nHorizStop = this.HorizontalSize;
if( nVertStart < 0 )
nVertStart = 0;
if( nVertStop >= this.VerticalSize )
nVertStop = this.VerticalSize;
/// update the neighbors of the winning node
for( nHoriz=nHorizStart; nHoriz<nHorizStop; nHoriz++ )
{
for( nVert=nVertStart; nVert<nVertStop; nVert++ )
{
( ( SelfOrganizingNetworkNode )this.arrayKohonenLayer[ nHoriz ][ nVert ] ).SetValue( Values.LearningRate, this.LearningRate );
( ( SelfOrganizingNetworkNode )this.arrayKohonenLayer[ nHoriz ][ nVert ] ).Learn( Values.NodeValue );
}
}
}
/// <summary>
/// The epoch function is overriden to update the learningrate
/// </summary>
/// <param name="nMode"></param>
public override void Epoch()
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Epoch called for the Self organizing network ", ClassName );
}
Iterations++;
double fTemp = ( ( InitialLearningRate - ( ( double )Iterations/( double )NumberOfIterations ) ) );
fTemp = fTemp * ( double )( InitialLearningRate - FinalLearningRate );
if( fTemp < FinalLearningRate )
fTemp = FinalLearningRate;
LearningRate = fTemp;
if( ( Iterations + 1 )%NeighborhoodDecrement == 0 && NeighborhoodSize > 0 )
{
NeighborhoodSize--;
if( NeighborhoodSize < FinalNeighborhoodSize )
NeighborhoodSize = FinalNeighborhoodSize;
}
}
/// <summary>
/// save the current network
/// </summary>
/// <param name="xmlWriter">xml file to be written to</param>
public override void Save(System.Xml.XmlWriter xmlWriter)
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Save Node Links called for basic network ", ClassName );
}
xmlWriter.WriteStartElement( "SelfOrganizingNetwork" );
xmlWriter.WriteElementString( "HorizontalSize", HorizontalSize.ToString() );
xmlWriter.WriteElementString( "VerticalSize", VerticalSize.ToString() );
xmlWriter.WriteElementString( "InitialLearningRate", InitialLearningRate.ToString() );
xmlWriter.WriteElementString( "LearningRate", LearningRate.ToString() );
xmlWriter.WriteElementString( "FinalLearningRate", FinalLearningRate.ToString() );
xmlWriter.WriteElementString( "InitialNeighborhoodSize", InitialNeighborhoodSize.ToString() );
xmlWriter.WriteElementString( "FinalNeighborhoodSize", this.FinalNeighborhoodSize.ToString() );
xmlWriter.WriteElementString( "NeighborhoodDecrement", NeighborhoodDecrement.ToString() );
xmlWriter.WriteElementString( "NeighborhoodSize", this.NeighborhoodSize.ToString() );
xmlWriter.WriteElementString( "NumberOfIterations", this.NumberOfIterations.ToString() );
xmlWriter.WriteElementString( "Iterations", this.Iterations.ToString() );
xmlWriter.WriteElementString( "WinningHorizontalPosition", this.WinningHorizontalPos.ToString() );
xmlWriter.WriteElementString( "WinningVerticalPosition", this.WinningVerticalPos.ToString() );
xmlWriter.WriteElementString( "NumberOfNodes", this.nNumberOfNodes.ToString() );
xmlWriter.WriteStartElement( "InputLayer" );
for( int i=0; i<this.nNumberOfNodes; i++ )
{
this.GetNodeAt( i ).Save( xmlWriter );
}
xmlWriter.WriteEndElement();
xmlWriter.WriteStartElement( "KohonenLayer" );
for( int i=0; i<this.HorizontalSize; i++ )
{
for( int n=0; n<this.VerticalSize; n++ )
{
( ( SelfOrganizingNetworkNode )this.arrayKohonenLayer[ i ][ n ] ).Save( xmlWriter );
}
}
for( int i=0; i<this.Links.Count; i++ )
{
( ( SelfOrganizingNetworkLink )this.Links[ i ] ).Save( xmlWriter );
}
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
}
/// <summary>
/// load the self organizing network
/// </summary>
/// <param name="xmlReader"></param>
public override void Load(System.Xml.XmlReader xmlReader)
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Progress ) == true )
{
log.Log( DebugLevelSet.Progress, "Load Node links called for Backpropagation network", ClassName );
}
bool bBreak = false;
this.Nodes.Clear();
this.Links.Clear();
if( this.arrayKohonenLayer != null )
{
for( int i=0; i<this.HorizontalSize; i++ )
{
Array.Clear( arrayKohonenLayer, i, this.VerticalSize );
}
}
try
{
while( xmlReader.Name != "SelfOrganizingNetwork" )
{
xmlReader.Read();
}
while( xmlReader.Name != "HorizontalSize" )
{
xmlReader.Read();
}
xmlReader.Read();
this.HorizontalSize = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "VerticalSize" )
{
xmlReader.Read();
}
xmlReader.Read();
this.VerticalSize = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "InitialLearningRate" )
{
xmlReader.Read();
}
xmlReader.Read();
this.InitialLearningRate = Double.Parse( xmlReader.Value );
while( xmlReader.Name != "LearningRate" )
{
xmlReader.Read();
}
xmlReader.Read();
this.LearningRate = Double.Parse( xmlReader.Value );
while( xmlReader.Name != "FinalLearningRate" )
{
xmlReader.Read();
}
xmlReader.Read();
this.FinalLearningRate = Double.Parse( xmlReader.Value );
while( xmlReader.Name != "InitialNeighborhoodSize" )
{
xmlReader.Read();
}
xmlReader.Read();
this.InitialNeighborhoodSize = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "FinalNeighborhoodSize" )
{
xmlReader.Read();
}
xmlReader.Read();
this.FinalNeighborhoodSize = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "NeighborhoodDecrement" )
{
xmlReader.Read();
}
xmlReader.Read();
this.NeighborhoodDecrement = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "NeighborhoodSize" )
{
xmlReader.Read();
}
xmlReader.Read();
this.NeighborhoodSize = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "NumberOfIterations" )
{
xmlReader.Read();
}
xmlReader.Read();
this.NumberOfIterations = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "Iterations" )
{
xmlReader.Read();
}
xmlReader.Read();
this.Iterations = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "WinningHorizontalPosition" )
{
xmlReader.Read();
}
xmlReader.Read();
this.WinningHorizontalPos = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "WinningVerticalPosition" )
{
xmlReader.Read();
}
xmlReader.Read();
this.WinningVerticalPos = Int32.Parse( xmlReader.Value );
while( xmlReader.Name != "NumberOfNodes" )
{
xmlReader.Read();
}
xmlReader.Read();
this.nNumberOfNodes = Int32.Parse( xmlReader.Value );
/// go to the start of the nodes
while( xmlReader.Name != "InputLayer" )
{
xmlReader.Read();
}
/// set up the array
this.arrayKohonenLayer = new SelfOrganizingNetworkNode[ this.HorizontalSize ][];
for( int i=0; i<this.HorizontalSize; i++ )
{
this.arrayKohonenLayer[ i ] = new SelfOrganizingNetworkNode[ this.VerticalSize ];
}
int nCurrentHorizontalPosition = 0;
int nCurrentVerticalPosition = 0;
for( ;; )
{
xmlReader.Read();
switch( xmlReader.NodeType )
{
case XmlNodeType.Element:
{
switch( xmlReader.Name )
{
case "BasicNode":
{
BasicNode temp = new BasicNode( log );
temp.Load( xmlReader );
this.Nodes.Add( temp );
} break;
case "SelfOrganizingNetworkNode":
{
SelfOrganizingNetworkNode temp = new SelfOrganizingNetworkNode( log, LearningRate );
temp.Load( xmlReader );
if( nCurrentVerticalPosition == this.VerticalSize )
{
nCurrentVerticalPosition = 0;
nCurrentHorizontalPosition++;
}
this.arrayKohonenLayer[ nCurrentHorizontalPosition ][ nCurrentVerticalPosition ] = temp;
nCurrentVerticalPosition++;
} break;
case "SelfOrganizingNetworkLink": bBreak = true; break;
}
} break;
}
if( bBreak == true )
break;
}
bBreak = false;
for( ;; )
{
switch( xmlReader.NodeType )
{
case XmlNodeType.Element:
{
switch( xmlReader.Name )
{
case "SelfOrganizingNetworkLink":
{
SelfOrganizingNetworkLink temp = new SelfOrganizingNetworkLink( log );
temp.Load( xmlReader );
this.Links.Add( temp );
} break;
}
} break;
case XmlNodeType.EndElement:
{
switch( xmlReader.Name )
{
case "SelfOrganizingNetwork": bBreak = true; break;
} break;
}
}
if( bBreak == true )
break;
xmlReader.Read();
}
/// build the links
int nLinks = 0;
for( int i=0; i<this.HorizontalSize; i++ )
{
/// loop through the vertical
for( int n=0; n<this.VerticalSize; n++ )
{
/// connect each input node to each node in the k layer
for( int k=0; k<this.nNumberOfNodes; k++ )
{
( ( BasicNode )this.Nodes[ k ] ).CreateLink( ( BasicNode )this.arrayKohonenLayer[ i ][ n ], ( BasicLink )this.Links[ nLinks ] );
nLinks++;
}
}
}
}
catch( ArgumentNullException argNullExp )
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Errors ) == true )
{
log.Log( DebugLevelSet.Errors, "Argument Null exception thrown loading Self Organizing network, reason - " + argNullExp.Message, ClassName );
}
return;
}
catch( FormatException formExp )
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Errors ) == true )
{
log.Log( DebugLevelSet.Errors, "Format Exception thrown loading Self organizing network, reason - " + formExp.Message, ClassName );
}
return;
}
catch( OverflowException overExp )
{
if( debugLevel.TestDebugLevel( DebugLevelSet.Errors ) == true )
{
log.Log( DebugLevelSet.Errors, " Overflow exception thrown loading Self Organinzing Network, reason - " + overExp.Message );
}
return;
}
}
/// <summary>
/// Gets the winning position be it row or column composite returns a unique number for the rows position
/// </summary>
/// <param name="nID">Type to return Values.Row, Values.Column or Values.Composite</param>
/// <returns>a double depending on the value of nID</returns>
public double GetPosition( int nID )
{
switch( nID )
{
case Values.Row: return WinningHorizontalPos;
case Values.Column: return WinningVerticalPos;
default: return WinningHorizontalPos * VerticalSize + WinningVerticalPos;
}
}
public double GetWinningNodeValue( int nHorizontalPos, int nVerticalPos )
{
return ( double )( ( SelfOrganizingNetworkNode )this.arrayKohonenLayer[ nHorizontalPos ][ nVerticalPos ] ).NodeValues[ Values.NodeValue ];
}
}
}