[IE Common][Tests] saturated_cast: refactoring & tests (#1304)
authorAndrew Bakalin <andrew.bakalin@intel.com>
Wed, 15 Jul 2020 10:48:57 +0000 (13:48 +0300)
committerGitHub <noreply@github.com>
Wed, 15 Jul 2020 10:48:57 +0000 (13:48 +0300)
* [IE Common] Refactor saturated_cast

* [IE Common][Tests] Add tests for saturated casts

* [IE Common] Review fixes

* [IE Common] Make enable_if check a template parameter

inference-engine/src/legacy_api/src/net_pass.cpp
inference-engine/src/plugin_api/precision_utils.h
inference-engine/tests/unit/inference_engine/CMakeLists.txt
inference-engine/tests/unit/inference_engine/saturated_cast_test.cpp [new file with mode: 0644]

index aa02087..36dc1a8 100644 (file)
@@ -1330,28 +1330,13 @@ bool UnrollRNN_if(TensorIterator::Body& net, const std::function<bool(const RNNC
 
 namespace {
 
-template <typename TO, typename FROM>
-bool isConversionNarrowing(FROM from) {
-    return from == (static_cast<FROM>(static_cast<TO>(from)));
-}
-
-template <typename TO, typename FROM>
-TO saturatedCast(FROM from) {
-    FROM max = isConversionNarrowing<FROM>(std::numeric_limits<TO>::max()) ? std::numeric_limits<FROM>::max() :
-                                                                             static_cast<FROM>(std::numeric_limits<TO>::max());
-    FROM min = isConversionNarrowing<FROM>(std::numeric_limits<TO>::min()) ? std::numeric_limits<FROM>::min() :
-                                                                             static_cast<FROM>(std::numeric_limits<TO>::min());
-
-    return static_cast<TO>(std::min(std::max(from, min), max));
-}
-
 template <Precision::ePrecision PREC_FROM, Precision::ePrecision PREC_TO>
 void convertArrayPrecision(typename PrecisionTrait<PREC_TO>::value_type* dst,
                            const typename PrecisionTrait<PREC_FROM>::value_type* src, size_t nelem) {
     using dst_type = typename PrecisionTrait<PREC_TO>::value_type;
 
     for (size_t i = 0; i < nelem; i++) {
-        dst[i] = saturatedCast<dst_type>(src[i]);
+        dst[i] = PrecisionUtils::saturate_cast<dst_type>(src[i]);
     }
 }
 
index 386f2a1..9e99add 100644 (file)
@@ -12,6 +12,9 @@
 #include <ie_api.h>
 
 #include <cstddef>
+#include <type_traits>
+#include <limits>
+#include <algorithm>
 
 /**
  * @brief Inference Engine Plugin API namespace
@@ -123,6 +126,67 @@ f16tof32Arrays(float* dst, const ie_fp16* src, size_t nelem, float scale = 1.f,
 INFERENCE_ENGINE_API_CPP(void)
 f32tof16Arrays(ie_fp16* dst, const float* src, size_t nelem, float scale = 1.f, float bias = 0.f);
 
+/**
+ * @brief      Converts one integral type to another saturating the result if the source value doesn't fit
+ *             into destination type range
+ * @ingroup    ie_dev_api_precision
+ *
+ * @param      value   Value to be converted
+ */
+template <class OutT, class InT, typename std::enable_if<
+        std::is_integral<OutT>::value && std::is_integral<InT>::value &&
+        std::is_signed<InT>::value &&
+        !std::is_same<OutT, InT>::value
+        >::type* = nullptr>
+inline OutT saturate_cast(const InT& value) {
+    if (std::numeric_limits<OutT>::max() > std::numeric_limits<InT>::max() &&
+        std::numeric_limits<OutT>::min() < std::numeric_limits<InT>::min()) {
+        return static_cast<OutT>(value);
+    }
+
+    const InT max = std::numeric_limits<OutT>::max() < std::numeric_limits<InT>::max() ? std::numeric_limits<OutT>::max() :
+                    std::numeric_limits<InT>::max();
+    const InT min = std::numeric_limits<OutT>::min() > std::numeric_limits<InT>::min() ? std::numeric_limits<OutT>::min() :
+                    std::numeric_limits<InT>::min();
+
+    return std::min(std::max(value, min), max);
+}
+
+/**
+ * @brief      Converts one integral type to another saturating the result if the source value doesn't fit
+ *             into destination type range
+ * @ingroup    ie_dev_api_precision
+ *
+ * @param      value   Value to be converted
+ */
+template <class OutT, class InT, typename std::enable_if<
+        std::is_integral<OutT>::value && std::is_integral<InT>::value &&
+        std::is_unsigned<InT>::value &&
+        !std::is_same<OutT, InT>::value
+        >::type* = nullptr>
+inline OutT saturate_cast(const InT& value) {
+    if (std::numeric_limits<OutT>::max() > std::numeric_limits<InT>::max()) {
+        return static_cast<OutT>(value);
+    }
+
+    const InT max = std::numeric_limits<OutT>::max() < std::numeric_limits<InT>::max() ? std::numeric_limits<OutT>::max() :
+                    std::numeric_limits<InT>::max();
+
+    return std::min(value, max);
+}
+
+/**
+ * @brief      Converts one integral type to another saturating the result if the source value doesn't fit
+ *             into destination type range
+ * @ingroup    ie_dev_api_precision
+ *
+ * @param      value   Value to be converted
+ */
+template <class InT>
+inline InT saturate_cast(const InT& value) {
+    return value;
+}
+
 }  // namespace PrecisionUtils
 
 }  // namespace InferenceEngine
index 3c6a174..051dc00 100644 (file)
@@ -4,11 +4,20 @@
 
 set(TARGET_NAME ieUnitTests)
 
+# Find OpenCV components if exist
+find_package(OpenCV COMPONENTS imgcodecs videoio imgproc QUIET)
+if(NOT OpenCV_FOUND)
+    message(ERROR "OPENCV is disabled or not found, " ${TARGET_NAME} " needs OpenCV for its build")
+else()
+    add_definitions(-DUSE_OPENCV)
+endif()
+
 addIeTargetTest(
         NAME ${TARGET_NAME}
         ROOT ${CMAKE_CURRENT_SOURCE_DIR}
         LINK_LIBRARIES
             unitTestUtils
+            ${OpenCV_LIBRARIES}
         ADD_CPPLINT
         DEPENDENCIES
             mock_engine
diff --git a/inference-engine/tests/unit/inference_engine/saturated_cast_test.cpp b/inference-engine/tests/unit/inference_engine/saturated_cast_test.cpp
new file mode 100644 (file)
index 0000000..c89169e
--- /dev/null
@@ -0,0 +1,114 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <gtest/gtest.h>
+#include "common_test_utils/test_common.hpp"
+
+#include "precision_utils.h"
+#include "ie_precision.hpp"
+
+#include <opencv2/core.hpp>
+
+using namespace InferenceEngine;
+
+class SaturateCastTestsI64ToI32 : public CommonTestUtils::TestsCommon {
+public:
+    using fromType = typename PrecisionTrait<Precision::I64>::value_type;
+    using toType = typename PrecisionTrait<Precision::I32>::value_type;
+};
+
+TEST_F(SaturateCastTestsI64ToI32, I64ToI32NonNarrowingPositive) {
+    const auto value = fromType{42};
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsI64ToI32, I64ToI32NonNarrowingNegative) {
+    const auto value = fromType{-42};
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingMaxToMax) {
+    const auto value = std::numeric_limits<fromType>::max();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingNonMaxToMax) {
+    const auto value = std::numeric_limits<fromType>::max() - 1;
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingMinToMin) {
+    const auto value = std::numeric_limits<fromType>::min();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingNonMinToMin) {
+    const auto value = std::numeric_limits<fromType>::min() + 1;
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+class SaturateCastTestsU64ToI32 : public CommonTestUtils::TestsCommon {
+public:
+    using fromType = typename PrecisionTrait<Precision::U64>::value_type;
+    using toType = typename PrecisionTrait<Precision::I32>::value_type;
+};
+
+TEST_F(SaturateCastTestsU64ToI32, U64ToI32NonNarrowing) {
+    const auto value = fromType{42};
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsU64ToI32, U64ToI32NarrowingMaxToMax) {
+    const auto value = std::numeric_limits<fromType>::max();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+TEST_F(SaturateCastTestsU64ToI32, U64ToI32NarrowingNonMaxToMax) {
+    const auto value = std::numeric_limits<fromType>::max() - 1;
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+class SaturateCastTestsBoolToU8 : public CommonTestUtils::TestsCommon {
+public:
+    using fromType = typename PrecisionTrait<Precision::BOOL>::value_type;
+    using toType = typename PrecisionTrait<Precision::U8>::value_type;
+};
+
+TEST_F(SaturateCastTestsBoolToU8, BOOLtoU8MaxToNonMax) {
+    const auto value = std::numeric_limits<fromType>::max();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+class SaturateCastTestsBoolToI32 : public CommonTestUtils::TestsCommon {
+public:
+    using fromType = typename PrecisionTrait<Precision::BOOL>::value_type;
+    using toType = typename PrecisionTrait<Precision::I32>::value_type;
+};
+
+TEST_F(SaturateCastTestsBoolToI32, BOOLtoI32MaxToNonMax) {
+    const auto value = std::numeric_limits<fromType>::max();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+class SaturateCastTestsU8ToI32 : public CommonTestUtils::TestsCommon {
+public:
+    using fromType = typename PrecisionTrait<Precision::U8>::value_type;
+    using toType = typename PrecisionTrait<Precision::I32>::value_type;
+};
+
+TEST_F(SaturateCastTestsU8ToI32, U8toI32FMaxToNonMax) {
+    const auto value = std::numeric_limits<fromType>::max();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}
+
+class SaturateCastTestsU16ToI32 : public CommonTestUtils::TestsCommon {
+public:
+    using fromType = typename PrecisionTrait<Precision::U8>::value_type;
+    using toType = typename PrecisionTrait<Precision::I32>::value_type;
+};
+
+TEST_F(SaturateCastTestsU16ToI32, U16toI32FMaxToNonMax) {
+    const auto value = std::numeric_limits<fromType>::max();
+    EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
+}