home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-03-25 | 12.4 KB | 396 lines | [TEXT/CWIE] |
- // ===========================================================================
- // CNeuralNet.cp ©1996 Timo Eloranta
- // ===========================================================================
- // An abstract neural net class - derived from LPeriodical to
- // receive a function call (SpendTime()) at regular intervals
-
- #include "CNeuralNet.h"
- #include "NS_Utils.h"
- #include "CNeuroSimPane.h"
-
- // ---------------------------------------------------------------------------
- // • CNeuralNet
- //
- // Called by: CStdNeuralNet::CStdNeuralNet
- // ---------------------------------------------------------------------------
- // Constructor. Can't be called with 'new', since CNeuralNet is
- // an abstract class.
-
- CNeuralNet::CNeuralNet( Uint16 inSize )
- {
- mSize = inSize;
- mMatrix = NULL;
- mPane = NULL;
-
- mDemoMode = false;
-
- // Put this object to the static Idler queue of LPeriodical.
- // As a result our SpendTime-function starts getting called
- // after every Null Event.
-
- StartIdling();
- }
-
- // ---------------------------------------------------------------------------
- // • GetNeuron
- //
- // Called by: CNeuralNet::InitMatrix, GenerateConnections, etc.
- // CNeuroSimPane::ClickSelf
- // ---------------------------------------------------------------------------
- // The matrix of neurons is implemented as an array. This function
- // returns a pointer to the neuron in the given column & row.
- // Note that there is no bounds checking here because we want this to
- // be as fast as possible. Column and row should both be > 0 and <= mSize !!
-
- inline CNeuronPtr
- CNeuralNet::GetNeuron( Uint16 inCol, Uint16 inRow ) const
- {
- return & mMatrix[ inCol - 1 + ((inRow - 1) * mSize) ];
- }
-
- // ---------------------------------------------------------------------------
- // • RequestDraw
- //
- // Called by: CNeuralNet::ProcessLightQ
- // CStdNeuron::DoClickAction
- // ---------------------------------------------------------------------------
- // Pass the draw request to the pane.
-
- inline void
- CNeuralNet::RequestDraw() const
- {
- mPane -> InvalidateDrawing();
- }
-
- // ---------------------------------------------------------------------------
- // • AddToLightQ
- //
- // Called by: CNeuron::IncState
- // CStdNeuron::DoClickAction
- // ---------------------------------------------------------------------------
- // Add the given neuron pointer (LLink pointer actually) to the end
- // of the "light up queue".
-
- inline void
- CNeuralNet::AddToLightQ( LLink * inNeuron )
- {
- mLightQueue.NextPut( inNeuron );
- }
-
- // ---------------------------------------------------------------------------
- // • ProcessLightQ
- //
- // Called by: CNeuralNet::SpendTime
- // CStdNeuron::DoClickAction
- // ---------------------------------------------------------------------------
- // Here we process every neuron which has found its way to the
- // "light up queue". The main idea is that the processing is done in
- // little groups - "waves". A single wave includes all such neurons
- // which should light up simultaneously because their lighting up is
- // caused by the same neuron or an equally long chain of neurons lighting
- // up... The illusion of simultaneus lighting up is achieved by requesting
- // the pane to be redrawn only after all the neurons in the same wave have
- // been lit up (with the DoLightUpAction function). For a simultaneus
- // fading we use a local "fade queue" to which every neuron is added
- // after it has been lit up. Waves are processed as long as there are
- // new neurons in the "light up queue".
-
- void
- CNeuralNet::ProcessLightQ()
- {
- CNeuronPtr theNeuron;
- Uint16 theWaveSize;
- LQueue theFadeQueue;
- Int32 theDummyTicks;
-
- while ( ! mLightQueue.IsEmpty() ) {
-
- // All neurons which are currently in the queue belong to
- // the same "wave". Here we check the size of the wave.
-
- theWaveSize = mLightQueue.GetSize();
-
- for ( int i = 1; i <= theWaveSize; i++ ) {
- theNeuron = ( CNeuronPtr ) mLightQueue.NextGet();
- theNeuron -> DoLightUpAction();
-
- theFadeQueue.NextPut( theNeuron );
- }
-
- // Force redraw and stop for a while so that the user can
- // enjoy the full effect of the "animation"...
-
- RequestDraw();
- ::Delay( 20, &theDummyTicks );
-
- while ( ! theFadeQueue.IsEmpty() ) {
- theNeuron = ( CNeuronPtr ) theFadeQueue.NextGet();
- theNeuron -> SetPostLightUpState();
- }
-
- RequestDraw();
- }
- }
-
- // ---------------------------------------------------------------------------
- // • InitMatrix
- //
- // Called by: CStdNeuralNet::CStdNeuralNet
- // ---------------------------------------------------------------------------
- // Derived classes should call this function right after CreateMatrix()
-
- void
- CNeuralNet::InitMatrix( const SGenParams & inParams )
- {
- CNeuronPtr theNeuron;
-
- CNeuron::SetNet( this );
-
- for ( int theRow = 1; theRow <= mSize; theRow++ ) {
- for ( int theCol = 1; theCol <= mSize; theCol++ ) {
- theNeuron = GetNeuron( theCol, theRow );
- theNeuron -> SetNeuronPos( theCol, theRow );
-
- GenerateConnections( *theNeuron, inParams );
- }
- }
- }
-
- // ---------------------------------------------------------------------------
- // • ValidLocation
- //
- // Called by: CNeuralNet::GenerateConnections
- // ---------------------------------------------------------------------------
- // Check whether a location (column+row) is inside the matrix bounds.
-
- inline Boolean
- CNeuralNet::ValidLocation( Int16 inCol, Int16 inRow ) const
- {
- return ( inCol > 0 && inCol <= mSize &&
- inRow > 0 && inRow <= mSize );
- }
-
- // ---------------------------------------------------------------------------
- // • GenerateConnections
- //
- // Called by: CNeuralNet::InitMatrix
- // ---------------------------------------------------------------------------
- // Generate random connections for the given neuron (inNeuron).
- // The generation is guided by the parameters (inParams) set by the user.
- // We also set the "connection rect" (mConnRect) of the neuron. This
- // rectangle is big enough to include all the neurons which inNeuron
- // is connected to.
-
- void
- CNeuralNet::GenerateConnections(
- CNeuron & inNeuron,
- const SGenParams & inParams ) const
- {
- Int16 theQuantity,
- theCol, theRow, // Location of inNeuron
- theAvgCol, theAvgRow,
- theNewCol, theNewRow;
- Rect theRect; // Rect which is used for
- // calculating the "connection rect"
-
- theQuantity = RangedRdm( inParams.qtyMin, inParams.qtyMax );
- inNeuron.GetNeuronPos( theCol, theRow );
-
- SetRect ( &theRect, // Left, top, right, bottom...
- theCol, theRow, theCol, theRow );
-
- // Our [1,1] square is in the upper left corner of the matrix, but
- // the user doesn't really know this, so we can set the positive
- // directions to be RIGHT and UP. Therefore we ADD the average
- // "X-length" (columns) and SUBSTRACT the average "Y-length" (rows).
-
- theAvgCol = theCol + inParams.xLengthAvg;
- theAvgRow = theRow - inParams.yLengthAvg;
-
- // The location picked by random must...
- // • ...be a valid location inside the matrix
- // • ...not be the same where inNeuron is situated
- // • ...not be a location to which inNeuron already has a connection
- //
- // Note: If the location is rejected, the loop counter (i) is still
- // incresed by one. In other words, theQuantity is actually
- // the maximum number of connections which is possible, but
- // the neuron gets less connections if any of the randomly
- // picked locations are rejected !!
-
- for ( int i = 1; i <= theQuantity; i++ ) {
-
- theNewCol = RangedRdm( theAvgCol - inParams.xLengthDev,
- theAvgCol + inParams.xLengthDev );
- theNewRow = RangedRdm( theAvgRow - inParams.yLengthDev,
- theAvgRow + inParams.yLengthDev );
-
- if ( ValidLocation( theNewCol, theNewRow ) &&
- ( theNewCol != theCol || theNewRow != theRow ) ) {
-
- CNeuronPtr theNeuron = GetNeuron( theNewCol, theNewRow );
-
- if ( ! inNeuron.IsConnectedTo( theNeuron ) ) {
-
- inNeuron.AddToNeuronList( theNeuron );
-
- // Update the (temporary) connection rect
-
- if ( theNewCol < theRect.left )
- theRect.left = theNewCol;
- if ( theNewCol > theRect.right )
- theRect.right = theNewCol;
- if ( theNewRow < theRect.top )
- theRect.top = theNewRow;
- if ( theNewRow > theRect.bottom )
- theRect.bottom = theNewRow;
- }
- }
- }
-
- inNeuron.SetConnectionRect( theRect );
- }
-
- // ---------------------------------------------------------------------------
- // • GetRandomReceptor
- //
- // Called by: CNeuralNet::SpendTime
- // ---------------------------------------------------------------------------
- // Return a pointer to a random receptor
-
- inline CNeuronPtr
- CNeuralNet::GetRandomReceptor( ) const
- {
- Int16 theRow = RangedRdm( 1, mSize );
- return GetNeuron( 1, theRow );
- }
-
- // ---------------------------------------------------------------------------
- // • SpendTime
- //
- // Called by: LPeriodical::DevoteTimeToIdlers
- // ---------------------------------------------------------------------------
- // This function overrides the pure virtual function in LPeriodical.
- // SpendTime gets called after every single Idle Event (= often...).
- // If there aren't any neurons in the "light up queue" and the demo mode
- // is on, we pick a random receptor and simulate a click on it.
-
- void
- CNeuralNet::SpendTime( const EventRecord & /*inMacEvent*/ )
- {
- CNeuronPtr theNeuron;
- Int32 theDummyTicks;
-
- if (! mLightQueue.IsEmpty())
- ProcessLightQ();
-
- else if ( DemoModeOn() ) {
- ::Delay( 15, &theDummyTicks ); // Wait a moment (1/4 s)...
- theNeuron = GetRandomReceptor();
- theNeuron -> DoClickAction( false );
- }
- }
-
- // ---------------------------------------------------------------------------
- // • SetNeuronsDirty
- //
- // Called by: CStdNeuron::DoClickAction
- // CStdNeuron::DoLightUpAction
- // CStdNeuron::SetPostLightUpState
- // ---------------------------------------------------------------------------
- // Force every neuron inside the given rectangle to be redrawn.
-
- void
- CNeuralNet::SetNeuronsDirty( const Rect & inRect ) const
- {
- for ( Int16 theRow = inRect.top; theRow <= inRect.bottom; theRow++ )
- for ( Int16 theCol = inRect.left; theCol <= inRect.right; theCol++ ) {
-
- CNeuronPtr theNeuron = GetNeuron( theCol, theRow );
-
- theNeuron -> ForceRedraw();
- }
- }
-
- // ---------------------------------------------------------------------------
- // • DrawNeurons
- //
- // Called by: CNeuroSimPane::DrawNeurons
- // ---------------------------------------------------------------------------
- // Draw all the neurons which need redrawing. Any neuron's location
- // on the screen is calculated from the location of the neuron in the
- // first column of the first row (inOneOneRect).
-
- void
- CNeuralNet::DrawNeurons(
- const Rect &inOneOneRect,
- Uint16 inSquareSize ) const
- {
- Rect theNeuronRect;
- CNeuronPtr theNeuron;
-
- for ( Int16 theRow = 1; theRow <= mSize; theRow++ )
- for ( Int16 theCol = 1; theCol <= mSize; theCol++ ) {
-
- theNeuron = GetNeuron( theCol, theRow );
-
- if ( theNeuron -> RedrawNeeded() ) {
- theNeuronRect = inOneOneRect;
-
- ::OffsetRect( &theNeuronRect,
- (theCol - 1) * inSquareSize,
- (theRow - 1) * inSquareSize );
-
- theNeuron -> Draw( theNeuronRect );
- }
- }
- }
-
- // ---------------------------------------------------------------------------
- // • DrawConnections
- //
- // Called by: CNeuroSimPane::DrawConnections
- // ---------------------------------------------------------------------------
- // Tell every neuron to redraw its connections - if necessary.
-
- void
- CNeuralNet::DrawConnections() const
- {
- for ( Int16 theRow = 1; theRow <= mSize; theRow++ )
- for ( Int16 theCol = 1; theCol <= mSize; theCol++ ) {
-
- CNeuronPtr theNeuron = GetNeuron( theCol, theRow );
-
- if ( theNeuron -> ConnectionsDirty() )
- theNeuron -> DrawConnections();
- }
- }
-
- // ---------------------------------------------------------------------------
- // • SetCenterPoints
- //
- // Called by: CNeuroSimPane::SetNet
- // ---------------------------------------------------------------------------
- // This function must be called before calling the DrawConnections
- // function for the first time. Here we set the center points of the
- // neurons. We do this so that we don't have to calculate the center
- // points every time we draw the connections...
-
- void
- CNeuralNet::SetCenterPoints( Uint16 inOneOneXY, Uint16 inSquareSize) const
- {
- Point theCenter;
-
- for ( Int16 theRow = 1; theRow <= mSize; theRow++ ) {
- for ( Int16 theCol = 1; theCol <= mSize; theCol++ ) {
-
- CNeuronPtr theNeuron = GetNeuron( theCol, theRow );
-
- theCenter.h = inOneOneXY + (theCol - 1) * inSquareSize;
- theCenter.v = inOneOneXY + (theRow - 1) * inSquareSize;
-
- theNeuron -> SetCenterPoint( theCenter );
- }
- }
- }
-