1

How to represent data with complete scalar range in the first state then represent it as zero to one in the next state while using the same memory space?

Any approaches to the problem is appreciated, the example procedures does not have to be like that if solution requires them to change.

Example

Reading values from a file and then normalize it. Float_Array is for raw value with any range that comes directly from file. Feature_Array is for normalized values.

type Float_Array is array (Integer range <>) of Float;
type Feature is new Float range 0.0 .. 1.0;
type Feature_Array is array (Integer range <>) of Feature;

The first step is to read floats into an Float_Array and finding max and min value.

procedure Read (Name : String; Result : out Float_Array; Last : out Integer; Min : out Float; Max : out Float) is
   use Ada.Text_IO;
   use Ada.Float_Text_IO;
   F : File_Type;
begin
   Open (F, In_File, Name);
   for I in Result'Range loop
      exit when End_Of_File (F);
      Get (F, Result (I));
      Min := Float'Min (Min, Result (I));
      Max := Float'Max (Max, Result (I));
      Last := I;
   end loop;
   Close (F);
end;

Float_Array is just temporarily being used to read and find min max. The next step is to normalize all values.

function Normalize (Value : Float; Min, Max : Float) return Float is
begin
   return (Value - Min) / (Max - Min);
end;

procedure Normalize (Min : Float; Max : Float; Scale : Float; Result : in out Float_Array) is
begin
   for E of Result loop
      E := Normalize (E, Min, Max) * Scale;
   end loop;
end;

After normalization I want the values to be represented as Feature_Array.

Bad solution that does no range check.

There is no range check so it is not a proper solution. Scaling the values from one to three does not yield range check error. So at this point there is no point to have Feature_Array if there is no range check.

   Last : Integer;
   Data : Float_Array (1 .. 100);
   Min : Float := Float'First;
   Max : Float := Float'Last;
begin
   Read ("frequency.lines_of_float", Data, Last, Min, Max);
   Normalize (Min, Max, 1.0, Data);
   -- Normalize (Min, Max, 3.0, Data);
   declare
      The_Features : Feature_Array (Data'Range) with Address => Data'Address;
   begin
      Put (The_Features);
   end;

I have tried attribute 'Valid on the array i.e. The_Features'Valid but it only works on scalar types. And using 'Valid for range check will involve extra code.

Jossi
  • 1,020
  • 1
  • 17
  • 28
  • Why do you have to use the same memory space? – Jacob Sparre Andersen Feb 29 '16 at 18:18
  • @JacobSparreAndersen Because I have no use for un-normalized data. – Jossi Feb 29 '16 at 19:44
  • Use functions rather than procedures. As a side issue, given your definition of `Feature`, you’d better make sure that `Scale <= 1.0`. – Simon Wright Feb 29 '16 at 20:40
  • @SimonWright In the example I can change scale to larger than 1.0 to try deliberately induce error or testing false positive error. Scale is probably always going to be 1.0 but I put it there to have a more generalized normalization. – Jossi Feb 29 '16 at 21:51
  • What is the problem with Bad Solution? The only problem I see there is useless declaration of Feature_Array and unnecessary declare block (you could simply do Put (Data) considering that Data is already normalized...) – darkestkhan Mar 01 '16 at 13:24
  • @darkestkhan I try to use as small example as possible so I may have left out important information. I am classifying the data with algorithms. Data should never have values outside `0.0 .. 1.0` because the algorithms I am using requires that. – Jossi Mar 01 '16 at 16:39
  • @Jossi I see now what is going on here. But still overlaying arrays here is correct solution. Which is why I don't see this as bad one. Also I seem to have missed that you are using different types here. – darkestkhan Mar 01 '16 at 17:32

2 Answers2

1

I think that I finally understand what is needed here. You want to have variable of normalized type and not of Floats. (in case of floats one would have to constantly do array overlays or have 2 variables pointing to the same address).

  Last : Integer;
  The_Features : Feature_Array (1 .. 100);
  Min : Float := Float'First;
  Max : Float := Float'Last;
begin
  declare
    Data : Float_Array (The_Features'Range) with Address => The_Features'Address;
  begin
    Read ("frequency.lines_of_float", Data, Last, Min, Max);
    Normalize (Min, Max, 1.0, Data);
    -- Normalize (Min, Max, 3.0, Data);
  end;
  Put (The_Features);

This should work but keep in mind that you have to ensure that the result of Normalize is valid.

darkestkhan
  • 561
  • 4
  • 12
  • This code makes more sense than mine however I have tested it and it is the same result as mine that does no range check. Yours is better, `Float_Array` should be local, only temporarily visible for reading and normalization. – Jossi Mar 02 '16 at 17:00
  • Since we are using Ada 2012 we can always add postcondition to Normalize which would perform range check. This would result in runtime error. On the other hand range check failure results in raise of Constraint_Error (iirc) which also is runtime error. – darkestkhan Mar 02 '16 at 23:23
0

It seems that manual range checking is the way to go. I can't find a way to use Ada range checking automatically.

To manually check an array of float is within a range

This uses Ada 2012 - conditional expressions. This is needed sometimes when variables depends on address.

A := (for all E of Item (Item'First .. Last) => E'Valid);
Assert (A, "Elements of Item is not within range.");

Code

with Ada.Text_IO;
with Ada.Float_Text_IO;

procedure Main is

   type Float_Array is array (Integer range <>) of Float;
   type Feature is new Float range 0.0 .. 1.0;
   type Feature_Array is array (Integer range <>) of Feature;

   procedure Read (Name : String; Result : out Float_Array; Last : in out Integer; Min : in out Float; Max : in out Float) is
      use Ada.Text_IO;
      use Ada.Float_Text_IO;
      F : File_Type;
   begin
      Open (F, In_File, Name);
      loop
         exit when End_Of_File (F);
         Last := Last + 1;
         Get (F, Result (Last));
         Skip_Line (F);
         Min := Float'Min (Min, Result (Last));
         Max := Float'Max (Max, Result (Last));
         exit when Last = Result'Last;
      end loop;
      Close (F);
   end;

   function Normalize (Value : Float; Min, Max : Float) return Float is ((Value - Min) / (Max - Min));

   procedure Normalize (Min : Float; Max : Float; Scale : Float; Result : in out Float_Array) is
   begin
      for E of Result loop
         E := Normalize (E, Min, Max) * Scale;
      end loop;
   end;

   procedure Put (Item : Feature_Array) is
      use Ada.Float_Text_IO;
      use Ada.Text_IO;
   begin
      for E of Item loop
         Put (Float (E), 3, 3, 0);
         New_Line;
      end loop;
   end;

   procedure Put (Item : Float_Array) is
      use Ada.Float_Text_IO;
      use Ada.Text_IO;
   begin
      for E of Item loop
         Put (E, 3, 3, 0);
         New_Line;
      end loop;
   end;

   procedure Read (Item : out Feature_Array; Last : in out Integer) with
     Pre => Feature_Array'Component_Size = Float_Array'Component_Size,
     Post => (for all E of Item (Item'First .. Last) => E >= 0.0 and E <= 1.0);

   procedure Read (Item : out Feature_Array; Last : in out Integer) is
      Data : Float_Array (Item'Range) with Address => Item'Address;
      Min : Float := Float'Last;
      Max : Float := Float'First;
   begin
      Read ("f.ssv", Data, Last, Min, Max);
      Ada.Text_IO.Put_Line ("Before normalization.");
      Put (Data (Data'First .. Last));
      Normalize (Min, Max, 1.0, Data (Data'First .. Last));
   end;

   F : Feature_Array (-5 .. 10);
   Last : Integer := F'First - 1;

begin

   Read (F, Last);
   Ada.Text_IO.Put_Line ("After normalization.");
   Put (F (F'First .. Last));

end;

f.ssv

0.1
11.0
-3.0

Result

Before normalization.
  0.100
 11.000
 -3.000
After normalization.
  0.221
  1.000
  0.000
Jossi
  • 1,020
  • 1
  • 17
  • 28