[Tensor] Add `add_i(Tensor T)` operator
authorJihoon Lee <jhoon.it.lee@samsung.com>
Mon, 8 Jun 2020 04:56:14 +0000 (13:56 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Tue, 9 Jun 2020 07:11:02 +0000 (16:11 +0900)
**Changes proposed in this PR:**
- add_i(Tensor T) operator for memcopyless operation

**Self evaluation:**
1. Build test: [X]Passed [ ]Failed [ ]Skipped
2. Run test: [X]Passed [ ]Failed [ ]Skipped

Signed-off-by: Jihoon Lee <jhoon.it.lee@samsung.com>
nntrainer/include/tensor.h
nntrainer/src/tensor.cpp
test/unittest/unittest_nntrainer_tensor.cpp

index 37985651046f76c6bb747dc0e3b668679c6a0450..aec1fe7479fa81800290f9498b9499162167613b 100644 (file)
@@ -124,6 +124,14 @@ public:
    */
   Tensor divide(float const &value);
 
+  /**
+   * @brief Add Tensor Element by Element without mem copy
+   * @param[in] m Tensor to be added
+   * #retval #ML_ERROR_NONE  Successful
+   * #retval #ML_ERROR_INVALID_PARAMETER Invalid Parameter
+   */
+  int add_i(Tensor const &m);
+
   /**
    * @brief     Add Tensor Element by Element
    * @param[in] m Tensor to be added
index b9c2881f372c8d4f8b7b3628a25bb25ba2a08b29..aec9a8c82eafd61d745c9b0644072753d4db2bd6 100644 (file)
@@ -181,23 +181,26 @@ Tensor Tensor::add(float const &value) {
   return result;
 }
 
-Tensor Tensor::add(Tensor const &m) const {
+/**
+ * @brief Add Tensor Element by Element without mem copy
+ * @param[in] m Tensor to be added
+ * #retval #ML_ERROR_NONE  Successful
+ * #retval #ML_ERROR_INVALID_PARAMETER Invalid Parameter
+ */
+int Tensor::add_i(Tensor const &m) {
   if ((dim.height() != m.dim.height()) || (dim.width() != m.dim.width())) {
-    throw std::runtime_error("Error: Dimension must be equal each other");
+    return ML_ERROR_INVALID_PARAMETER;
   }
 
-  Tensor result(dim);
 #ifdef USE_BLAS
-  cblas_scopy(dim.getDataLen(), this->data.data(), 1, result.data.data(), 1);
-  unsigned int size = dim.channel() * dim.width() * dim.height();
-
+  unsigned int size = dim.width() * dim.height() * dim.channel();
   if (m.dim.batch() == 1) {
     for (unsigned int k = 0; k < dim.batch(); ++k) {
-      cblas_saxpy(size, 1.0, m.data.data(), 1, &(result.data.data()[k * size]),
+      cblas_saxpy(size, 1.0, m.data.data(), 1, &(this->data.data()[k * size]),
                   1);
     }
   } else {
-    cblas_saxpy(dim.getDataLen(), 1.0, m.data.data(), 1, result.data.data(), 1);
+    cblas_saxpy(dim.getDataLen(), 1.0, m.data.data(), 1, this->data.data(), 1);
   }
 #else
   unsigned int i, j, k;
@@ -205,16 +208,28 @@ Tensor Tensor::add(Tensor const &m) const {
     for (k = 0; k < dim.batch(); ++k) {
       for (i = 0; i < m.dim.getFeatureLen(); ++i) {
         j = k * m.dim.getFeatureLen();
-        result.data[j + i] = data[j + i] + m.data[i];
+        this->data[j + i] += m.data[i];
       }
     }
   } else {
     for (k = 0; k < dim.getDataLen(); ++k) {
-      result.data[k] = data[k] + m.data[k];
+      this->data[k] = this->data[k] + m.data[k];
     }
   }
 #endif
 
+  return ML_ERROR_NONE;
+}
+
+Tensor Tensor::add(Tensor const &m) const {
+  if ((dim.height() != m.dim.height()) || (dim.width() != m.dim.width())) {
+    throw std::runtime_error("Error: Dimension must be equal each other");
+  }
+
+  Tensor result(dim);
+  result.copy(*this);
+  result.add_i(m);
+
   return result;
 }
 
@@ -698,6 +713,9 @@ Tensor &Tensor::copy(const Tensor &from) {
     dim.height(from.dim.height());
     dim.width(from.dim.width());
     dim.batch(from.dim.batch());
+    if (this->data.empty()) {
+      this->data.resize(from.data.size());
+    }
 #ifdef USE_BLAS
     cblas_scopy(dim.getDataLen(), from.data.data(), 1, this->data.data(), 1);
 #else
index 20a6979c5fb9caaaf07432b6a5360da4f8b33b9f..b45dd16c6d42cdd2e1a59caec2b89a497975f884 100644 (file)
@@ -191,6 +191,51 @@ TEST(nntrainer_Tensor, divide_03_n) {
                    "Error: Dimension must be equal each other");
 }
 
+TEST(nntrainer_Tensor, add_i_02_p) {
+  int status = ML_ERROR_NONE;
+  int batch = 3;
+  int height = 3;
+  int width = 10;
+  int channel = 1;
+
+  nntrainer::Tensor target(batch, channel, height, width);
+  GEN_TEST_INPUT(target, i * (batch * height) + j * (width) + k + 1);
+
+  nntrainer::Tensor original(batch, height, width);
+  original.copy(target);
+
+  status = target.add_i(target);
+  EXPECT_EQ(status, ML_ERROR_NONE);
+
+  float *previous = original.getData();
+  ASSERT_NE(nullptr, previous);
+  float *data = target.getData();
+  ASSERT_NE(nullptr, data);
+
+  for (int i = 0; i < batch * height * width; ++i) {
+    EXPECT_FLOAT_EQ(data[i], previous[i] + previous[i]);
+  }
+}
+
+/**
+ * @brief operand dimension is not right
+ */
+TEST(nntrainer_Tensor, add_i_01_n) {
+  int status = ML_ERROR_NONE;
+  int batch = 3;
+  int height = 3;
+  int width = 10;
+  int channel = 1;
+
+  nntrainer::Tensor target(batch, channel, height, width);
+  GEN_TEST_INPUT(target, i * (batch * height) + j * (width) + k + 1);
+
+  nntrainer::Tensor target2(batch, height - 2, width - 3);
+
+  status = target.add_i(target2);
+  EXPECT_EQ(status, ML_ERROR_INVALID_PARAMETER);
+}
+
 TEST(nntrainer_Tensor, add_01_p) {
   int status = ML_ERROR_NONE;
   int batch = 3;