Schema changes for CLAMP, PAD float attributes

* Float attributes now serialized as uint8 vectors, but treated as floats at input/output to serialization

Signed-off-by: James Ward <james.ward@arm.com>
Change-Id: I417b0fabe0ef11fea263fe937b57d49bbfdb00da
diff --git a/python/serializer/tosa_serializer.py b/python/serializer/tosa_serializer.py
index e8311ce..f579df2 100644
--- a/python/serializer/tosa_serializer.py
+++ b/python/serializer/tosa_serializer.py
@@ -13,10 +13,11 @@
 #    limitations under the License.
 
 import os
+import struct
+import serializer.tosa_serializer as ts
 import json
 import flatbuffers
 import numpy as np
-import struct
 from enum import IntEnum, unique
 from tosa import (
     TosaGraph,
@@ -197,7 +198,7 @@
         self.ints.append((a.AddWeightZp, weight_zp))
         self.ints.append((a.AddAccumDtype, accum_dtype))
 
-    def PadAttribute(self, padding, pad_const_int, pad_const_fp):
+    def PadAttribute(self, serializer_builder, padding, pad_const_int, pad_const_fp):
         from tosa import PadAttribute as a, Attribute
 
         self.utype = Attribute.Attribute().PadAttribute
@@ -205,7 +206,14 @@
 
         self.intvecs.append((a.AddPadding, padding))
         self.ints.append((a.AddPadConstInt, pad_const_int))
-        self.floats.append((a.AddPadConstFp, pad_const_fp))
+
+        # pad_const_fp attribute serialized as uint8 vector
+        pad_const_float_as_bytes = struct.pack("<f", pad_const_fp)
+        serialized_pad_const_fp = ts.TosaSerializer.serializeUint8Vec(
+            serializer_builder, pad_const_float_as_bytes
+        )
+
+        self.floats.append((a.AddPadConstFp, serialized_pad_const_fp))
 
     def AxisAttribute(self, axis):
         from tosa import AxisAttribute as a, Attribute
@@ -251,7 +259,7 @@
         self.int16vecs.append((a.AddBorder, border))
         self.ints.append((a.AddMode, mode))
 
-    def ClampAttribute(self, minint, maxint, minfp, maxfp):
+    def ClampAttribute(self, serializer_builder, minint, maxint, minfp, maxfp):
         from tosa import ClampAttribute as a, Attribute
 
         self.utype = Attribute.Attribute().ClampAttribute
@@ -260,8 +268,18 @@
         self.ints.append((a.AddMinInt, minint))
         self.ints.append((a.AddMaxInt, maxint))
 
-        self.ints.append((a.AddMinFp, minfp))
-        self.ints.append((a.AddMaxFp, maxfp))
+        # min/max float attributes serialized as uint8 vectors
+        minfp_bytes = struct.pack("<f", minfp)
+        maxfp_bytes = struct.pack("<f", maxfp)
+        serialized_minfp_bytes = ts.TosaSerializer.serializeUint8Vec(
+            serializer_builder, minfp_bytes
+        )
+        serialized_maxfp_bytes = ts.TosaSerializer.serializeUint8Vec(
+            serializer_builder, maxfp_bytes
+        )
+
+        self.floats.append((a.AddMinFp, serialized_minfp_bytes))
+        self.floats.append((a.AddMaxFp, serialized_maxfp_bytes))
 
     def RescaleAttribute(
         self, input_zp, output_zp, multiplier, shift, scale32, double_round, per_channel
@@ -477,9 +495,11 @@
                 np_arr = np.array(self.data, dtype=np.float16)
                 u8_data.extend(np_arr.view(np.uint8))
             elif self.dtype == DType.FP32 or self.dtype == DType.BF16:
-                for val in self.data:
-                    b = struct.pack("!f", val)
-                    u8_data.extend([b[3], b[2], b[1], b[0]])
+                # for val in self.data:
+                #     b = struct.pack("!f", val)
+                #     u8_data.extend([b[3], b[2], b[1], b[0]])
+                np_arr = np.array(self.data, dtype=np.float32)
+                u8_data.extend(np_arr.view(np.uint8))
             elif self.dtype == TosaDType.DType:
                 # Serialize DType enum data as uint8 bytes
                 for val in self.data:
diff --git a/python/tosa/ClampAttribute.py b/python/tosa/ClampAttribute.py
index 58d1f0a..7937944 100644
--- a/python/tosa/ClampAttribute.py
+++ b/python/tosa/ClampAttribute.py
@@ -43,18 +43,58 @@
         return 0
 
     # ClampAttribute
-    def MinFp(self):
+    def MinFp(self, j):
         o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
         if o != 0:
-            return self._tab.Get(flatbuffers.number_types.Float32Flags, o + self._tab.Pos)
-        return 0.0
+            a = self._tab.Vector(o)
+            return self._tab.Get(flatbuffers.number_types.Uint8Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 1))
+        return 0
 
     # ClampAttribute
-    def MaxFp(self):
+    def MinFpAsNumpy(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
+        if o != 0:
+            return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint8Flags, o)
+        return 0
+
+    # ClampAttribute
+    def MinFpLength(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
+        if o != 0:
+            return self._tab.VectorLen(o)
+        return 0
+
+    # ClampAttribute
+    def MinFpIsNone(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
+        return o == 0
+
+    # ClampAttribute
+    def MaxFp(self, j):
         o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10))
         if o != 0:
-            return self._tab.Get(flatbuffers.number_types.Float32Flags, o + self._tab.Pos)
-        return 0.0
+            a = self._tab.Vector(o)
+            return self._tab.Get(flatbuffers.number_types.Uint8Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 1))
+        return 0
+
+    # ClampAttribute
+    def MaxFpAsNumpy(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10))
+        if o != 0:
+            return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint8Flags, o)
+        return 0
+
+    # ClampAttribute
+    def MaxFpLength(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10))
+        if o != 0:
+            return self._tab.VectorLen(o)
+        return 0
+
+    # ClampAttribute
+    def MaxFpIsNone(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10))
+        return o == 0
 
 def ClampAttributeStart(builder): builder.StartObject(4)
 def Start(builder):
@@ -65,12 +105,18 @@
 def ClampAttributeAddMaxInt(builder, maxInt): builder.PrependInt32Slot(1, maxInt, 0)
 def AddMaxInt(builder, maxInt):
     return ClampAttributeAddMaxInt(builder, maxInt)
-def ClampAttributeAddMinFp(builder, minFp): builder.PrependFloat32Slot(2, minFp, 0.0)
+def ClampAttributeAddMinFp(builder, minFp): builder.PrependUOffsetTRelativeSlot(2, flatbuffers.number_types.UOffsetTFlags.py_type(minFp), 0)
 def AddMinFp(builder, minFp):
     return ClampAttributeAddMinFp(builder, minFp)
-def ClampAttributeAddMaxFp(builder, maxFp): builder.PrependFloat32Slot(3, maxFp, 0.0)
+def ClampAttributeStartMinFpVector(builder, numElems): return builder.StartVector(1, numElems, 1)
+def StartMinFpVector(builder, numElems):
+    return ClampAttributeStartMinFpVector(builder, numElems)
+def ClampAttributeAddMaxFp(builder, maxFp): builder.PrependUOffsetTRelativeSlot(3, flatbuffers.number_types.UOffsetTFlags.py_type(maxFp), 0)
 def AddMaxFp(builder, maxFp):
     return ClampAttributeAddMaxFp(builder, maxFp)
+def ClampAttributeStartMaxFpVector(builder, numElems): return builder.StartVector(1, numElems, 1)
+def StartMaxFpVector(builder, numElems):
+    return ClampAttributeStartMaxFpVector(builder, numElems)
 def ClampAttributeEnd(builder): return builder.EndObject()
 def End(builder):
     return ClampAttributeEnd(builder)
\ No newline at end of file
diff --git a/python/tosa/PadAttribute.py b/python/tosa/PadAttribute.py
index 8d49d5a..23698d1 100644
--- a/python/tosa/PadAttribute.py
+++ b/python/tosa/PadAttribute.py
@@ -63,11 +63,31 @@
         return 0
 
     # PadAttribute
-    def PadConstFp(self):
+    def PadConstFp(self, j):
         o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
         if o != 0:
-            return self._tab.Get(flatbuffers.number_types.Float32Flags, o + self._tab.Pos)
-        return 0.0
+            a = self._tab.Vector(o)
+            return self._tab.Get(flatbuffers.number_types.Uint8Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 1))
+        return 0
+
+    # PadAttribute
+    def PadConstFpAsNumpy(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
+        if o != 0:
+            return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint8Flags, o)
+        return 0
+
+    # PadAttribute
+    def PadConstFpLength(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
+        if o != 0:
+            return self._tab.VectorLen(o)
+        return 0
+
+    # PadAttribute
+    def PadConstFpIsNone(self):
+        o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
+        return o == 0
 
 def PadAttributeStart(builder): builder.StartObject(3)
 def Start(builder):
@@ -81,9 +101,12 @@
 def PadAttributeAddPadConstInt(builder, padConstInt): builder.PrependInt32Slot(1, padConstInt, 0)
 def AddPadConstInt(builder, padConstInt):
     return PadAttributeAddPadConstInt(builder, padConstInt)
-def PadAttributeAddPadConstFp(builder, padConstFp): builder.PrependFloat32Slot(2, padConstFp, 0.0)
+def PadAttributeAddPadConstFp(builder, padConstFp): builder.PrependUOffsetTRelativeSlot(2, flatbuffers.number_types.UOffsetTFlags.py_type(padConstFp), 0)
 def AddPadConstFp(builder, padConstFp):
     return PadAttributeAddPadConstFp(builder, padConstFp)
+def PadAttributeStartPadConstFpVector(builder, numElems): return builder.StartVector(1, numElems, 1)
+def StartPadConstFpVector(builder, numElems):
+    return PadAttributeStartPadConstFpVector(builder, numElems)
 def PadAttributeEnd(builder): return builder.EndObject()
 def End(builder):
     return PadAttributeEnd(builder)
\ No newline at end of file