VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "NetFast"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
#Const ShowDebugTimes = 0

'----------------------------------------------------------------------------------------------------------------
' Neural Net Code and classes originally by Ulli (umgedv@aol.com)
' Conversion to ActiveX Object Library and other improvements
' by Jonathan Daniel (bigcalm@hotmail.com)
' This class is an array based version of a neural net.
'
' This code is shareware.  Please credit the authors if you use this code.
'
' Bugs, fixes, improvements, and suggestions to bigcalm@hotmail.com
'
'----------------------------------------------------------------------------------------------------------------
' Many thanks to Chikh for all his help.
' Some code was adapted from Jason Tiscione's java code.
' Urgh my brain hurts after reading the neural net FAQ at faqs.org


Private Declare Function GetTickCount Lib "kernel32" () As Long

Private Type Synapse
  TransferWeight As Double
End Type

Private Type Neuron
  Value As Double
  Error As Double
  BaseValue As Double
End Type

Private Type Layer
  StartNeuron As Long
  EndNeuron As Long
  StartDendrite As Long
  EndDendrite As Long
  NeuronCount As Long
  DendriteCount As Long
End Type

Private StartTime As Long

' Constants for load/save.
Private Const OCXNAME As String = "Perceptron"
Private Const VERSION As String = "1.3"

' Main objects
Private Dendrites() As Synapse
Private Neurons() As Neuron
Private Layers() As Layer

' From cNet
Private mvarCreated As Boolean
Private mTag As Variant
Private mTrainingCycles As Long
Private mLearningCoefficient As Double
Private mLearningRateIncrease As Double
Private mLearningRateDecrease As Double
'Private mMaximumErrorRate As Double
Private mAnnealingEpoch As Long
Private mAnnealingSSE As Double
Private mLastAnnealingSSE As Double
Private mSumSquaredError As Double
Private mRunning As Boolean ' Because we're allowing the caller to utilise
  ' DoEvents when we Raise an event, we need a Boolean value to stop
  ' something being called twice (or say Destroy being called while we're training).
  ' Also allow owner to read this property if they want.
Private mStopping As Boolean ' Set this flag to abort current procedure.
      ' Set by calling StopWorking.  Not available as a property (yet).
' Events
' These three events will be raised so the controlling application has information
' on what's going on.  It'll also give the controlling app a chance to do things
' like DoEvents for example.
' These are useful for progress bars/panels/etc.  Just define your net "WithEvents"
' to use these.
Public Event InfoMessage(vTag As Variant, Info As String)
Public Event Progress(vTag As Variant, Percentage As Single)
'
' Example use for Progress...
' Private WithEvents MyNet as New cNet
' Private Sub Form_Click()
'   If MyNet.Running = True Then
'     Exit Sub
'   End If
'   ProgressBar1.Visible = True
'   ProgressBar1.Min = 0
'   ProgressBar1.Max = 0
'   ProgressBar1.Value = 0
'   MyNet.CreateNet(Array(60,8,2))
'   ProgressBar1.Visible = False
' End Sub
' Private Sub MyNet_Progress(vTag as Variant, Percentage as Single)
'   ProgressBar1.Value = Percentage
'   DoEvents
' End Sub

' Property Let/Get/Sets. from cNet
Public Property Get Created() As Boolean
  Created = mvarCreated
End Property
Friend Property Let Created(ByVal vData As Boolean)
  mvarCreated = vData
End Property
Public Property Get TrainingCycles() As Long
  TrainingCycles = mTrainingCycles
End Property
Friend Property Let TrainingCycles(ByVal vData As Long)
  mTrainingCycles = vData
End Property
Public Property Let LearningCoefficient(ByVal vData As Double)
  mLearningCoefficient = vData
End Property
Public Property Get LearningCoefficient() As Double
  LearningCoefficient = mLearningCoefficient
End Property
Public Property Let LearningRateIncrease(ByVal vData As Double)
  mLearningRateIncrease = vData
End Property
Public Property Get LearningRateIncrease() As Double
  LearningRateIncrease = mLearningRateIncrease
End Property
Public Property Let LearningRateDecrease(ByVal vData As Double)
  mLearningRateDecrease = vData
End Property
Public Property Get LearningRateDecrease() As Double
  LearningRateDecrease = mLearningRateDecrease
End Property
'Public Property Let MaximumErrorRate(ByVal vData As Double)
'  mMaximumErrorRate = vData
'End Property
'Public Property Get MaximumErrorRate() As Double
'  MaximumErrorRate = mMaximumErrorRate
'End Property

Public Property Get SumSquaredError() As Double
  SumSquaredError = mSumSquaredError
End Property
Friend Property Let SumSquaredError(ByVal vData As Double)
  mSumSquaredError = vData
End Property

' This should properly be called "Mean Square Error"
Public Property Get AverageSquaredError() As Double
  If mvarCreated = True Then
    AverageSquaredError = mSumSquaredError / Layers(UBound(Layers)).NeuronCount
  Else
    AverageSquaredError = 0
  End If
End Property
Public Property Get AnnealingEpoch() As Long
  AnnealingEpoch = mAnnealingEpoch
End Property
Public Property Let AnnealingEpoch(ByVal vData As Long)
  mAnnealingEpoch = vData
End Property

Public Property Get Tag() As Variant
  If IsObject(mTag) Or mTag Is Nothing Then
    Set Tag = mTag
  Else
    Tag = mTag
  End If
End Property
Public Property Set Tag(ByVal vData As Variant)
  Set mTag = vData
End Property
Public Property Let Tag(ByVal vData As Variant)
  mTag = vData
End Property

Public Property Get Running() As Boolean
  Running = mRunning
End Property
Private Property Let Running(ByVal vData As Boolean)
  mRunning = vData
End Property
Public Property Get TotalNeuronCount() As Long
  TotalNeuronCount = UBound(Neurons) - LBound(Neurons) + 1
End Property

Public Property Get OutputLayer(Index As Long) As Double
  If mvarCreated = True Then
    OutputLayer = Neurons(Layers(UBound(Layers)).StartNeuron + Index - 1).Value
  Else
    Err.Raise vbObjectError + 2, "Perceptron", "You must initialise your net before you attempt to retrieve output from it"
    OutputLayer = 0
  End If
End Property

Public Sub SetInput(Data As Variant)
Dim i As Long

    If mStopping = True Then
      Exit Sub
    End If
    
    ' error checking on passed Data here:
    
    RaiseEvent InfoMessage(mTag, "Initialising Input Layer")
    
    For i = Layers(LBound(Layers)).StartNeuron To Layers(LBound(Layers)).EndNeuron
        Neurons(i).Value = CDbl(Data(i))
    Next i
End Sub

' This can be publicly called - the function that actually does the work is a hidden
' procedure called CalculateOutput (below).
Public Sub ProcessOutput()
  If mvarCreated = False Then
    Err.Raise vbObjectError + 2, "Perceptron", "You must initialise your net before you attempt to retrieve output from it"
    Exit Sub
  End If
  If mRunning = True Then
    Exit Sub
  End If
  #If ShowDebugTimes = 1 Then
  StartTime = GetTickCount
  #End If
  mRunning = True
  mStopping = False
  CalculateOutput
  #If ShowDebugTimes = 1 Then
  Debug.Print "Time to process output: " & GetTickCount - StartTime & " ms"
  #End If
  mRunning = False
End Sub

Friend Sub CalculateOutput()
Dim i As Long, j As Long, k As Long
Dim TotalToDo As Long
Dim Percentage As Long
Dim WorkDone As Long
Dim PrevNLayerPtr As Long
  
  ' Calculate work to be done
  TotalToDo = UBound(Neurons) - Layers(LBound(Layers) + 1).StartNeuron + 1
  Percentage = TotalToDo / 100
  WorkDone = 0
  
  ' Apologies for the complexity of this - whenever I've seen nn code before, it's
  ' always too complex to understand.  Hence the original OO stuff adapted from
  ' Ulli.  However, I've gone back to complexity to save on speed. :-(
  
  ' Go through all the layers except the first one
  For i = LBound(Layers) + 1 To UBound(Layers)
    RaiseEvent InfoMessage(mTag, "Calculating Output For Layer " & i)
    ' Go through all neurons in this layer
    For j = Layers(i).StartNeuron To Layers(i).EndNeuron
      ' for each neuron, sum the total of it's inputs from the previous layer.
      Neurons(j).Value = 0
      PrevNLayerPtr = Layers(i - 1).StartNeuron
      For k = Layers(i).StartDendrite + ((j - Layers(i).StartNeuron) * Layers(i - 1).NeuronCount) To Layers(i).StartDendrite + ((j - Layers(i).StartNeuron) * Layers(i - 1).NeuronCount) + Layers(i - 1).NeuronCount
        Neurons(j).Value = Neurons(j).Value + (Neurons(PrevNLayerPtr).Value * Dendrites(k).TransferWeight)
        PrevNLayerPtr = PrevNLayerPtr + 1
      Next k
      ' sigmoid squash
      Neurons(j).Value = Squish(Neurons(j).Value + Neurons(j).BaseValue)
      WorkDone = WorkDone + 1
      If WorkDone Mod Percentage = 0 Then
        RaiseEvent Progress(mTag, (WorkDone / TotalToDo) * 100)
      End If
      If mStopping = True Then
        Exit For
      End If
    Next j
    If mStopping = True Then
      Exit For
    End If
  Next i
End Sub

Private Sub AdjustWeights(Target As Variant)
  Dim i As Long, j As Long, k As Long
Dim Percentage As Long
Dim WorkDone As Long
Dim TotalToDo As Long
Dim SSE As Double
Dim PrevNLayerPtr As Long

    j = 0
    RaiseEvent InfoMessage(mTag, "Training the Net")
    
    'calculation of raw error in output layer
    SSE = 0
    For i = Layers(UBound(Layers)).StartNeuron To Layers(UBound(Layers)).EndNeuron
      Neurons(i).Error = CDbl(Target(j)) - Neurons(i).Value
      SSE = SSE + (Neurons(i).Error * Neurons(i).Error)
      j = j + 1
    Next i
    
    ' Simulated annealing - adjustment of learning coefficient to match error value.
    If mAnnealingEpoch > 0 Then
        mAnnealingSSE = mAnnealingSSE + SSE
        If mTrainingCycles Mod mAnnealingEpoch = 0 And mTrainingCycles > 0 Then
            If mLastAnnealingSSE > 0 Then
                If mAnnealingSSE < mLastAnnealingSSE Then
                    mLearningCoefficient = mLearningCoefficient * mLearningRateDecrease
                Else
                    mLearningCoefficient = mLearningCoefficient * mLearningRateIncrease
                End If
            End If
            mLastAnnealingSSE = mAnnealingSSE
            mAnnealingSSE = 0
        End If
    End If
    
    mSumSquaredError = SSE
    
    If mStopping = True Then
      Exit Sub
    End If
    
    ' Calculate work to be done
    TotalToDo = UBound(Neurons) - Layers(LBound(Layers) + 1).StartNeuron + 1
    Percentage = TotalToDo \ 100
    WorkDone = 0
    
    'hidden layers
    For i = UBound(Layers) To LBound(Layers) + 1 Step -1
        RaiseEvent InfoMessage(mTag, "Running Back Propogation on Layer " & i)
        For j = Layers(i).StartNeuron To Layers(i).EndNeuron
          ' Back propagate.
          With Neurons(j)
            .Error = .Error * .Value * (1# - .Value) ' proportional error
          End With
          ' Now, update all connected neurons error appropriately:
          PrevNLayerPtr = Layers(i - 1).StartNeuron
          For k = Layers(i).StartDendrite + ((j - Layers(i).StartNeuron) * Layers(i - 1).NeuronCount) To Layers(i).StartDendrite + ((j - Layers(i).StartNeuron) * Layers(i - 1).NeuronCount) + Layers(i - 1).NeuronCount
            Neurons(PrevNLayerPtr).Error = Neurons(PrevNLayerPtr).Error + (Neurons(j).Error * Dendrites(k).TransferWeight)
            PrevNLayerPtr = PrevNLayerPtr + 1
          Next k
        Next j
        WorkDone = WorkDone + 1
        If WorkDone Mod Percentage = 0 Then
          RaiseEvent Progress(mTag, (WorkDone / TotalToDo) * 100)
        End If
        If mStopping = True Then
          Exit For
        End If
    Next i
        
    If mStopping = True Then
      Exit Sub
    End If
    
    ' Calculate work to be done
    TotalToDo = UBound(Neurons) - Layers(LBound(Layers) + 1).StartNeuron + 1
    Percentage = TotalToDo \ 100
    WorkDone = 0
    
    ' Update weights
    For i = UBound(Layers) To LBound(Layers) + 1 Step -1
          RaiseEvent InfoMessage(mTag, "Updating Weights in Layer " & i)
          For j = Layers(i).StartNeuron To Layers(i).EndNeuron
              ' update base value
              With Neurons(j)
                .BaseValue = .BaseValue + mLearningCoefficient * .Error
              End With
              ' update dendrite weights
              PrevNLayerPtr = Layers(i - 1).StartNeuron
              For k = Layers(i).StartDendrite + ((j - Layers(i).StartNeuron) * Layers(i - 1).NeuronCount) To Layers(i).StartDendrite + ((j - Layers(i).StartNeuron) * Layers(i - 1).NeuronCount) + Layers(i - 1).NeuronCount
                With Dendrites(k)
                  .TransferWeight = .TransferWeight + mLearningCoefficient * Neurons(PrevNLayerPtr).Value * Neurons(j).Error
                End With
                PrevNLayerPtr = PrevNLayerPtr + 1
              Next k
              ' reset neuron error for next training cycle.
              Neurons(j).Error = 0
              WorkDone = WorkDone + 1
              If WorkDone Mod Percentage = 0 Then
                RaiseEvent Progress(mTag, (WorkDone / TotalToDo) * 100)
              End If
              If mStopping = True Then
                Exit For
              End If
          Next j
          If mStopping = True Then
            Exit For
          End If
    Next i
    
End Sub

Public Sub Train(Data As Variant, Target As Variant)
  If mvarCreated = True Then
    If mRunning = True Then
      Exit Sub
    End If
    ' need some error checking for data and target arrays to see if they're valid.
    
    mRunning = True
    mStopping = False
    #If ShowDebugTimes = 1 Then
    StartTime = GetTickCount
    #End If

    SetInput Data
    If mStopping = False Then
      CalculateOutput
      If mStopping = False Then
        AdjustWeights Target
      End If
    End If
    
  Else
    Err.Raise vbObjectError + 1, "Perceptron", "You must initialise your net before you attempt to train it"
    mStopping = True
  End If
  If mStopping = False Then
    mTrainingCycles = mTrainingCycles + 1
  End If
  #If ShowDebugTimes = 1 Then
  Debug.Print "Time for one training cycle to take place: " & GetTickCount - StartTime & " ms"
  #End If
  mRunning = False
End Sub

' Test Ok.
Public Sub CreateNet(pTag As Variant, ParamArray Struc() As Variant)
Dim TotalToDo As Long
Dim TotalDone As Long
Dim PercentageHit As Long
Dim i As Long, TotalNeurons As Long, TotalSynapses As Long

    If mRunning = True Then
      Exit Sub
    End If
    mRunning = True
    mStopping = False
    #If ShowDebugTimes = 1 Then
    StartTime = GetTickCount
    #End If
    ' Initialise
    ' Need to destroy first
    DestroyNicely
    Set Tag = pTag
    mTrainingCycles = 0
    mSumSquaredError = 0
        
    ' Redim all arrays.
    ' Layers
    ReDim Layers(LBound(Struc) To UBound(Struc)) ' maintains array base of ParamArray
    ' Neurons
    ' Total neurons - add up all the values in the Struc array.
    ' Total synapses = Layer1*Layer2 + Layer2*Layer3 + Layer3*Layer4 etc.
    TotalNeurons = 0
    TotalSynapses = 0
    RaiseEvent InfoMessage(mTag, "Initialising Neural arrays")
    For i = LBound(Struc) To UBound(Struc)
      Layers(i).StartNeuron = TotalNeurons
      TotalNeurons = TotalNeurons + Struc(i)
      Layers(i).EndNeuron = TotalNeurons - 1
      Layers(i).NeuronCount = Struc(i)
      If i <> LBound(Struc) Then
        Layers(i).StartDendrite = TotalSynapses
        TotalSynapses = TotalSynapses + (Struc(i) * Struc(i - 1))
        Layers(i).EndDendrite = TotalSynapses - 1
        Layers(i).DendriteCount = Struc(i) * Struc(i - 1)
      Else
        Layers(i).StartDendrite = 0
        Layers(i).EndDendrite = 0
        Layers(i).DendriteCount = 0
      End If
    Next i
    ReDim Neurons(0 To TotalNeurons)
    ReDim Dendrites(0 To TotalSynapses)
        
    ' Calculate work to be done
'    RaiseEvent InfoMessage(mTag, "Randomising Weights and Base Values")
'    TotalToDo = -LBound(Neurons) + UBound(Neurons) + 1 - LBound(Dendrites) + UBound(Dendrites) + 1
'    PercentageHit = TotalToDo / 100
'    TotalDone = 0
    
    NguyenWidrowInitialise
    
'    ' Set BaseValue for each neuron
'    For i = LBound(Neurons) To UBound(Neurons)
'      Neurons(i).BaseValue = GetRand
'      TotalDone = TotalDone + 1
'      If TotalDone Mod PercentageHit = 0 Then
'        RaiseEvent Progress(mTag, (TotalDone / TotalToDo) * 100)
'      End If
'    Next i
'    ' Set TransferWeight for each dendrite.
'    For i = LBound(Dendrites) To UBound(Dendrites)
'      Dendrites(i).TransferWeight = GetRand
'      TotalDone = TotalDone + 1
'      If TotalDone Mod PercentageHit = 0 Then
'        RaiseEvent Progress(mTag, (TotalDone / TotalToDo) * 100)
'      End If
'    Next i
    
'    For i = LBound(Layers) To UBound(Layers)
'      Debug.Print "Layer " & i & ": Neurons: " & Layers(i).StartNeuron; " - " & Layers(i).EndNeuron & "(" & Layers(i).NeuronCount & ")" & "      Dendrites: " & Layers(i).StartDendrite & " - " & Layers(i).EndDendrite & "(" & Layers(i).DendriteCount & ")"
'    Next i
    
    If mStopping = True Then
    Else
      mvarCreated = True
    End If
    #If ShowDebugTimes = 1 Then
    Debug.Print "Time to create net: " & GetTickCount - StartTime & " ms"
    #End If
    mRunning = False
End Sub

' This is a bit pointless now - just here to preserve compatibility with original cNet module.
Public Sub DestroyNicely()
  If mRunning = True Then
    Exit Sub
  End If
  #If ShowDebugTimes = 1 Then
  StartTime = GetTickCount
  #End If
  RaiseEvent InfoMessage(mTag, "Destroying Dendrites")
  Erase Dendrites
  RaiseEvent InfoMessage(mTag, "Destroying Neurons")
  Erase Neurons
  RaiseEvent InfoMessage(mTag, "Destroying Layers")
  Erase Layers
  mvarCreated = False
  #If ShowDebugTimes = 1 Then
  Debug.Print "Time to destroy net: " & GetTickCount - StartTime & " ms"
  #End If
End Sub

Private Sub Class_Initialize()
  Randomize
  mvarCreated = False
  mLearningRateIncrease = 1#
  mLearningRateDecrease = 1#
  mAnnealingEpoch = 0
  mAnnealingSSE = 0
  mLastAnnealingSSE = 0
End Sub

Private Sub Class_Terminate()
  Erase Dendrites
  Erase Neurons
  Erase Layers
End Sub

Public Sub Jitter(Optional MaxVariance As Double = 0.05)
Dim i As Long, j As Long, k As Long
Dim Variance As Double
Dim TotalToDo As Long
Dim TotalDone As Long
Dim PercentageHit As Long

  If mRunning = True Or mvarCreated = False Then
    Exit Sub
  End If
  mRunning = True
  mStopping = False
  #If ShowDebugTimes = 1 Then
  StartTime = GetTickCount
  #End If

  
  TotalToDo = UBound(Dendrites) - LBound(Dendrites) + 1
  PercentageHit = TotalToDo / 100
  TotalDone = 0
  
  RaiseEvent InfoMessage(mTag, "Adding random noise to the net's weights")
  For i = LBound(Dendrites) To UBound(Dendrites)
    Variance = (MaxVariance * Rnd * 2) - MaxVariance
    Dendrites(i).TransferWeight = Dendrites(i).TransferWeight + Variance
    If Dendrites(i).TransferWeight < -1 Then
      Dendrites(i).TransferWeight = -1
    End If
    If Dendrites(i).TransferWeight > 1 Then
      Dendrites(i).TransferWeight = 1
    End If
    TotalDone = TotalDone + 1
    If TotalDone Mod PercentageHit = 0 Then
      RaiseEvent Progress(mTag, (TotalDone / TotalToDo) * 100)
    End If
  Next i
  
  #If ShowDebugTimes = 1 Then
  Debug.Print "Time to jitter net: " & GetTickCount - StartTime & " ms"
  #End If
  
  mRunning = False
End Sub

Public Sub KickZeros(Optional Amount As Double = 0.05)
Dim i As Long
Dim tmpAmount As Double
  For i = LBound(Dendrites) To UBound(Dendrites)
    If Dendrites(i).TransferWeight <= 0.001 And Dendrites(i).TransferWeight >= -0.001 Then
      tmpAmount = Amount * 2 * Rnd - Amount
      Dendrites(i).TransferWeight = tmpAmount
    End If
  Next i
  For i = LBound(Neurons) To UBound(Neurons)
    If Neurons(i).BaseValue <= 0.001 And Neurons(i).BaseValue >= -0.001 Then
      tmpAmount = Amount * Rnd
      Dendrites(i).TransferWeight = tmpAmount
    End If
  Next i
End Sub

Public Sub StopWorking()
  mStopping = True
End Sub

' ok
Public Function SaveNet(Filename As Variant, Optional SaveVersion As String = VERSION) As Boolean
Dim FileNumber As Long
Dim ll             As Long
Dim Percentage As Long
Dim WorkDone As Long
Dim TotalToDo As Long
Dim i As Long, j As Long, k As Long
Dim SaveHeader As String
Dim strVersion As String

  If mRunning = True Then
    Exit Function
  End If
  mRunning = True
  mStopping = False
  #If ShowDebugTimes = 1 Then
  StartTime = GetTickCount
  #End If
  
  If mvarCreated = False Then
      Err.Raise vbObjectError + 3, "Perceptron", "There is no Net to save."
  Else
      ' Version 1.3 save format
      On Error GoTo ErrHandler
      If VarType(Filename) = vbString Then
        FileNumber = FreeFile
        Open Filename For Binary Access Write As #FileNumber
      Else
        FileNumber = Filename
      End If
      On Error GoTo 0
      
      If SaveVersion = "1.2" Then
        SaveNetOnePointTwo FileNumber
      Else
        
        ' Work out how much there is to do
        RaiseEvent InfoMessage(mTag, "Saving Neural Net")
        Percentage = TotalNeuronCount
        WorkDone = 0
        TotalToDo = TotalNeuronCount
        i = 1
        SaveHeader = OCXNAME
        Put #FileNumber, , SaveHeader
        strVersion = VERSION
        Put #FileNumber, , strVersion ' Write header
        Put #FileNumber, , CLng(UBound(Layers) - LBound(Layers) + 1) ' Write Number of layers
        Put #FileNumber, , Me.TotalNeuronCount ' Write Total number of neurons
        Put #FileNumber, , CLng(UBound(Dendrites) - LBound(Dendrites) + 1) ' Write total number of dendrites
        Put #FileNumber, , Me.TrainingCycles ' Write Total amount trained
        ' Write learning coefficient information
        Put #FileNumber, , mAnnealingEpoch
        Put #FileNumber, , mAnnealingSSE
        Put #FileNumber, , mLastAnnealingSSE
        Put #FileNumber, , mLearningRateDecrease
        Put #FileNumber, , mLearningRateIncrease
        Put #FileNumber, , mLearningCoefficient
        lename) = vecpealingSSE
        Put #FileNum Write foMessage(mTag, "DestrtrrHandler
   ) = vecpealingug   ' Work rTo UBound(ingug   ' Work rTo UBound(ingug   ' Work gSSEug   ' WmTag, "DestroyirrrrrrbTo UBounde1w much there is to Dendrites
  Erase Neurons
  Erase , , SaveHeader
c PropeemErase"raBindingBehavSSEug   '
  Erase Neurons
  Erase , , SaveHeader
c PropeemErase"raBindingBehavSSEug   '
  Erase Neurons
  Erase , , SaveHeauch theSTot Erasee , , SaveHeal = TotaemErase"raBindingBehavSSEuamt #FileNumb   s
      ta nHeauch theSTot Enf, ,   Put endriteu  PraBmendriEximumEa nHeauch theSTondrHeauB   pAmo Pra  tmpAmount EVariant)
 DAmo rErase"raBindingr oes ' Write Topr/tm   Nt   PutSaveHeal = Toth"r, ,ingBehavS
  evNLay , CLng(UBound(Dendrites) - LBound(De.veCount = Struc(i) * Struc(i - 1)
      Else
        Layers(i).StartDendrite = earni/Heauch theSTondrHeaDendrite sAs Dm strVersd(De.veCmRunning = Trdl   lse Then
 CLng(UBound(Dennnnnnnnnnnnnn_TermiCLng(UBoCvlNoppi - 1)
     t
       t
  LublEx = 0
  l arltaaCmRunning = Trrt
  LublEx = 0
  l eub Class_Terminate(   SEug  se"raBindingBehni/Heau   Put #Fime to cre     agpTrue O   
  LublEx = 0
  l arltaaCmRunning = Trrt
  Lu #FileNum Write foMessage(mTag, "DeOeR O   
  LublEx =n
   se DeTrrT(mTag, "Des
  Erons)
    or Write foMessage(M   
 ta nHeauch theSTotaBouateu  PraBmendriEximumEa nHeauch theSTondrHeauBx = 0
  l arltaaCmeUrT(mTag,he"raBindingnn
   se DeTrrT(mTag, "Des
  Erons)
    or Write foMessageMtar auBx = 0
  l arltayou attemns)
r Wr
     te
   se Derrt
  Lu 1themhcnSTondrHeauBx = 0
  l arltaaCmeUrT(mTag,he"raBinre  
 ta nHeauch ns)rrt
  LufendrHeauBx = 0
 r
     te
   se Derrt
  Lu /     te
   se Derrt
  Lu 1themhcnSTondrHefendrHeauBx = 0
 r
essaBouateu  PraBml
        Put #Fiu  PraBmmTaFalse
Enx = 0
 e
.
 ta nHeauch nsx =n

  
  If mvarCreated = False Then
 
.
 ta ne Class_ theSTotaBouatosa(lms"
    #End If
    mRuh Era If mvarCratosa(lms"
    #EndgeMtar auendrue Derrt
  Lu /     te
   se e     te
   t
  Lu /     te
htopWorkingN   Erase Dy     Put #F   f
    mRuhse D
    oyirrrrrrbTo UBou   e D
    oyirrrr  If Dendrites(i).TransferWeaes(i)mRunninga Purons
  Erase , )mRu)s) + 1).Searni/Hea              NeeeeeeeeeeaaC'hoaBinn