[TensorDim/Tensor] Update transpose relates
authorJihoon Lee <jhoon.it.lee@samsung.com>
Thu, 29 Apr 2021 09:54:11 +0000 (18:54 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Mon, 3 May 2021 04:26:13 +0000 (13:26 +0900)
**Changes proposed in this PR:**
- Add tensorDim::transpose(out)
- Add tensor::transpose
- Add test about tensor::transpose

**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/tensor/tensor.cpp
nntrainer/tensor/tensor.h
nntrainer/tensor/tensor_dim.cpp
nntrainer/tensor/tensor_dim.h
test/unittest/unittest_nntrainer_tensor.cpp

index ce61a3c..3595d14 100644 (file)
@@ -390,7 +390,7 @@ Tensor &Tensor::add(float const &value, Tensor &out) const {
 }
 
 int Tensor::add_i(Tensor const &m, float const alpha) {
-  /// @TODO: add axis rather doing add over the last two dimensions always
+  /// @todo: add axis rather doing add over the last two dimensions always
   /// operator i has optimized version
   auto f = [&](const BroadcastInfo &e, const float *buf, const float *m_buf,
                float *out_buf) {
@@ -800,29 +800,25 @@ Tensor &Tensor::dot(Tensor const &m, Tensor &result, bool trans, bool trans_m,
   return result;
 }
 
-Tensor Tensor::transpose(std::string direction) const {
+Tensor &Tensor::transpose(const std::string &direction, Tensor &out) const {
+  if (out.getData() == getData()) {
+    Tensor tmp = clone();
+    return tmp.transpose(direction, out);
+  }
+
   unsigned int SL, SI, SJ, SK;
-  int dir[MAXDIM - 1];
-  unsigned int fromDim[4];
   const float *inptr;
   float *outptr;
 
-  fromDim[0] = dim.batch();
-  fromDim[1] = dim.channel();
-  fromDim[2] = dim.height();
-  fromDim[3] = dim.width();
-
-  getValues(3, direction, dir);
-  Tensor result(dim.batch(), fromDim[dir[0] + 1], fromDim[dir[1] + 1],
-                fromDim[dir[2] + 1]);
+  out.reshape(dim.transpose(direction));
 
-  int indexI = dir[0];
-  int indexJ = dir[1];
+  int indexI = direction[0] - '0';
+  int indexJ = direction[2] - '0';
 
-  SL = fromDim[0], SI = fromDim[1], SJ = fromDim[2], SK = fromDim[3];
+  SL = dim.batch(), SI = dim.channel(), SJ = dim.height(), SK = dim.width();
 
   inptr = getData();
-  outptr = result.getData();
+  outptr = out.getData();
 
   switch (indexI) {
   case 0:
@@ -848,6 +844,12 @@ Tensor Tensor::transpose(std::string direction) const {
     break;
   }
 
+  return out;
+}
+
+Tensor Tensor::transpose(const std::string &direction) const {
+  Tensor result(dim);
+  transpose(direction, result);
   return result;
 }
 
index aa87918..d7b5f49 100644 (file)
@@ -56,7 +56,7 @@ public:
    */
   Tensor() :
     dim(TensorDim()),
-    strides{dim.computeStrides()},
+    strides(dim.computeStrides()),
     is_contiguous(true),
     data(nullptr),
     src_tensor() {}
@@ -504,7 +504,15 @@ public:
    * @param[in] direction to transpose ex) 0:2:1
    * @retval    Calculated Tensor
    */
-  Tensor transpose(std::string direction) const;
+  Tensor transpose(const std::string &direction) const;
+
+  /**
+   * @brief Transpose Tensor
+   * @param direction to transpose ex) 0:2:1
+   * @param[out] Tensor to save to, dimension is always reshaped.
+   * @retval Tensor& reference to the out
+   */
+  Tensor &transpose(const std::string &direction, Tensor &out) const;
 
   /**
    * @brief     sum all the Tensor elements according to the batch
@@ -980,8 +988,20 @@ private:
    */
   std::shared_ptr<SrcSharedTensor> src_tensor;
 
+  /**
+   * @brief Set the Dist object
+   *
+   * @tparam T distrubution engine
+   * @param dist distribution engine
+   */
   template <typename T> void setDist(T dist);
 
+  /**
+   * @brief copy a buffer to @a this, the caller has to ensure that @a this is
+   * initialized otherwise undefined behavior
+   *
+   * @param buf buffer to copy from
+   */
   void copy(const float *buf) noexcept;
 
   /**
index 4df49e2..d107815 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
+#include <parse_util.h>
 #include <tensor_dim.h>
 
 namespace nntrainer {
@@ -95,6 +96,29 @@ int TensorDim::setTensorDim(const std::string &input_shape) {
   return status;
 }
 
+TensorDim TensorDim::transpose(const std::string &direction) const {
+  int dirs[MAXDIM - 1];
+
+  getValues(3, direction, dirs);
+
+  const std::array<unsigned int, MAXDIM> axes{{0, (unsigned int)dirs[0] + 1,
+                                               (unsigned int)dirs[1] + 1,
+                                               (unsigned int)dirs[2] + 1}};
+
+  return transpose(axes);
+}
+
+TensorDim
+TensorDim::transpose(const std::array<unsigned int, MAXDIM> &axes) const {
+  TensorDim tmp(*this);
+
+  for (unsigned int i = 0; i < MAXDIM; ++i) {
+    tmp.setTensorDim(i, getTensorDim(axes[i]));
+  }
+
+  return tmp;
+}
+
 bool TensorDim::operator==(const TensorDim &rhs) const {
   for (size_t i = 0; i < MAXDIM; ++i) {
     if (this->dim[i] != rhs.dim[i]) {
index d67eb21..5398e69 100644 (file)
@@ -210,6 +210,25 @@ public:
   unsigned int getNumDim() const { return MAXDIM; }
 
   /**
+   * @brief calculate tranposed dimension
+   * @note In this function, batch direction is not considered, so channel is 0
+   * @todo make batch 0
+   *
+   * @param direction  direction to transpose
+   * @return TensorDim calculated dimension
+   */
+  TensorDim transpose(const std::string &direction) const;
+
+  /**
+   * @brief calculate trasposed dimension
+   * @note In this function, batch direction is considered 0
+   *
+   * @param axes axes to be transposed
+   * @return TensorDim calculated dimension
+   */
+  TensorDim transpose(const std::array<unsigned int, MAXDIM> &axes) const;
+
+  /**
    * @brief Get the Tensor dimension for an axis
    *
    * @param idx axis to get
@@ -221,7 +240,7 @@ public:
    * @brief Set the Tensor Dim object
    *
    * @param idx axis to set
-   * @param value value to sset
+   * @param value value to set
    */
   void setTensorDim(unsigned int idx, unsigned int value);
 
index 931e7fe..3fdd3bb 100644 (file)
@@ -2254,32 +2254,223 @@ TEST(nntrainer_Tensor, dot_shortcuts_p) {
   }
 }
 
-TEST(nntrainer_Tensor, transpose_01_p) {
-  int status = ML_ERROR_NONE;
-  int batch = 3;
-  int channel = 1;
-  int height = 3;
-  int width = 3;
-  float ans[3][1][3][3] = {{{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}}},
-                           {{{10, 13, 16}, {11, 14, 17}, {12, 15, 18}}},
-                           {{{19, 22, 25}, {20, 23, 26}, {21, 24, 27}}}};
-  nntrainer::Tensor input(batch, channel, height, width);
-  GEN_TEST_INPUT(input, i * (channel * width * height) + j * (height * width) +
-                          k * (width) + l + 1);
-  nntrainer::Tensor result = input.transpose("0:2:1");
+TEST(nntrainer_Tensor, transpose_p) {
+  nntrainer::TensorDim ref_dim(3, 2, 4, 5);
 
-  for (unsigned int i = 0; i < result.batch(); ++i) {
-    for (unsigned int j = 0; j < result.height(); ++j) {
-      for (unsigned int k = 0; k < result.width(); ++k) {
-        if (ans[i][0][j][k] != result.getValue(i, 0, j, k)) {
-          status = ML_ERROR_RESULT_OUT_OF_RANGE;
-          goto end_transpose_01_p;
-        }
-      }
-    }
+  /// plain transpose
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,
+      14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,
+      28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
+      42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,
+      56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+      70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+      84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
+      98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+      112, 113, 114, 115, 116, 117, 118, 119};
+    nntrainer::Tensor answer({3, 2, 4, 5}, answer_data);
+    nntrainer::Tensor m = t.transpose("0:1:2");
+    EXPECT_EQ(answer, m);
   }
-end_transpose_01_p:
-  EXPECT_EQ(status, ML_ERROR_NONE);
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,   5,   10,  15,  1,   6,   11,  16,  2,   7,   12,  17,  3,   8,
+      13,  18,  4,   9,   14,  19,  20,  25,  30,  35,  21,  26,  31,  36,
+      22,  27,  32,  37,  23,  28,  33,  38,  24,  29,  34,  39,  40,  45,
+      50,  55,  41,  46,  51,  56,  42,  47,  52,  57,  43,  48,  53,  58,
+      44,  49,  54,  59,  60,  65,  70,  75,  61,  66,  71,  76,  62,  67,
+      72,  77,  63,  68,  73,  78,  64,  69,  74,  79,  80,  85,  90,  95,
+      81,  86,  91,  96,  82,  87,  92,  97,  83,  88,  93,  98,  84,  89,
+      94,  99,  100, 105, 110, 115, 101, 106, 111, 116, 102, 107, 112, 117,
+      103, 108, 113, 118, 104, 109, 114, 119};
+    nntrainer::Tensor answer({3, 2, 5, 4}, answer_data);
+    nntrainer::Tensor m = t.transpose("0:2:1");
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,   1,   2,   3,   4,   20,  21,  22,  23,  24,  5,   6,   7,   8,
+      9,   25,  26,  27,  28,  29,  10,  11,  12,  13,  14,  30,  31,  32,
+      33,  34,  15,  16,  17,  18,  19,  35,  36,  37,  38,  39,  40,  41,
+      42,  43,  44,  60,  61,  62,  63,  64,  45,  46,  47,  48,  49,  65,
+      66,  67,  68,  69,  50,  51,  52,  53,  54,  70,  71,  72,  73,  74,
+      55,  56,  57,  58,  59,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+      84,  100, 101, 102, 103, 104, 85,  86,  87,  88,  89,  105, 106, 107,
+      108, 109, 90,  91,  92,  93,  94,  110, 111, 112, 113, 114, 95,  96,
+      97,  98,  99,  115, 116, 117, 118, 119};
+    nntrainer::Tensor answer({3, 4, 2, 5}, answer_data);
+    nntrainer::Tensor m = t.transpose("1:0:2");
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,  20,  1,  21,  2,  22,  3,  23,  4,  24,  5,  25,  6,  26,  7,  27,
+      8,  28,  9,  29,  10, 30,  11, 31,  12, 32,  13, 33,  14, 34,  15, 35,
+      16, 36,  17, 37,  18, 38,  19, 39,  40, 60,  41, 61,  42, 62,  43, 63,
+      44, 64,  45, 65,  46, 66,  47, 67,  48, 68,  49, 69,  50, 70,  51, 71,
+      52, 72,  53, 73,  54, 74,  55, 75,  56, 76,  57, 77,  58, 78,  59, 79,
+      80, 100, 81, 101, 82, 102, 83, 103, 84, 104, 85, 105, 86, 106, 87, 107,
+      88, 108, 89, 109, 90, 110, 91, 111, 92, 112, 93, 113, 94, 114, 95, 115,
+      96, 116, 97, 117, 98, 118, 99, 119};
+    nntrainer::Tensor answer({3, 4, 5, 2}, answer_data);
+    nntrainer::Tensor m = t.transpose("1:2:0");
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,  5,  10,  15,  20,  25,  30,  35, 1,  6,   11,  16,  21,  26,  31,
+      36, 2,  7,   12,  17,  22,  27,  32, 37, 3,   8,   13,  18,  23,  28,
+      33, 38, 4,   9,   14,  19,  24,  29, 34, 39,  40,  45,  50,  55,  60,
+      65, 70, 75,  41,  46,  51,  56,  61, 66, 71,  76,  42,  47,  52,  57,
+      62, 67, 72,  77,  43,  48,  53,  58, 63, 68,  73,  78,  44,  49,  54,
+      59, 64, 69,  74,  79,  80,  85,  90, 95, 100, 105, 110, 115, 81,  86,
+      91, 96, 101, 106, 111, 116, 82,  87, 92, 97,  102, 107, 112, 117, 83,
+      88, 93, 98,  103, 108, 113, 118, 84, 89, 94,  99,  104, 109, 114, 119};
+    nntrainer::Tensor answer({3, 5, 2, 4}, answer_data);
+    nntrainer::Tensor m = t.transpose("2:0:1");
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,  20,  5,  25,  10, 30,  15, 35,  1,  21,  6,  26,  11, 31,  16, 36,
+      2,  22,  7,  27,  12, 32,  17, 37,  3,  23,  8,  28,  13, 33,  18, 38,
+      4,  24,  9,  29,  14, 34,  19, 39,  40, 60,  45, 65,  50, 70,  55, 75,
+      41, 61,  46, 66,  51, 71,  56, 76,  42, 62,  47, 67,  52, 72,  57, 77,
+      43, 63,  48, 68,  53, 73,  58, 78,  44, 64,  49, 69,  54, 74,  59, 79,
+      80, 100, 85, 105, 90, 110, 95, 115, 81, 101, 86, 106, 91, 111, 96, 116,
+      82, 102, 87, 107, 92, 112, 97, 117, 83, 103, 88, 108, 93, 113, 98, 118,
+      84, 104, 89, 109, 94, 114, 99, 119};
+    nntrainer::Tensor answer({3, 5, 4, 2}, answer_data);
+    nntrainer::Tensor m = t.transpose("2:1:0");
+    EXPECT_EQ(answer, m);
+  }
+
+  /// outplace transpose
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    nntrainer::Tensor m = ranged(3, 2, 4, 5);
+    float answer_data[] = {
+      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,
+      14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,
+      28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
+      42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,
+      56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+      70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+      84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
+      98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+      112, 113, 114, 115, 116, 117, 118, 119};
+    nntrainer::Tensor answer({3, 2, 4, 5}, answer_data);
+    t.transpose("0:1:2", m);
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    nntrainer::Tensor m = ranged(3, 2, 5, 4);
+    float answer_data[] = {
+      0,   5,   10,  15,  1,   6,   11,  16,  2,   7,   12,  17,  3,   8,
+      13,  18,  4,   9,   14,  19,  20,  25,  30,  35,  21,  26,  31,  36,
+      22,  27,  32,  37,  23,  28,  33,  38,  24,  29,  34,  39,  40,  45,
+      50,  55,  41,  46,  51,  56,  42,  47,  52,  57,  43,  48,  53,  58,
+      44,  49,  54,  59,  60,  65,  70,  75,  61,  66,  71,  76,  62,  67,
+      72,  77,  63,  68,  73,  78,  64,  69,  74,  79,  80,  85,  90,  95,
+      81,  86,  91,  96,  82,  87,  92,  97,  83,  88,  93,  98,  84,  89,
+      94,  99,  100, 105, 110, 115, 101, 106, 111, 116, 102, 107, 112, 117,
+      103, 108, 113, 118, 104, 109, 114, 119};
+    nntrainer::Tensor answer({3, 2, 5, 4}, answer_data);
+    t.transpose("0:2:1", m);
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    nntrainer::Tensor m = ranged(3, 4, 2, 5);
+    float answer_data[] = {
+      0,   1,   2,   3,   4,   20,  21,  22,  23,  24,  5,   6,   7,   8,
+      9,   25,  26,  27,  28,  29,  10,  11,  12,  13,  14,  30,  31,  32,
+      33,  34,  15,  16,  17,  18,  19,  35,  36,  37,  38,  39,  40,  41,
+      42,  43,  44,  60,  61,  62,  63,  64,  45,  46,  47,  48,  49,  65,
+      66,  67,  68,  69,  50,  51,  52,  53,  54,  70,  71,  72,  73,  74,
+      55,  56,  57,  58,  59,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+      84,  100, 101, 102, 103, 104, 85,  86,  87,  88,  89,  105, 106, 107,
+      108, 109, 90,  91,  92,  93,  94,  110, 111, 112, 113, 114, 95,  96,
+      97,  98,  99,  115, 116, 117, 118, 119};
+    nntrainer::Tensor answer({3, 4, 2, 5}, answer_data);
+    t.transpose("1:0:2", m);
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    nntrainer::Tensor m = ranged(3, 4, 5, 2);
+    float answer_data[] = {
+      0,  20,  1,  21,  2,  22,  3,  23,  4,  24,  5,  25,  6,  26,  7,  27,
+      8,  28,  9,  29,  10, 30,  11, 31,  12, 32,  13, 33,  14, 34,  15, 35,
+      16, 36,  17, 37,  18, 38,  19, 39,  40, 60,  41, 61,  42, 62,  43, 63,
+      44, 64,  45, 65,  46, 66,  47, 67,  48, 68,  49, 69,  50, 70,  51, 71,
+      52, 72,  53, 73,  54, 74,  55, 75,  56, 76,  57, 77,  58, 78,  59, 79,
+      80, 100, 81, 101, 82, 102, 83, 103, 84, 104, 85, 105, 86, 106, 87, 107,
+      88, 108, 89, 109, 90, 110, 91, 111, 92, 112, 93, 113, 94, 114, 95, 115,
+      96, 116, 97, 117, 98, 118, 99, 119};
+    nntrainer::Tensor answer({3, 4, 5, 2}, answer_data);
+    t.transpose("1:2:0", m);
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    nntrainer::Tensor m = ranged(3, 5, 2, 4);
+    float answer_data[] = {
+      0,  5,  10,  15,  20,  25,  30,  35, 1,  6,   11,  16,  21,  26,  31,
+      36, 2,  7,   12,  17,  22,  27,  32, 37, 3,   8,   13,  18,  23,  28,
+      33, 38, 4,   9,   14,  19,  24,  29, 34, 39,  40,  45,  50,  55,  60,
+      65, 70, 75,  41,  46,  51,  56,  61, 66, 71,  76,  42,  47,  52,  57,
+      62, 67, 72,  77,  43,  48,  53,  58, 63, 68,  73,  78,  44,  49,  54,
+      59, 64, 69,  74,  79,  80,  85,  90, 95, 100, 105, 110, 115, 81,  86,
+      91, 96, 101, 106, 111, 116, 82,  87, 92, 97,  102, 107, 112, 117, 83,
+      88, 93, 98,  103, 108, 113, 118, 84, 89, 94,  99,  104, 109, 114, 119};
+    nntrainer::Tensor answer({3, 5, 2, 4}, answer_data);
+    t.transpose("2:0:1", m);
+    EXPECT_EQ(answer, m);
+  }
+  {
+    nntrainer::TensorDim ref_dim(3, 2, 4, 5);
+    nntrainer::Tensor t = ranged(3, 2, 4, 5);
+    nntrainer::Tensor m = ranged(3, 5, 4, 2);
+    float answer_data[] = {
+      0,  20,  5,  25,  10, 30,  15, 35,  1,  21,  6,  26,  11, 31,  16, 36,
+      2,  22,  7,  27,  12, 32,  17, 37,  3,  23,  8,  28,  13, 33,  18, 38,
+      4,  24,  9,  29,  14, 34,  19, 39,  40, 60,  45, 65,  50, 70,  55, 75,
+      41, 61,  46, 66,  51, 71,  56, 76,  42, 62,  47, 67,  52, 72,  57, 77,
+      43, 63,  48, 68,  53, 73,  58, 78,  44, 64,  49, 69,  54, 74,  59, 79,
+      80, 100, 85, 105, 90, 110, 95, 115, 81, 101, 86, 106, 91, 111, 96, 116,
+      82, 102, 87, 107, 92, 112, 97, 117, 83, 103, 88, 108, 93, 113, 98, 118,
+      84, 104, 89, 109, 94, 114, 99, 119};
+    nntrainer::Tensor answer({3, 5, 4, 2}, answer_data);
+    t.transpose("2:1:0", m);
+    EXPECT_EQ(answer, m);
+  }
+}
+
+TEST(nntrainer_Tensor, tranpose_dimension_not_match_n) {
+  nntrainer::Tensor a(3, 2, 4, 5);
+  nntrainer::Tensor b(3, 1, 2, 3);
+
+  EXPECT_THROW(a.transpose("0:1:2", b), std::invalid_argument);
 }
 
 TEST(nntrainer_Tensor, set_01_p) {