Page 1 of 1

Home made

Posted: Tue Jun 16, 2026 7:46 pm
by ONiX

Code: Select all

program PurePascalAI;

{$MODE OBJFPC} {$H+}

uses
  SysUtils, Math;

type
  TVector = array of Double;
  TMatrix = array of TVector;

  { TNeuralNetwork represents a 3-layer network: Input -> Hidden -> Output }
  TNeuralNetwork = class
  private
    FInputSize: Integer;
    FHiddenSize: Integer;
    FOutputSize: Integer;
    FLearningRate: Double;

    // Weights and Biases
    FWeightsIH: TMatrix; // Input to Hidden
    FWeightsHO: TMatrix; // Hidden to Output
    FBiasesH: TVector;   // Hidden Biases
    FBiasesO: TVector;   // Output Biases

    // Layer States (cached for backpropagation)
    FHiddenActivation: TVector;
    FOutputActivation: TVector;

    function Sigmoid(X: Double): Double;
    function SigmoidDerivative(X: Double): Double;
    procedure InitializeWeights;
  public
    constructor Create(InSize, HideSize, OutSize: Integer; LR: Double);
    procedure ForwardPropagate(const Inputs: TVector);
    procedure BackPropagate(const Inputs, Targets: TVector);
    function Predict(const Inputs: TVector): TVector;
  end;

{ TNeuralNetwork Implementation }

constructor TNeuralNetwork.Create(InSize, HideSize, OutSize: Integer; LR: Double);
begin
  FInputSize := InSize;
  FHiddenSize := HideSize;
  FOutputSize := OutSize;
  FLearningRate := LR;

  // Allocate memory for network structures
  SetLength(FWeightsIH, FInputSize, FHiddenSize);
  SetLength(FWeightsHO, FHiddenSize, FOutputSize);
  SetLength(FBiasesH, FHiddenSize);
  SetLength(FBiasesO, FOutputSize);
  SetLength(FHiddenActivation, FHiddenSize);
  SetLength(FOutputActivation, FOutputSize);

  Randomize;
  InitializeWeights;
end;

function TNeuralNetwork.Sigmoid(X: Double): Double;
begin
  Result := 1.0 / (1.0 + Exp(-X));
end;

function TNeuralNetwork.SigmoidDerivative(X: Double): Double;
begin
  // X is expected to already be passed through the Sigmoid function
  Result := X * (1.0 - X);
end;

procedure TNeuralNetwork.InitializeWeights;
var
  i, j: Integer;
begin
  // Randomize weights between -1.0 and 1.0
  for i := 0 to FInputSize - 1 do
    for j := 0 to FHiddenSize - 1 do
      FWeightsIH[i][j] := Random * 2.0 - 1.0;

  for i := 0 to FHiddenSize - 1 do
    for j := 0 to FOutputSize - 1 do
      FWeightsHO[i][j] := Random * 2.0 - 1.0;

  for j := 0 to FHiddenSize - 1 do
    FBiasesH[j] := Random * 2.0 - 1.0;

  for j := 0 to FOutputSize - 1 do
    FBiasesO[j] := Random * 2.0 - 1.0;
end;

procedure TNeuralNetwork.ForwardPropagate(const Inputs: TVector);
var
  i, j: Integer;
  Sum: Double;
begin
  // Calculate Hidden Layer Activations
  for j := 0 to FHiddenSize - 1 do
  begin
    Sum := FBiasesH[j];
    for i := 0 to FInputSize - 1 do
      Sum := Sum + Inputs[i] * FWeightsIH[i][j];
    FHiddenActivation[j] := Sigmoid(Sum);
  end;

  // Calculate Output Layer Activations
  for j := 0 to FOutputSize - 1 do
  begin
    Sum := FBiasesO[j];
    for i := 0 to FHiddenSize - 1 do
      Sum := Sum + FHiddenActivation[i] * FWeightsHO[i][j];
    FOutputActivation[j] := Sigmoid(Sum);
  end;
end;

procedure TNeuralNetwork.BackPropagate(const Inputs, Targets: TVector);
var
  i, j: Integer;
  OutputErrors: TVector;
  HiddenErrors: TVector;
  OutputGradients: TVector;
  HiddenGradients: TVector;
  Sum: Double;
begin
  SetLength(OutputErrors, FOutputSize);
  SetLength(HiddenErrors, FHiddenSize);
  SetLength(OutputGradients, FOutputSize);
  SetLength(HiddenGradients, FHiddenSize);

  // 1. Compute Output Layer Errors and Gradients
  for i := 0 to FOutputSize - 1 do
  begin
    OutputErrors[i] := Targets[i] - FOutputActivation[i];
    OutputGradients[i] := OutputErrors[i] * SigmoidDerivative(FOutputActivation[i]) * FLearningRate;
  end;

  // 2. Compute Hidden Layer Errors and Gradients
  for i := 0 to FHiddenSize - 1 do
  begin
    Sum := 0.0;
    for j := 0 to FOutputSize - 1 do
      Sum := Sum + OutputErrors[j] * FWeightsHO[i][j];
    HiddenErrors[i] := Sum;
    HiddenGradients[i] := HiddenErrors[i] * SigmoidDerivative(FHiddenActivation[i]) * FLearningRate;
  end;

  // 3. Update Hidden-to-Output Weights and Biases
  for j := 0 to FOutputSize - 1 do
  begin
    for i := 0 to FHiddenSize - 1 do
      FWeightsHO[i][j] := FWeightsHO[i][j] + FHiddenActivation[i] * OutputGradients[j];
    FBiasesO[j] := FBiasesO[j] + OutputGradients[j];
  end;

  // 4. Update Input-to-Hidden Weights and Biases
  for j := 0 to FHiddenSize - 1 do
  begin
    for i := 0 to FInputSize - 1 do
      FWeightsIH[i][j] := FWeightsIH[i][j] + Inputs[i] * HiddenGradients[j];
    FBiasesH[j] := FBiasesH[j] + HiddenGradients[j];
  end;
end;

function TNeuralNetwork.Predict(const Inputs: TVector): TVector;
begin
  ForwardPropagate(Inputs);
  Result := FOutputActivation;
end;

{ Main Execution Program }

var
  Brain: TNeuralNetwork;
  XOR_Inputs: array[0..3] of TVector;
  XOR_Targets: array[0..3] of TVector;
  Epoch, i: Integer;
  Outputs: TVector;

begin
  // Define dataset for the XOR Gate problem
  XOR_Inputs[0] := TVector.Create(0.0, 0.0); XOR_Targets[0] := TVector.Create(0.0);
  XOR_Inputs[1] := TVector.Create(0.0, 1.0); XOR_Targets[1] := TVector.Create(1.0);
  XOR_Inputs[2] := TVector.Create(1.0, 0.0); XOR_Targets[2] := TVector.Create(1.0);
  XOR_Inputs[3] := TVector.Create(1.0, 1.0); XOR_Targets[3] := TVector.Create(0.0);

  // Instantiating the AI: 2 inputs, 3 hidden neurons, 1 output, learning rate = 0.5
  Brain := TNeuralNetwork.Create(2, 3, 1, 0.5);
  try
    Writeln('Training the AI model to learn XOR...');
    
    // Train for 20,000 iterations (epochs)
    for Epoch := 1 to 20000 do
    begin
      for i := 0 to 3 do
      begin
        Brain.ForwardPropagate(XOR_Inputs[i]);
        Brain.BackPropagate(XOR_Inputs[i], XOR_Targets[i]);
      end;
    end;

    Writeln('Training Complete! Testing predictions:');
    Writeln('-------------------------------------');
    for i := 0 to 3 do
    begin
      Outputs := Brain.Predict(XOR_Inputs[i]);
      Writeln(Format('Input: [%.0f, %.0f] -> Target: %.0f -> AI Prediction: %.4f', 
        [XOR_Inputs[i][0], XOR_Inputs[i][1], XOR_Targets[i][0], Outputs[0]]));
    end;

  finally
    Brain.Free;
  end;
  
  Readln;
end.

Re: Home made

Posted: Tue Jun 16, 2026 7:47 pm
by ONiX
Architectural BreakdownData Layout: Matrix manipulations rely on standard, dynamically-allocated array of array of Double. This eliminates pointer layout confusion while remaining memory safe.The Activation Engine: Uses the native standard Math unit's Exp function to build the classic Sigmoid mathematical function (\(1 / (1 + e^{-x})\)).Backpropagation Architecture: The program tracks hidden-layer error states by calculating the transpose of output weight products without pulling external linear algebra dependencies.How to Compile and RunSave the code into a file named PurePascalAI.lpr (or .pas).Open your terminal and run the Free Pascal Compiler: fpc PurePascalAI.lprExecute the native binary. The AI output error will visibly diminish, giving final predictions incredibly close to 0.0 or 1.0.