Graphical Artificial Neural Net
Posted: Mon Apr 27, 2009 1:48 pm
This is something I've wanted to do for a lot of years but whether VB, powerbasic or Purebasic, I could never get working. Recently I had another idea for a use for it so to test it I wanted to do the good old basic character recognition thing for numerals (ANN's 101). I finally got it working and now have a basic ann.pbi built that handles creation of a perceptron with 1 hidden layer and backpropergation.
Next, in order to see it I created this little app with some very ugly hacked code as I was in a hurry to see how it worked, it has all sorts of horrible code like calling the include files globals directly and lots of hard coded positional values
. It helped me debug a few things with regards to neurons firing and some issues with the weight state distribution.
You can change the number of hidden neurons or the learning level to see how it performs. Run it at "Fast" to get through a few hundred epochs to get to a point where the task is getting solved then slow it down to actually see it. On high speed, the flickering makes it hard to see how the solution works. The net usually converges on a solution at between 700 and 2000 epochs although it does get stuck sometimes.
First the Include file... "ANN.PBI"
Next the app.
Next, in order to see it I created this little app with some very ugly hacked code as I was in a hurry to see how it worked, it has all sorts of horrible code like calling the include files globals directly and lots of hard coded positional values

You can change the number of hidden neurons or the learning level to see how it performs. Run it at "Fast" to get through a few hundred epochs to get to a point where the task is getting solved then slow it down to actually see it. On high speed, the flickering makes it hard to see how the solution works. The net usually converges on a solution at between 700 and 2000 epochs although it does get stuck sometimes.
First the Include file... "ANN.PBI"
Code: Select all
#HiddenLayer = 1
#OutputLayer = 2
;Export Functions
Declare ANNCreate(InputCount.l, HiddenCount.l, OutputCount.l, LearningRate.d)
Declare ANNTrainSet(Array Inputs.d(1), Array Outputs.d(1), Array AnswerOutputs.d(1)) ;'Back propergates and learns
Declare ANNQuerySet(Array Inputs.d(1), Array Outputs.d(1))
Declare BackProp(ANNLayer.l )
Declare.d MeanSqrErr()
Declare CopyDoubleArray(Array DestArray.d(1), Array SrcArray.d(1) )
Declare CopyDouble2DArray(Array DestArray.d(2), Array SrcArray.d(2))
Declare LoadWeights(Filename.s)
Declare SaveWeights(Filename.s)
;NonExported Functions
Declare CalcLayerOutput(ANNLayer.l)
;'if %HiddenLayer, take raw inputs, hidden weights and write to hidden neurons
;'if %Outputlayer, Take Hiddenlayer vals as inputs, output weights and write to output neurons
Declare.d ActivateNeuron(InputTotal.d)
;'Global Vars
Global ANNInputCount.l
Global ANNHiddenCount.l
Global ANNOutputCount.l
Global ANNLearningRate.d
Global Dim ANNInputs.d(0) ;'Raw input values
Global Dim ANNHiddenNeurons.d(0) ;'output values
Global Dim ANNOutputNeurons.d(0) ;'output values
Global Dim ANNHiddenWeights.d(0,0) ;'weight values
Global Dim ANNOutputWeights.d(0,0) ;'weight values
Global Dim ANNAnswerOutputs.d(0) ;'Answer outputs to test against
Global Dim ANNOutputErrors.d(0) ;'Error value from the output layer to be remembered for the hidden layer backprop
;'Init Network
Procedure ANNCreate(InputCount.l, HiddenCount.l, OutputCount.l,LearningRate.d)
;Randomize Timer
RndLoop1.l
RndLoop2.l
ANNInputCount = InputCount
ANNHiddenCount = HiddenCount
ANNOutputCount = OutputCount
ANNLearningRate = LearningRate
Dim ANNInputs.d(ANNInputCount)
Dim ANNHiddenWeights.d(ANNInputCount, ANNHiddenCount)
Dim ANNOutputWeights.d(ANNHiddenCount, ANNOutputCount)
Dim ANNHiddenNeurons.d(ANNHiddenCount)
Dim ANNOutputNeurons.d(ANNOutputCount)
Dim ANNAnswerOutputs.d(ANNOutputCount)
Dim ANNOutputErrors.d(ANNOutputCount)
;init weights With inital random values
ANNHiddenNeurons(0) = 1
ANNInputs(0) = 1
RandomSeed(ElapsedMilliseconds())
For RndLoop1 = 0 To ANNHiddenCount
For RndLoop2 = 0 To ANNInputCount
ANNHiddenWeights(RndLoop2, RndLoop1) = (5 - Random(10)) / 10 ; -0.5 to 0.5 system
Next
Next
For RndLoop1 = 0 To ANNOutputCount
For RndLoop2 = 0 To ANNHiddenCount
ANNOutputWeights(RndLoop2, RndLoop1) = (5 - Random(10)) / 10
;Debug ANNOutputWeights(RndLoop2, RndLoop1)
Next
Next
EndProcedure
;==================================================================================
Procedure ANNTrainSet(Array Inputs.d(1), Array Outputs.d(1), Array AnswerOutputs.d(1)) ;all as double
;bias
Inputs(0) = 1
CopyDoubleArray(ANNAnswerOutputs(),AnswerOutputs())
CopyDoubleArray(ANNInputs(),Inputs())
CalcLayerOutput(#HiddenLayer)
CalcLayerOutput(#OutputLayer)
BackProp(#OutputLayer)
BackProp(#HiddenLayer)
CopyDoubleArray(Outputs(),ANNOutputNeurons())
EndProcedure
;==================================================================================
Procedure ANNQuerySet(Array Inputs.d(1), Array Outputs.d(1)) ;all as double
Inputs(0) = 1
CopyDoubleArray(ANNInputs(),Inputs())
CalcLayerOutput(#HiddenLayer)
CalcLayerOutput(#OutputLayer)
CopyDoubleArray(Outputs(),ANNOutputNeurons())
EndProcedure
;==================================================================================
Procedure CalcLayerOutput(ANNLayer.l)
InputTotal.d
InputCount.l
OutputCount.l
Dim Inputs.d(0)
Dim Outputs.d(0)
Dim Weights.d(0,0)
InputNeuronLoop.l
HiddenNeuronLoop.l
WeightLoop.l
; Prepare DataArrays depending on layer, (makes it easier To add layers in future)
If ANNLayer = #HiddenLayer
InputCount = ANNInputCount
OutputCount = ANNHiddenCount
Dim Inputs.d(InputCount)
Dim Outputs.d(OutputCount)
Dim Weights.d(InputCount, OutputCount)
;Copy Arrays
CopyDoubleArray(Inputs(),ANNInputs())
CopyDoubleArray(Outputs(),ANNHiddenNeurons())
CopyDouble2dArray(Weights(),ANNHiddenWeights())
ElseIf ANNLayer = #OutputLayer
InputCount = ANNHiddenCount
OutputCount = ANNOutputCount
Dim Inputs.d(InputCount)
Dim Outputs.d(OutputCount)
Dim Weights.d(InputCount, OutputCount)
;Copy Arrays
CopyDoubleArray(Inputs(),ANNHiddenNeurons())
CopyDoubleArray(Outputs(),ANNOutputNeurons())
CopyDouble2dArray(Weights(),ANNOutputWeights())
Else
Debug "Layer Error"
EndIf
;Calculate Outputs from inputs And weights For each neuron in layer.
For HiddenNeuronLoop = 1 To OutputCount
For WeightLoop = 0 To InputCount
InputTotal = InputTotal + ( Inputs(WeightLoop) * Weights(WeightLoop, HiddenNeuronLoop) )
Next
Outputs(HiddenNeuronLoop) = ActivateNeuron(InputTotal)
InputTotal = 0
Next
;copy Data back again
If ANNLayer = #HiddenLayer
CopyDoubleArray(ANNHiddenNeurons(),Outputs())
ElseIf ANNLayer = #OutputLayer
CopyDoubleArray(ANNOutputNeurons(),Outputs())
Else
Debug "Layer Error"
EndIf
EndProcedure
;'==================================================================================
Procedure.d ActivateNeuron(InputTotal.d)
ProcedureReturn 1 / (1 + Pow(2.718282,(-1 * InputTotal)))
EndProcedure
;'==================================================================================
Procedure BackProp(ANNLayer.l)
NeuronLoop.l
InputLoop.l
HiddenLoop.l
InputWeightLoop.l
WeightLoop.l
DeltaError.d
;'Dim MeanErr As double
Ok.d ;'Output for K
Ik .d ;'Output for Input neuron
Tk.d ;'Target for K
Wjk.d ;'Weight for OutputK from hidden J
Wij.d ;'Weight for HiddenJ from Input i
J.d ;'Output of Hidden J
Ej.d ;'Error for hidden layer
SigmaTotal.d ;'running total of additions in hidden layer
If ANNLayer = #OutputLayer
For NeuronLoop = 0 To ANNOutputCount ;'loop output neurons
Ok = ANNOutputNeurons(NeuronLoop)
Tk = ANNAnswerOutputs(NeuronLoop)
DeltaError = (Tk - Ok) * Ok * (1 - Ok)
ANNOutputErrors(NeuronLoop) = DeltaError
For WeightLoop = 0 To ANNHiddenCount ;'adjust weight from each hidden layer neuron
Wjk = ANNOutputWeights(WeightLoop, NeuronLoop)
J = ANNHiddenNeurons(WeightLoop)
Wjk = Wjk + (ANNLearningRate * DeltaError * J)
ANNOutputWeights(WeightLoop,NeuronLoop ) = Wjk
Next
Next
ElseIf ANNLayer = #HiddenLayer
Ej = 0
For HiddenLoop = 0 To ANNHiddenCount
Ok = ANNHiddenNeurons(HiddenLoop)
SigmaTotal = 0
For NeuronLoop = 1 To ANNOutputCount
SigmaTotal = SigmaTotal + (ANNOutputErrors(NeuronLoop) * ANNOutputWeights(ANNHiddenCount, ANNOutputCount))
Next
Ej = Ok * (1-Ok) * SigmaTotal ;* 10
For inputloop = 0 To ANNInputCount
Wij = ANNHiddenWeights(InputLoop,HiddenLoop)
Wij = Wij + (ANNLearningRate * Ej * ANNInputs(InputLoop))
ANNHiddenWeights(InputLoop,HiddenLoop) = Wij
Next
Next
Else
Debug "Layer Error"
EndIf
EndProcedure
;'==================================================================================
Procedure.d MeanSqrErr()
TotalSqrErr.d
ErrLoop.l
; If ANNOutputCount = 0;< 0.0000001 ;'bail before div by zero
; ProcedureReturn -1
; EndIf
For errloop = 0 To ANNOutputCount
TotalSqrErr = TotalSqrErr + Pow((ANNOutputErrors(ErrLoop)),2)
Next
; If TotalSqrErr =0; 1 ;'bail before div by zero
; ProcedureReturn -1
; EndIf
ProcedureReturn TotalSqrErr / ANNOutputCount
EndProcedure
;'==================================================================================
Procedure CopyDoubleArray(Array DestArray.d(1), Array SrcArray.d(1)) ;all as double
ArrayLoop.l
VarSize.l = 8
Elements.l = ArraySize(DestArray())
CopyMemory(@SrcArray(),@DestArray(),VarSize * (Elements+1))
EndProcedure
;'==================================================================================
Procedure CopyDouble2DArray(Array DestArray.d(2), Array SrcArray.d(2)) ;all as double
ArrayLoop.l
VarSize.l = 8
Elements1.l = ArraySize(DestArray(),1)
Elements2.l = ArraySize(DestArray(),2)
CopyMemory(@SrcArray(),@DestArray(),VarSize * (Elements1+1) * (Elements2 + 1))
EndProcedure
;'==================================================================================
Procedure SaveWeights(Filename.s)
hfile.i
hfile = CreateFile(#PB_Any, Filename)
For ic.l = 0 To ANNInputCount
For hc.l = 0 To ANNHiddenCount
WriteDouble(hfile, ANNHiddenWeights(ic, hc))
Next
Next
For hc.l = 0 To ANNHiddenCount
For oc.l = 0 To ANNOutputCount
WriteDouble(hfile, ANNOutputWeights(hc, oc))
Next
Next
CloseFile(hfile)
EndProcedure
;'==================================================================================
Procedure LoadWeights(Filename.s)
hfile.i
hfile = ReadFile(#PB_Any, Filename)
For ic.l = 0 To ANNInputCount
For hc.l = 0 To ANNHiddenCount
ANNHiddenWeights(ic, hc) = ReadDouble(hfile)
Next
Next
For hc.l = 0 To ANNHiddenCount
For oc.l = 0 To ANNOutputCount
ANNOutputWeights(hc, oc) = ReadDouble(hfile)
Next
Next
CloseFile(hfile)
EndProcedure
;'==================================================================================
Next the app.
Code: Select all
XIncludeFile "ann.pbi"
#ScrWidth = 1200
#ScrHeight = 800
#Exp = 2.7182818284590451
#CmdSlow = 1
#CmdMed = 2
#CmdFast = 3
#CmdRestart = 4
Declare Main()
Declare MakeArray(Array TheArray.d(1), CSV.s)
main()
Procedure Main()
If OpenWindow(0, 0, 0, #ScrWidth, #ScrHeight, "Nerual Network", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget) ;And CreateGadgetList(WindowID(0))
If CreateImage(0, #ScrWidth, #ScrHeight-22, 32)
ImageGadget(0, 0, 0, #ScrWidth, #ScrHeight-22, ImageID(0)) ; imagegadget standard
EndIf
StartDrawing(ImageOutput(0))
Box(1,1,#ScrWidth, #ScrHeight,RGB(30,30,30))
StopDrawing()
ButtonGadget(#CmdSlow, 10,780, 50, 20, "Slow")
ButtonGadget(#CmdMed, 60,780, 50, 20, "Normal")
ButtonGadget(#CmdFast, 110,780, 50, 20, "Fast")
ButtonGadget(#CmdRestart, 160,780, 50, 20, "Restart")
frame.q = 0
InputCount.l
HiddenCount.l
OutputCount.l
LearningRate.d
TrainingLoop.l
Dim Outputs.d(10)
Dim One.d(20)
Dim Two.d(20)
Dim Three.d(20)
Dim Four.d(20)
Dim Five.d(20)
Dim Six.d(20)
Dim Seven.d(20)
Dim Eight.d(20)
Dim Nine.d(20)
Dim Zero.d(20)
Dim Display.d(20)
Dim CurrentDispPtr.l(10)
Dim CurrentAnsPtr.l(10)
Dim CurrentAnswer.d(10)
Dim Answer1.d(10): Answer1(1) = 1
Dim Answer2.d(10): Answer2(2) = 1
Dim Answer3.d(10): Answer3(3) = 1
Dim Answer4.d(10): Answer4(4) = 1
Dim Answer5.d(10): Answer5(5) = 1
Dim Answer6.d(10): Answer6(6) = 1
Dim Answer7.d(10): Answer7(7) = 1
Dim Answer8.d(10): Answer8(8) = 1
Dim Answer9.d(10): Answer9(9) = 1
Dim Answer0.d(10): Answer0(10) = 1
MakeArray(Zero(), "0.01,0.99,0.99,0.01,0.99,0.01,0.01,0.99,0.99,0.01,0.01,0.99,0.99,0.01,0.01,0.99,0.01,0.99,0.99,0.01")
MakeArray(One(), "0.01,0.99,0.01,0.01,0.99,0.99,0.01,0.01,0.01,0.99,0.01,0.01,0.01,0.99,0.01,0.01,0.99,0.99,0.99,0.01")
MakeArray(two(), "0.01,0.99,0.99,0.01,0.99,0.01,0.01,0.99,0.01,0.01,0.99,0.01,0.01,0.99,0.01,0.01,0.99,0.99,0.99,0.99")
MakeArray(three(), "0.99,0.99,0.99,0.01,0.01,0.01,0.01,0.99,0.01,0.99,0.99,0.01,0.01,0.01,0.01,0.99,0.99,0.99,0.99,0.01")
MakeArray(Four(), "0.99,0.01,0.01,0.01,0.99,0.01,0.99,0.01,0.99,0.99,0.99,0.99,0.01,0.01,0.99,0.01,0.01,0.01,0.99,0.01")
MakeArray(five(), "0.99,0.99,0.99,0.99,0.99,0.01,0.01,0.01,0.99,0.99,0.99,0.01,0.01,0.01,0.01,0.99,0.99,0.99,0.99,0.01")
MakeArray(Six(), "0.01,0.99,0.99,0.99,0.99,0.01,0.01,0.01,0.99,0.99,0.99,0.01,0.99,0.01,0.01,0.99,0.01,0.99,0.99,0.01")
MakeArray(seven(), "0.99,0.99,0.99,0.99,0.01,0.01,0.01,0.99,0.01,0.01,0.99,0.01,0.01,0.99,0.01,0.01,0.01,0.99,0.01,0.01")
MakeArray(eight(), "0.01,0.99,0.99,0.01,0.99,0.01,0.01,0.99,0.01,0.99,0.99,0.01,0.99,0.01,0.01,0.99,0.01,0.99,0.99,0.01")
MakeArray(nine(), "0.01,0.99,0.99,0.01,0.99,0.01,0.01,0.99,0.01,0.99,0.99,0.99,0.01,0.01,0.01,0.99,0.99,0.99,0.99,0.01")
CurrentDispPtr(1) = @one()
CurrentDispPtr(2) = @two()
CurrentDispPtr(3) = @three()
CurrentDispPtr(4) = @four()
CurrentDispPtr(5) = @five()
CurrentDispPtr(6) = @six()
CurrentDispPtr(7) = @seven()
CurrentDispPtr(8) = @eight()
CurrentDispPtr(9) = @nine()
CurrentDispPtr(10) = @zero()
CurrentAnsPtr(1) = @Answer1()
CurrentAnsPtr(2) = @Answer2()
CurrentAnsPtr(3) = @Answer3()
CurrentAnsPtr(4) = @Answer4()
CurrentAnsPtr(5) = @Answer5()
CurrentAnsPtr(6) = @Answer6()
CurrentAnsPtr(7) = @Answer7()
CurrentAnsPtr(8) = @Answer8()
CurrentAnsPtr(9) = @Answer9()
CurrentAnsPtr(10) = @Answer0()
For i = 1 To 20
Display(i) = 1
Next
For i = 1 To 10
CurrentAnswer(i) = 1
Next
Digitloop = 1
RefreshDelay = 50
;Create ANN
InputCount = 20
HiddenCount = 25
OutputCount = 10
LearningRate = 0.55
ANNCreate(InputCount, HiddenCount, OutputCount, LearningRate)
FontID1 = FontID(LoadFont(#PB_Any, "Arial" , 12, #PB_Font_Bold))
Repeat
frame = frame + 1
; Init Display Drawing
StartDrawing(ImageOutput(0))
Box(1,1,#ScrWidth, #ScrHeight,RGB(30,30,30))
DigXoffset.l = 20
DigYOffset.l = 300
DigPixelSize = 9
DispY = 100
Digitloop = Digitloop + 1
If Digitloop > 10
Digitloop = 1
EndIf
CopyMemory(CurrentDispPtr(Digitloop),@Display(),8 * 21)
CopyMemory(CurrentAnsPtr(Digitloop),@CurrentAnswer(),8 * 11)
ANNTrainSet(Display(), Outputs(), CurrentAnswer())
;Display Layer 1
For i = 1 To 20 Step 4
LineXY(DigXoffset + 14, DigYOffset + (i*3), 250, DispY, RGB(0,Display(i) * 255,0))
LineXY(DigXoffset + 26, DigYOffset + (i*3), 250, DispY + 30, RGB(0,Display(i+1) * 255,0))
LineXY(DigXoffset + 38, DigYOffset + (i*3), 250, DispY + 60, RGB(0,Display(i+2) * 255,0))
LineXY(DigXoffset + 50, DigYOffset + (i*3), 250, DispY + 90, RGB(0,Display(i+3) * 255,0))
Circle(250, 70 , DigPixelSize-1, RGB(0,ANNInputs(0) * 127,255))
Circle(250, DispY, DigPixelSize-1, RGB(0,ANNInputs(i) * 255,0))
Circle(250, DispY + 30, DigPixelSize-1, RGB(0,ANNInputs(i+1) * 255,0))
Circle(250, DispY + 60, DigPixelSize-1, RGB(0,ANNInputs(i+2) * 255,0))
Circle(250, DispY + 90, DigPixelSize-1, RGB(0,ANNInputs(i+3) * 255,0))
DispY = DispY + 120
Box(DigXoffset + 12, DigYOffset + (i*3), DigPixelSize, DigPixelSize,RGB(0,Display(i) * 255,0))
Box(DigXoffset + 24, DigYOffset + (i*3), DigPixelSize, DigPixelSize,RGB(0,Display(i+1) * 255,0))
Box(DigXoffset + 36, DigYOffset + (i*3), DigPixelSize, DigPixelSize,RGB(0,Display(i+2) * 255,0))
Box(DigXoffset + 48, DigYOffset + (i*3), DigPixelSize, DigPixelSize,RGB(0,Display(i+3) * 255,0))
Next
;Display HiddenLayer
DispY = 50
NegLevel.d = 0
For i = 0 To HiddenCount
If i = 0
Circle(600, DispY + (i*25), DigPixelSize-1, RGB(0,ANNHiddenNeurons(i) * 127,255))
Else
Circle(600, DispY + (i*25), DigPixelSize-1, RGB(0,ANNHiddenNeurons(i) * 255,0))
EndIf
;weights from input layer
For j = 0 To 20
If ANNHiddenWeights(j,i) < 0
NegLevel.d = 1 /(1 + Pow(#Exp,(-1 * ANNHiddenWeights(j,i))))
PosLevel.d = 0
Else
NegLevel.d = 0
PosLevel.d = 1 /(1 + Pow(#Exp,(-1 * ANNHiddenWeights(j,i))))
;Debug StrD(PosLevel) + " " + StrD(ANNHiddenWeights(j,i))
EndIf
If i > 0
LineXY(250, DispY + 20 + (j*30), 600, DispY + (i*25), RGB(NegLevel * 255,PosLevel * 255,0))
EndIf
Next
Next
;Display Output Layer
DispY = 100
DrawingFont(FontID1)
For i = 1 To 10
Circle(905, DispY + 12 + (i*40), DigPixelSize, RGB(0,Outputs(i) * 255,0))
DrawText(930, DispY + (i*40), Right(Str(i),1) + " (" + StrD(Outputs(i),2) + ")", RGB(0,Outputs(i) * 255,0) , RGB(30,30,30))
;weights from hidden layer
For j = 0 To HiddenCount
If ANNOutputWeights(j,i) < 0
NegLevel = (1 / (1 + Pow(#Exp,(-1 * ANNOutputWeights(j,i))))) ;* -1
PosLevel = 0
Else
NegLevel = 0
PosLevel = (1 / (1 + Pow(#Exp,(-1 * ANNOutputWeights(j,i)))))
EndIf
LineXY(600, DispY - 50 + (j*25), 900, DispY + 10 + (i*40), RGB(NegLevel * 255,PosLevel * 255,0))
Next
Next
;Update text labels
DrawText(900, 650, "Epoch: " + Str(frame/10), RGB(0,255,0) , RGB(30,30,30))
TmpAvgErr.d = TmpAvgErr + MeanSqrErr()
If Digitloop = 10
AvgErr.d = TmpAvgErr / 10
TmpAvgErr = 0
EndIf
DrawText(900, 670, "Avg Err: " + StrD(AvgErr,6), RGB(0,255,0) , RGB(30,30,30))
Circle(12, 697, DigPixelSize-1, RGB(0,127,255))
DrawText(30, 690, "Bias ", RGB(0,255,0) , RGB(30,30,30))
Circle(12, 717, DigPixelSize-1, RGB(0,255,0))
DrawText(30, 710, "Neuron (Green level shows activiation intensity)", RGB(0,255,0) , RGB(30,30,30))
LineXY(5, 757, 20, 757, RGB(255,0,0))
LineXY(5, 737, 20, 737, RGB(0,255,0))
DrawText(30, 730, "Weight (Green level shows Positive Intensity)", RGB(0,255,0) , RGB(30,30,30))
DrawText(30, 750, "Weight (Red level shows negative Intensity)", RGB(255,0,0) , RGB(30,30,30))
Delay(RefreshDelay)
StopDrawing()
SetGadgetState(0, ImageID(0))
Repeat
event.l = WindowEvent()
Select event
Case #PB_Event_Gadget
Select EventMenu() ; To see which menu has been selected
Case #CmdSlow
RefreshDelay = 400
Case #CmdMed
RefreshDelay = 50
Case #CmdFast
RefreshDelay = 0
Case #CmdRestart
ANNCreate(InputCount, HiddenCount, OutputCount, LearningRate)
frame = 1
Digitloop = 1
EndSelect
EndSelect
Until event = 0 Or event = #PB_Event_CloseWindow
Until event = #PB_Event_CloseWindow
EndIf
EndProcedure
Procedure MakeArray(Array TheArray.d(1), CSV.s)
CSVLoop.l
FieldCount.l
FieldCount = CountString(CSV, ",") + 1
ReDim TheArray(FieldCount)
For CSVLoop = 1 To FieldCount
TheArray(CSVLoop) = ValD(StringField(CSV,CSVLoop ,"," ))
Next
EndProcedure