Main Compliance testing for CONV3D

Signed-off-by: evacha01 <evan.chandler@arm.com>
Change-Id: Ie05f88db15cd07fd5483ab669329d7048bd3349c
diff --git a/reference_model/src/generate/generate_dot_product.cc b/reference_model/src/generate/generate_dot_product.cc
index 046007e..7337969 100644
--- a/reference_model/src/generate/generate_dot_product.cc
+++ b/reference_model/src/generate/generate_dot_product.cc
@@ -736,6 +736,147 @@
             return false;
     }
 }
+
+//---------------------------------------------------------------------------//
+//                              Conv3D                                       //
+//---------------------------------------------------------------------------//
+
+template <typename DataType>
+bool generateConv3DInput(const TosaReference::GenerateConfig& cfg,
+                         TosaReference::IDotProductGenerator& generator,
+                         DataType* data,
+                         size_t size)
+{
+    if (cfg.dotProductInfo.kernel.size() != 3 || cfg.dotProductInfo.kernel[0] <= 0 ||
+        cfg.dotProductInfo.kernel[1] <= 0 || cfg.dotProductInfo.kernel[2] <= 0)
+    {
+        WARNING("[Generator][DP][Conv3D][Input] Missing or incorrect kernel size information.");
+        return false;
+    }
+    if (cfg.shape.size() != 5)
+    {
+        WARNING("[Generator][DP][Conv3D][Input] Tensor shape expected 5 dimensions.");
+        return false;
+    }
+
+    const int64_t T   = TosaReference::numElementsFromShape(cfg.shape);
+    const uint32_t ID = cfg.shape[1];
+    const uint32_t IH = cfg.shape[2];
+    const uint32_t IW = cfg.shape[3];
+    const uint32_t IC = cfg.shape[4];
+    const uint32_t KD = cfg.dotProductInfo.kernel[0];
+    const uint32_t KH = cfg.dotProductInfo.kernel[1];
+    const uint32_t KW = cfg.dotProductInfo.kernel[2];
+
+    for (int64_t t = 0; t < T; ++t)
+    {
+        uint32_t ic = t % IC;
+        uint32_t ix = (t / IC) % IW;
+        uint32_t iy = ((t / IC) / IW) % IH;
+        uint32_t id = (((t / IC) / IW) / IH) % ID;
+        uint32_t k  = (((id % KD) * KH + (iy % KH)) * KW + (ix % KW)) * IC + ic;
+
+        data[t] = static_cast<DataType>(generator(k));
+    }
+    return true;
+}
+
+template <typename DataType>
+bool generateConv3DWeight(const TosaReference::GenerateConfig& cfg,
+                          TosaReference::IDotProductGenerator& generator,
+                          DataType* data,
+                          size_t size)
+{
+    if (cfg.shape.size() != 5)
+    {
+        WARNING("[Generator][DP][Conv3D][Weight] Tensor shape expected 5 dimensions.");
+        return false;
+    }
+
+    const int64_t T   = TosaReference::numElementsFromShape(cfg.shape);
+    const uint32_t KD = cfg.shape[0];
+    const uint32_t KH = cfg.shape[1];
+    const uint32_t KW = cfg.shape[2];
+    const uint32_t IC = cfg.shape[3];
+
+    for (int64_t t = 0; t < T; ++t)
+    {
+        uint32_t ic = t % IC;
+        uint32_t kx = (t / IC) % KW;
+        uint32_t ky = ((t / IC) / KW) % KH;
+        uint32_t kd = (((t / IC) / KW) / KH) % KD;
+        uint32_t k  = ((kd * KH + ky) * KW + kx) * IC + ic;
+
+        data[t] = static_cast<DataType>(generator(k));
+    }
+    return true;
+}
+
+template <typename DataType>
+bool generateConv3DBias(const TosaReference::GenerateConfig& cfg,
+                        TosaReference::IDotProductGenerator& generator,
+                        DataType* data,
+                        size_t size)
+{
+    if (cfg.shape.size() != 1)
+    {
+        WARNING("[Generator][DP][Conv3D][Bias] Tensor shape expected 1 dimension.");
+        return false;
+    }
+
+    const uint32_t T = cfg.shape[0];
+
+    for (uint32_t t = 0; t < T; ++t)
+    {
+        data[t] = static_cast<DataType>(generator(2));
+    }
+    return true;
+}
+
+bool generateConv3D(const TosaReference::GenerateConfig& cfg,
+                    TosaReference::IDotProductGenerator& generator,
+                    void* data,
+                    size_t size)
+{
+    switch (cfg.dataType)
+    {
+        case DType::DType_FP32: {
+            float* outData = reinterpret_cast<float*>(data);
+            switch (cfg.inputPos)
+            {
+                case 0:
+                    return generateConv3DInput(cfg, generator, outData, size);
+                case 1:
+                    return generateConv3DWeight(cfg, generator, outData, size);
+                case 2:
+                    return generateConv3DBias(cfg, generator, outData, size);
+                default:
+                    WARNING("[Generator][DP][Conv3D] Invalid input tensor slot position to operator.");
+                    return false;
+            }
+            break;
+        }
+        case DType::DType_FP16: {
+            half_float::half* outData = reinterpret_cast<half_float::half*>(data);
+            switch (cfg.inputPos)
+            {
+                case 0:
+                    return generateConv3DInput(cfg, generator, outData, size);
+                case 1:
+                    return generateConv3DWeight(cfg, generator, outData, size);
+                case 2:
+                    return generateConv3DBias(cfg, generator, outData, size);
+                default:
+                    WARNING("[Generator][DP][Conv3D] Invalid input tensor slot position to operator.");
+                    return false;
+            }
+            break;
+        }
+        default:
+            WARNING("[Generator][DP][Conv3D] Only supports FP32 or FP16.");
+            return false;
+    }
+}
 //---------------------------------------------------------------------------//
 //                              FFT2D                                        //
 //---------------------------------------------------------------------------//
@@ -858,6 +999,8 @@
             return generateDepthwiseConv2D(cfg, *generator, data, size);
         case tosa::Op_TRANSPOSE_CONV2D:
             return generateTransposeConv2D(cfg, *generator, data, size);
+        case tosa::Op_CONV3D:
+            return generateConv3D(cfg, *generator, data, size);
         case tosa::Op_FFT2D:
             return generateFFT2D(cfg, *generator, data, size);
         default:
diff --git a/reference_model/src/generate/generate_utils.cc b/reference_model/src/generate/generate_utils.cc
index 2e40b04..c495fb6 100644
--- a/reference_model/src/generate/generate_utils.cc
+++ b/reference_model/src/generate/generate_utils.cc
@@ -50,6 +50,7 @@
                                  { Op::Op_CONST, "CONST" },
                                  { Op::Op_CONV2D, "CONV2D" },
                                  { Op::Op_DEPTHWISE_CONV2D, "DEPTHWISE_CONV2D" },
+                                 { Op::Op_CONV3D, "CONV3D" },
                                  { Op::Op_EQUAL, "EQUAL" },
                                  { Op::Op_ERF, "ERF" },
                                  { Op::Op_EXP, "EXP" },
diff --git a/reference_model/test/generate_tests.cpp b/reference_model/test/generate_tests.cpp
index 4f62ede..3be402c 100644
--- a/reference_model/test/generate_tests.cpp
+++ b/reference_model/test/generate_tests.cpp
@@ -1224,6 +1224,164 @@
     }
 }
 
+void conv3d_test_FP16(const std::string tosaName[3],
+                      const size_t tosaElements[3],
+                      const std::string templateJsonCfg,
+                      const std::string setStr,
+                      int32_t param,
+                      const std::vector<uint16_t> expected)
+{
+    std::string jsonCfg = templateJsonCfg;
+    update_json_template(jsonCfg, "_SET_", setStr);
+
+    std::vector<half_float::half> buffer(tosaElements[param]);
+    REQUIRE(tgd_generate_data(jsonCfg.c_str(), tosaName[param].c_str(), (void*)buffer.data(), tosaElements[param] * 2));
+    check_output<half_float::half>(buffer, expected);
+}
+
+TEST_CASE("positive - FP16 conv3d dot product (first 3 values)")
+{
+    std::string templateJsonCfg = R"({
+        "tensors" : {
+            "input" : {
+                "generator": "DOT_PRODUCT",
+                "data_type": "FP16",
+                "input_type": "VARIABLE",
+                "shape" : [1, 3, 2, 2, 3],
+                "input_pos": 0,
+                "op" : "CONV3D",
+                "dot_product_info": {
+                    "s": _SET_,
+                    "ks": 27,
+                    "acc_type": "FP16",
+                    "kernel": [3, 1, 3]
+                }
+            },
+            "weight" : {
+                "generator": "DOT_PRODUCT",
+                "data_type": "FP16",
+                "input_type": "CONSTANT",
+                "shape" : [4, 3, 1, 3, 3],
+                "input_pos": 1,
+                "op" : "CONV3D",
+                "dot_product_info": {
+                    "s": _SET_,
+                    "ks": 27,
+                    "acc_type": "FP16"
+                }
+            },
+            "bias" : {
+                "generator": "DOT_PRODUCT",
+                "data_type": "FP16",
+                "input_type": "CONSTANT",
+                "shape" : [ 4 ],
+                "input_pos": 2,
+                "op" : "CONV3D",
+                "dot_product_info": {
+                    "s": _SET_,
+                    "ks": 27,
+                    "acc_type": "FP16"
+                }
+            }
+
+        }
+    })";
+
+    const std::string tosaName[3] = { "input", "weight", "bias" };
+    const size_t tosaElements[3]  = { (1 * 3 * 2 * 2 * 3), (4 * 3 * 1 * 3 * 3), 4 };
+
+    SUBCASE("conv3d, set 0, param 0")
+    {
+        std::vector<uint16_t> expected = { 0xbb33, 0xbb9b, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "0", 0, expected);
+    }
+    SUBCASE("conv3d, set 0, param 1")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x39a8 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "0", 1, expected);
+    }
+    SUBCASE("conv3d, set 0, param 2")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "0", 2, expected);
+    }
+    SUBCASE("conv3d, set 1, param 0")
+    {
+        std::vector<uint16_t> expected = { 0x4e37, 0x4ed1, 0x4f87 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "1", 0, expected);
+    }
+    SUBCASE("conv3d, set 1, param 1")
+    {
+        std::vector<uint16_t> expected = { 0x51fe, 0x5104, 0x4fbf };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "1", 1, expected);
+    }
+    SUBCASE("conv3d, set 1, param 2")
+    {
+        std::vector<uint16_t> expected = { 0x6498, 0x66e0, 0x687d };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "1", 2, expected);
+    }
+    SUBCASE("conv3d, set 2, param 0")
+    {
+        std::vector<uint16_t> expected = { 0x3c00, 0x2bdb, 0xad62 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "2", 0, expected);
+    }
+    SUBCASE("conv3d, set 2, param 1")
+    {
+        std::vector<uint16_t> expected = { 0x3c00, 0x1814, 0x31be };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "2", 1, expected);
+    }
+    SUBCASE("conv3d, set 2, param 2")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "2", 2, expected);
+    }
+    SUBCASE("conv3d, set 3, param 0")
+    {
+        std::vector<uint16_t> expected = { 0x4c00, 0xb92b, 0x30f4 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "3", 0, expected);
+    }
+    SUBCASE("conv3d, set 3, param 1")
+    {
+        std::vector<uint16_t> expected = { 0x4c00, 0x3a2e, 0x3bf5 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "3", 1, expected);
+    }
+    SUBCASE("conv3d, set 3, param 2")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "3", 2, expected);
+    }
+    SUBCASE("conv3d, set 4, param 0")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x5110 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "4", 0, expected);
+    }
+    SUBCASE("conv3d, set 4, param 1")
+    {
+        std::vector<uint16_t> expected = { 0x4384, 0xd1de, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "4", 1, expected);
+    }
+    SUBCASE("conv3d, set 4, param 2")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "4", 2, expected);
+    }
+    SUBCASE("conv3d, set 5, param 0")
+    {
+        std::vector<uint16_t> expected = { 0x490c, 0x4ccf, 0x5046 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "5", 0, expected);
+    }
+    SUBCASE("conv3d, set 5, param 1")
+    {
+        std::vector<uint16_t> expected = { 0xc994, 0x4ca4, 0x4f9f };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "5", 1, expected);
+    }
+    SUBCASE("conv3d, set 5, param 2")
+    {
+        std::vector<uint16_t> expected = { 0x0, 0x0, 0x0 };
+        conv3d_test_FP16(tosaName, tosaElements, templateJsonCfg, "5", 2, expected);
+    }
+}
+
 void fft2d_test_FP32(const std::string tosaName,
                      const size_t tosaElements,
                      const std::string templateJsonCfg,
diff --git a/verif/conformance/tosa_main_profile_ops_info.json b/verif/conformance/tosa_main_profile_ops_info.json
index 067fab7..a53d0c7 100644
--- a/verif/conformance/tosa_main_profile_ops_info.json
+++ b/verif/conformance/tosa_main_profile_ops_info.json
@@ -684,6 +684,7 @@
         "profile": [
             "tosa-mi"
         ],
+        "support_for": [ "lazy_data_gen" ],
         "generation": {
             "standard": {
                 "negative_dim_range": "1,10",
@@ -696,7 +697,7 @@
                         "--target-dtype",
                         "bf16",
                         "--fp-values-range",
-                        "-2.0,2.0",
+                        "-max,max",
                         "--target-shape",
                         "1,7,18,5,4",
                         "--target-shape",
@@ -709,7 +710,7 @@
                         "--target-dtype",
                         "fp32",
                         "--fp-values-range",
-                        "-2.0,2.0",
+                        "-max,max",
                         "--target-shape",
                         "1,2,65539,1,2",
                         "--target-shape",
diff --git a/verif/generator/tosa_test_gen.py b/verif/generator/tosa_test_gen.py
index 68a4e94..9c3cd32 100644
--- a/verif/generator/tosa_test_gen.py
+++ b/verif/generator/tosa_test_gen.py
@@ -325,6 +325,7 @@
             Op.FULLY_CONNECTED,
             Op.DEPTHWISE_CONV2D,
             Op.TRANSPOSE_CONV2D,
+            Op.CONV3D,
         )
         if (
             errorName
@@ -952,7 +953,7 @@
         dilations = args_dict["dilation"]
 
         assert len(padding) == 6
-        result_tens = OutputShaper.conv3dOp(
+        result_tensor = OutputShaper.conv3dOp(
             self.ser,
             self.rng,
             ifm,
@@ -971,12 +972,12 @@
         ):
             qinfo = [
                 TosaQuantGen.getZeroPoint(self, ifm.dtype),
-                TosaQuantGen.getZeroPoint(self, result_tens.dtype),
+                TosaQuantGen.getZeroPoint(self, result_tensor.dtype),
             ]
 
         # Invalidate Input/Output list for error_if checks.
         input_list = [ifm.name, filter.name, bias.name]
-        output_list = [result_tens.name]
+        output_list = [result_tensor.name]
         num_operands = sum(op["operands"])
         input_list, output_list = TosaErrorIfArgGen.eiInvalidateInputOutputList(
             self, error_name, input_list, output_list
@@ -989,7 +990,7 @@
             op=op,
             input_dtype=ifm.dtype,
             weight_dtype=filter.dtype,
-            output_dtype=result_tens.dtype,
+            output_dtype=result_tensor.dtype,
             qinfo=qinfo,
             input_list=input_list,
             num_operands=num_operands,
@@ -999,7 +1000,7 @@
             dilation=dilations,
             input_shape=ifm.shape,
             weight_shape=filter.shape,
-            output_shape=result_tens.shape,
+            output_shape=result_tensor.shape,
         ):
             return None
 
@@ -1010,7 +1011,12 @@
         attr.ConvAttribute(padding, strides, dilations, qinfo[0], qinfo[1], local_bound)
 
         self.ser.addOperator(op["op"], input_list, output_list, attr)
-        return result_tens
+
+        compliance = self.tensorComplianceMetaData(
+            op, ifm.dtype, args_dict, result_tensor, error_name
+        )
+
+        return TosaTestGen.BuildInfo(result_tensor, compliance)
 
     def build_transpose_conv2d(
         self,
@@ -3254,6 +3260,9 @@
                 TosaErrorValidator.evConvOutputShapeMismatch,
                 TosaErrorValidator.evConvOutputShapeNonInteger,
             ),
+            "data_gen": {
+                "fp": (gtu.DataGenType.DOT_PRODUCT,),
+            },
             "template": True,
         },
         # Templated operator.  Filled in by createDynamicOpLists