Kazuto Kirigaya
Kazuto Kirigaya

Reputation: 93

Delphi - List Index Out Of Bounds(4)

I know this question has been brought up a million times, however, I cant understand why this code is throwing the error, I have tracked down the culprit FOR loop causing the error, however, I don't see anything wrong with it.

I'm getting the error - "List Index Out Of Bounds(4)"

function TNetwork.FeedForward(InputVals : array of Real) : Real;
var
  I : Integer;
begin

  for I := 0 to Length(InputVals)-1 do
  begin
    Input[I].Input(InputVals[I]);
  end;

  for I := 0 to Length(Hidden)-1 do
  begin
    Hidden[I].CalcOutput;
  end;

  Output.CalcOutput;

  Result := Output.GetOutput;
  end;

The error occurs on the second For Loop, here is where I set the size of the hidden array.

constructor TNetwork.Create(Inputs, HiddenTotal : Integer);
var
  C : TConnection;
  I, J : Integer;
begin
  LEARNING_CONSTANT := 0.5;

  SetLength(Input,Inputs+1);
  SetLength(Hidden,HiddenTotal+1);

So, as I see it, the loop executes only three times, so why is it trying to index the 4th space? never mind why, more importantly, HOW?

If someone could shed some light on the cause, and a possible fix, I would be ever grateful

For completion's sake, here is the complete unit..

unit NeuralNetwork_u;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, Math;

type
  TConnection = Class;
  TNeuron = class(TObject)
  protected
    Output : Real;
    Connections : TList;
    isBias : Boolean;
  public
    Constructor Create; overload;
    Constructor Create(BiasValue : Integer); overload;
    procedure CalcOutput;
    procedure AddConnection( Con : TConnection );
    function GetOutput : Real;
    Function F( X : Real ) : Real;
  end;

  TConnection = class
  private
    nFrom, nTo : TNeuron;
    Weight : Real;
  public
    constructor Create(a , b : TNeuron) ; overload;
    constructor Create(a, b : TNeuron ; W : Real) ; overload;
    function GetFrom : TNeuron;
    function GetTo : TNeuron;
    function GetWeight : Real;
    procedure AdjustWeight(DeltaWeight : Real);
  end;


type TInputNeuron = class(TNeuron)
public
  procedure Input (D : Real);
end;

type THiddenNeuron = class(TNeuron)
private
public
end;

type TOutputNeuron = Class(TNeuron)
private
public
end;

type TNetwork = class(TObject)
private
  LEARNING_CONSTANT : Real;
public
  Input : array of TInputNeuron;
  Hidden : array of THiddenNeuron;
  Output : TOutputNeuron;

  constructor Create(Inputs,HiddenTotal : Integer);
  function FeedForward(InputVals : array of Real) : Real;
  function Train(Inputs : array of Real ; Answer : Real) : Real;
  function TrainOnFile(Epochs : Integer ; TrainingFile : String) : Real;
end;

implementation

constructor TNeuron.Create;
begin
  Output := 0;
  Connections := TList.Create;
  isBias := False;
end;

Constructor TNeuron.Create(BiasValue : Integer);
begin
  Output := BiasValue;
  Connections := TList.Create;
  isBias := True;
end;

procedure TNeuron.CalcOutput;
var
  Sum : Real;
  Bias : Real;
  C : TConnection ;
  NeuronFrom, NeuronTo : TNeuron;
  I : Integer;
begin
  if isBias then

  else
  begin
    Sum := 0;
    Bias := 0;
    for I := 0 to Connections.Count do
    begin
      C := Connections[I];
      NeuronFrom := C.GetFrom;
      NeuronTo := C.GetTo;
      if NeuronTo = self then
      begin
        if NeuronFrom.isBias then
        begin
          Bias := NeuronFrom.GetOutput * C.GetWeight;
        end
        else
        begin
          Sum := Sum + NeuronFrom.GetOutput * C.GetWeight;
        end;
      end;
    end;
    Output := F(Bias + Sum);
  end;  
end;

procedure TNeuron.AddConnection(Con : TConnection);
begin
  Connections.Add(Con) ;
end;

function TNeuron.GetOutput : Real;
begin
  Result := Output;
end;

function TNeuron.F( X : Real ) : Real;
begin
  Result := 1.0 /(1.0 + Exp(-X));
end;

procedure TInputNeuron.Input ( D : Real);
begin
  Output := D;
end;

constructor TConnection.Create(a, b : TNeuron);
begin
  nFrom := a;
  nTo := b;
  Weight := Random * 2 - 1;
end;

constructor TConnection.Create(a, b : TNeuron ; w : Real);
begin
  nFrom := a;
  nTo := b;
  Weight := w;
end;

function TConnection.GetFrom : TNeuron;
begin
  Result := nFrom;
end;

function TConnection.GetTo : TNeuron;
begin
  Result := nTo;
end;

function TConnection.GetWeight;
begin
  Result := Weight;
end;

procedure Tconnection.AdjustWeight(DeltaWeight : Real);
begin
  Weight := Weight + DeltaWeight;
end;

constructor TNetwork.Create(Inputs, HiddenTotal : Integer);
var
  C : TConnection;
  I, J : Integer;
begin
  LEARNING_CONSTANT := 0.5;

  SetLength(Input,Inputs+1);
  SetLength(Hidden,HiddenTotal+1);

  for I := 0 to Length(Input)-1 do
  begin
    Input[I] := TInputNeuron.Create;
  end;

  for I := 0 to Length(Hidden)-1 do
  begin
    Hidden[I] := THiddenNeuron.Create;
  end;

  Input[Length(Input)-1] := TInputNeuron.Create(1);
  Hidden[Length(Hidden)-1] := THiddenNeuron.Create(1);

  Output := TOutputNeuron.Create;

  for I := 0 to Length(Input)-1 do
  begin
    for J := 0 to Length(Hidden)-1 do
    begin
      C := TConnection.Create(Input[I],Hidden[J]);
      Input[I].AddConnection(C);
      Hidden[J].AddConnection(C);
    end;  
  end;

  for I := 0 to Length(Hidden)-1 do
  begin
    C := TConnection.Create(Hidden[I],Output);
    Hidden[I].AddConnection(C);
    Output.AddConnection(C);
  end;  
end;

function TNetwork.FeedForward(InputVals : array of Real) : Real;
var
  I : Integer;
begin
  for I := 0 to Length(InputVals)-1 do
  begin
    Input[I].Input(InputVals[I]);
  end;

  for I := 0 to Length(Hidden)-1 do
  begin
    Hidden[I].CalcOutput;
  end;

  Output.CalcOutput;

  Result := Output.GetOutput;
end;

function TNetwork.Train(Inputs : array of Real ; Answer : Real) : Real;
var
  rResult : Real;
  deltaOutput, rOutput, deltaWeight, Sum, deltaHidden : Real;
  Connections : TList;
  C : TConnection;
  Neuron : TNeuron;
  I, J : Integer;
begin
  rResult := FeedForward(Inputs);
  deltaOutput := rResult * (1 - rResult) * (Answer - rResult);
  Connections := Output.Connections;
  for I := 0 to Connections.Count do
  begin
    C := Connections[I];
    Neuron := C.GetFrom;
    rOutput := Neuron.Output;
    deltaWeight := rOutput * deltaOutput;
    C.AdjustWeight(LEARNING_CONSTANT * deltaWeight);
  end;

  for I := 0 to Length(Hidden) do
  begin
    Connections := Hidden[I].Connections;
    Sum := 0;
    for J := 0 to Connections.Count do
    begin
      C := Connections[J];
      if c.GetFrom = Hidden[I] then
      begin
        Sum := Sum + (C.GetWeight * deltaOutput);
      end;
    end;

    for J := 0 to Connections.Count do
    begin
      C := Connections[I];
      if C.GetTo = Hidden[I] then
      begin
        rOutput := Hidden[I].GetOutput;
        deltaHidden := rOutput * ( 1 - rOutput);
        deltaHidden := deltaHidden * Sum;
        Neuron := C.GetFrom;
        deltaWeight := Neuron.GetOutput * deltaHidden;
        C.AdjustWeight(LEARNING_CONSTANT * deltaWeight);
      end;
    end;
  end;
  Result := rResult;
end;

function TNetwork.TrainOnFile(Epochs : Integer ; TrainingFile : string) : Real;
var
  FileT : TStringList;
  Inputss : array of Real;
  Outputss : Real;
  I, C : Integer;
  sTemp : String;
  NumInputs, NumOutputs : Integer;
begin
  // Load File
  FileT := TStringList.Create;
  try
    FileT.LoadFromFile(TrainingFile);
  except
    raise Exception.Create('Training File Does Not Exist');
  end;

  for I := 0 to FileT.Count-1 do
  begin
    sTemp := FileT[I];
    if I = 0 then
    begin
      // get Configurators
      Delete(sTemp,1,Pos(' ',stemp));   // no Longer need training Set count
      NumInputs := StrToInt(Copy(sTemp,1,Pos(' ',sTemp)-1));
      Delete(sTemp,1,Pos(' ',sTemp));
      NumOutputs := StrToInt(Copy(sTemp,1,Length(sTemp)));
      SetLength(Inputss,NumInputs+1);
    end
    else
    begin
      for C := 0 to NumInputs-1 do
      begin
        Inputss[C] := StrToFloat(Copy(sTemp,1,Pos(' ',sTemp)-1));
        Delete(sTemp,1,Pos(' ',sTemp));
      end;
      Outputss := StrToFloat(Copy(sTemp,1,Length(sTemp)));

      Train(Inputss,Outputss);
    end;
  end;
end;

end.

Upvotes: 3

Views: 17115

Answers (2)

MartynA
MartynA

Reputation: 30735

This is a minor supplement, not alternative, to @David's answer.

Especially when dynamic arrays are involved, doing something like

  for I := 0 to Length(Hidden)-1 do
  begin
    Hidden[I].CalcOutput;
  end;

is a kind of premature optimisation, because if an exception occurs on the

    Hidden[I].CalcOutput;

line, it may not be easy for someone not fully au fait with Delphi's debugger, how to use it and what the exception message is actually referring to (which isn't always obvious) to tell whether the exception is arising on the indexing of the Hidden[] array, or the call to CalcOutput on its Ith item. So, at least for debugging purposes, it can be useful to do something like this:

var
  H :  THiddenNeuron; 
[...]
  for I := 0 to Length(Hidden) -1 do
  begin
    H := Hidden[I];
    H.CalcOutput;
  end;

and then it becomes easy to distinguish the two possible places the original code might be going wrong.

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 613481

for I := 0 to Connections.Count do

You run off the end of the list here. Valid indices are 0 to Connections.Count-1 inclusive. You go one too far.

You make this mistake repeatedly. You need to fix it everywhere, of course.

The list index out of bounds error is generally seen when you perform an out of bounds access of a collection class like TList or TStringList.

On the other hand, array bounds errors are unpredictable unless you have enabled range checking. If you do that, and you should, then you get a runtime error for such events. You'll want to enable range checking.

Upvotes: 4

Related Questions