     with Text_DC; use Text_DC;

     package body Backpro is
     ----------------------------------------------------------------------
     ----------------------------------------------------------------------

     procedure Backpropagation (
       Weights       : in out Matrix_Type;
       Inputs_Train  : in     Matrix_Type;
       Outputs_Train : in     Matrix_Type;
       Learning_Rate : in     float       := 0.01 ) is
     ----------------------------------------------------------------------
     -- Weight matrix is NxN where N = number of neurons in net.
     -- Assumes already layered by having weights set to 0.0 in matrix.
     -- Assumes outputs are the last neurons in the range for backprop.
     -- Inputs'range ( 1 ) used to determine placement of inputs.
     -- Example Matrices for "And" function
     -- Inputs_Train ( 1..4, 1..2 )     Outputs_Train ( 1..4, 3..3 )
     --   ( ( 0, 0 ),                     ( ( 0 ),
     --     ( 0, 1 ),                       ( 0 ),
     --     ( 1, 0 ),                       ( 0 ),
     --     ( 1, 1 ) )                      ( 1 ) )
     -- Inputs_Train'range ( 1 ) must equal Outputs_Train'range ( 1 ),
     -- that is, training set size must be equal for inputs and outputs.
     ----------------------------------------------------------------------
       Weights_Old : Matrix_Type ( Weights'range, Weights'range );
       Out_Actual : Vector_Type ( Weights'range ) := ( others => 0.0 );
       Stable : boolean;
       Delta_Vector : Vector_Type ( Weights'range );
       Weighted_Sum_Post_Deltas : float;
     begin
       for Train_Set in Inputs_Train'range ( 1 ) loop
	 Weights_Old := Weights;
     -- Setting input neurons to the training values for the training set.
	 for Input_Neuron in Inputs_Train'range ( 2 ) loop
	   Out_Actual ( Input_Neuron )
	     := Inputs_Train ( Train_Set, Input_Neuron );
	 end loop;
     -- Propagate inputs until network is stable (assumes feed-forward).
	 loop
	   Propagate ( Stable, Out_Actual, Weights );
	   exit when Stable;
	 end loop;
     -- Backpropagate the delta matrix.
	 for Neuron in reverse Weights'range ( 1 ) loop
	   if Neuron in Outputs_Train'range then
	     Weighted_Sum_Post_Deltas := Outputs_Train ( Train_Set, Neuron )
	       - Out_Actual ( Neuron );
	   else
	     Weighted_Sum_Post_Deltas := 0.0;
	     for Post_Neuron in reverse ( Neuron + 1 )..Weights'last loop
	       Weighted_Sum_Post_Deltas := Weighted_Sum_Post_Deltas
		 + Delta_Vector ( Post_Neuron )
		 * Weights ( Post_Neuron, Neuron );
	     end loop;
	   end if;
	   Delta_Vector ( Neuron )
	     := Out_Actual ( Neuron ) * ( 1.0 - Out_Actual ( Neuron ) )
	       * Weighted_Sum_Post_Deltas;
	 end loop;
     -- Modify the weights.
	 for Neuron in Weights'range ( 1 ) loop
	   for Pre_Neuron in Weights'range ( 2 ) loop
	     Weights ( Neuron, Pre_Neuron )
	       := Weights ( Neuron, Pre_Neuron )
	       + Learning_Rate * Delta_Vector ( Neuron )
	       * Out_Actual ( Pre_Neuron );
	   end loop;
	 end loop;
     -- Exit when stable.
	 exit when Weights = Weights_Old;
       end loop;
     end Backpropagation;

     procedure Demo is
     ----------------------------------------------------------------------
       Neurons : positive := 2;
       Inputs  : positive;
       Outputs : positive := 1;
       Layers  : positive := 2;
       Weight  : float := 1.0;
       Pairs   : positive;
     begin
       Ask ( Neurons, "Please enter the number of neurons in a layer ",
	 Neurons );
       Ask ( Layers,
	 "Please enter the number of layers in the feed-forward network ",
	 Layers );
       Weight := Ask ( "Please enter an initial value for the weights " );
       Put_Line ( "Your initial weight matrix follows:" );
       Put_Line ( Feedforward ( Neurons, Layers, Weight ) );
       Pause;
       Ask ( Inputs, "Please enter the number of input neurons ",
	 Neurons, 1, Neurons );
       Ask ( Outputs, "Please enter the number of output neurons ",
	 Outputs, 1, Neurons );
       Ask ( Pairs, "Please enter the number of training pairs ",
	 Inputs ** 2 );
	 for Train in 1..Train_Input loop
	   Put_Line ( "Please enter input training vector"
	     & positive'image ( Train )
	     & " (example:  1.0 -3.4 1.0e-10)." );
	   for index in Neurons ( 1 ) loop
	     Get ( Training_Input ( Train, index ) );
	   end loop;
	 end loop;
	 for Train in Training_Output'range ( 1 ) loop
	   Put_Line ( "Please enter output training vector"
	     & positive'image ( Train )
	     & " (example:  1.0 -3.4 1.0e-10)." );
	   for index in Training_Ouput'range ( 2 ) loop
	     Get ( Training_Input ( Train, index ) );
	   end loop;
	 end loop;
	 Backprop (
	   Feedforward ( Neurons, Layers, Weight ),
	   Training_Input, Training_Output );
	 Put_Line ( "The trained weight matrix is " );
	 Put_Line;
	 Put_Line ( W );
	 Pause;
       end;
     end Demo;

     function Feedforward (
       Neurons : in     positive := 2;
       Layers  : in     positive := 2;
       Weight  : in     float    := 1.0 )
       return Matrix_Type is
     ----------------------------------------------------------------------
     -- Generates a feed-forward weight matrix.
     ----------------------------------------------------------------------
       W : Matrix_Type ( Neurons * Layers, Neurons * Layers )
	 := ( others => ( others => Weight ) );
     begin
     -- Eliminate self feedback connections.
       for Neuron in W'range loop
	 for Pre_Neuron in Neuron..W'last loop
	   W ( Neuron, Pre_Neuron ) := 0.0;
	 end loop;
       end loop;
     -- Eliminate connections to self and neurons within the same layer.
       for Neuron in W'range loop
	 Prior := ( Neuron / Neurons ) * Neurons;
	 for Pre_Neuron in ( Prior + 1 )..( Prior + Neurons ) loop
	   W ( Neuron, Pre_Neuron ) := 0.0;
	 end loop;
       end loop;
     end Feedforward;

     procedure Propagate (
       Stable        :    out boolean;
       Output_Vector : in out Vector_Type;
       Weights       : in     Matrix_Type ) is
     ----------------------------------------------------------------------
       Output_Vector_Old : constant Vector_Type ( Output_Vector'range )
	 := Output_Vector;
       Weighted_Sum : float;
     begin
       for Neuron in Output_Vector'range loop
	 Weighted_Sum := 0.0;
	 for Pre_Neuron in Output_Vector'range loop
	   Weighted_Sum := Weighted_Sum + Weights ( Neuron, Pre_Neuron )
	     * Output_Vector_Old ( Pre_Neuron );
	 end loop;
	 Output_Vector ( Neuron ) := Sigmoid ( Weighted_Sum );
       end loop;
       Stable := Output_Vector = Output_Vector_Old;
     end Propagate;

     function Sigmoid (
       F : float )
       return float is
     ----------------------------------------------------------------------
     begin
       return 1.0 / ( 1.0 + Exp ( -F ) );
     end Sigmoid;

     ----------------------------------------------------------------------
     ----------------------------------------------------------------------
     end Backpro;
