2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "NodeExecution.h"
19 #include "locomotiv/NodeData.h"
20 #include "NodeDataImpl.h"
21 #include "NodeDomain.h"
23 #include <loco/IR/PermutingCodec.h>
25 #include <nncc/core/ADT/tensor/Shape.h>
26 #include <nncc/core/ADT/tensor/Buffer.h>
27 #include <nncc/core/ADT/tensor/LexicalLayout.h>
28 #include <nncc/core/ADT/tensor/IndexEnumerator.h>
30 #include <gtest/gtest.h>
32 using nncc::core::ADT::tensor::Index;
33 using nncc::core::ADT::tensor::Shape;
34 using nncc::core::ADT::tensor::LexicalLayout;
35 using nncc::core::ADT::tensor::make_buffer;
36 using nncc::core::ADT::tensor::IndexEnumerator;
37 using nncc::core::ADT::tensor::Buffer;
39 // This file is intended to test FeatureEncode and FeatureDecode at once
43 class NodeExecution_FeatureCodec : public ::testing::Test
49 /// @brief Make Pull node and set data by given buffer and data type
50 template <typename DT> loco::Pull *pull_layer(Buffer<DT> &pull_buf, loco::DataType dtype)
52 auto pull = g.nodes()->create<loco::Pull>();
55 auto pull_data = locomotiv::make_data(pull_buf);
56 locomotiv::annot_data(pull, std::move(pull_data));
57 locomotiv::annot_domain(pull, loco::Domain::Tensor);
62 /// @brief Make FeatureEncode node with given input and encoding permutation
63 loco::FeatureEncode *feature_encode_layer(loco::Node *input,
64 const loco::Permutation<loco::Domain::Feature> &perm)
66 auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::Feature>>(
67 new loco::PermutingEncoder<loco::Domain::Feature>);
71 auto enc = g.nodes()->create<loco::FeatureEncode>();
73 enc->encoder(std::move(encoder));
78 /// @brief Make FeatureDecode node with given input and decoding permutation
79 loco::FeatureDecode *feature_decode_layer(loco::Node *input,
80 const loco::Permutation<loco::Domain::Feature> &perm)
82 auto decoder = std::unique_ptr<loco::PermutingDecoder<loco::Domain::Feature>>(
83 new loco::PermutingDecoder<loco::Domain::Feature>);
87 auto dec = g.nodes()->create<loco::FeatureDecode>();
89 dec->decoder(std::move(decoder));
97 TEST_F(NodeExecution_FeatureCodec, s32)
100 const uint32_t H = 3;
101 const uint32_t W = 4;
102 const uint32_t C = 5;
104 // Make "NCHW" data for pull node
105 auto pull_buf = make_buffer<int32_t, LexicalLayout>(Shape{N, C, H, W});
107 for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance())
109 pull_buf.at(e.current()) = i;
110 ++i; // Doesn't matter what it is
113 // Make NCHW permutation for encoder and decoder
114 loco::Permutation<loco::Domain::Feature> NCHW;
116 NCHW.axis(loco::FeatureAxis::Count) = 0;
117 NCHW.axis(loco::FeatureAxis::Depth) = 1;
118 NCHW.axis(loco::FeatureAxis::Height) = 2;
119 NCHW.axis(loco::FeatureAxis::Width) = 3;
122 auto pull = pull_layer(pull_buf, loco::DataType::S32);
125 auto enc = feature_encode_layer(pull, NCHW);
126 locomotiv::NodeExecution::get().run(enc);
128 // Test FeatureEncode
129 auto enc_data = locomotiv::annot_data(enc);
130 ASSERT_NE(enc_data, nullptr);
131 ASSERT_EQ(loco::DataType::S32, enc_data->dtype());
132 ASSERT_EQ((Shape{N, H, W, C}), *(enc_data->shape())); // locomotiv feature is NHWC
133 auto enc_buf = enc_data->as_s32_bufptr();
134 for (uint32_t n = 0; n < N; ++n)
135 for (uint32_t h = 0; h < H; ++h)
136 for (uint32_t w = 0; w < W; ++w)
137 for (uint32_t c = 0; c < C; ++c)
138 ASSERT_EQ(enc_buf->at(Index{n, h, w, c}), pull_buf.at(Index{n, c, h, w}));
140 ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(enc));
143 auto dec = feature_decode_layer(enc, NCHW);
144 locomotiv::NodeExecution::get().run(dec);
146 // Test FeatureDecode: Encode -> Decode == identity
147 auto dec_data = locomotiv::annot_data(dec);
148 ASSERT_NE(dec_data, nullptr);
149 ASSERT_EQ(loco::DataType::S32, dec_data->dtype());
150 ASSERT_EQ((Shape{N, C, H, W}), *(dec_data->shape()));
151 auto dec_buf = dec_data->as_s32_bufptr();
152 for (uint32_t n = 0; n < N; ++n)
153 for (uint32_t h = 0; h < H; ++h)
154 for (uint32_t w = 0; w < W; ++w)
155 for (uint32_t c = 0; c < C; ++c)
156 ASSERT_EQ(dec_buf->at(Index{n, c, h, w}), pull_buf.at(Index{n, c, h, w}));
158 ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(dec));
161 TEST_F(NodeExecution_FeatureCodec, f32)
163 const uint32_t N = 2;
164 const uint32_t H = 3;
165 const uint32_t W = 4;
166 const uint32_t C = 5;
168 // Make crazy "CHNW" data for pull node
169 auto pull_buf = make_buffer<float, LexicalLayout>(Shape{C, H, N, W});
171 for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance())
173 pull_buf.at(e.current()) = f;
174 f += 0.1f; // Doesn't matter what it is
177 // Make CHNW permutation for encoder and decoder
178 loco::Permutation<loco::Domain::Feature> CHNW;
180 CHNW.axis(loco::FeatureAxis::Depth) = 0;
181 CHNW.axis(loco::FeatureAxis::Height) = 1;
182 CHNW.axis(loco::FeatureAxis::Count) = 2;
183 CHNW.axis(loco::FeatureAxis::Width) = 3;
186 auto pull = pull_layer(pull_buf, loco::DataType::FLOAT32);
189 auto enc = feature_encode_layer(pull, CHNW);
190 locomotiv::NodeExecution::get().run(enc);
192 // Test FeatureEncode
193 auto enc_data = locomotiv::annot_data(enc);
194 ASSERT_NE(enc_data, nullptr);
195 ASSERT_EQ(loco::DataType::FLOAT32, enc_data->dtype());
196 ASSERT_EQ((Shape{N, H, W, C}), *(enc_data->shape())); // locomotiv feature is NHWC
197 auto enc_buf = enc_data->as_f32_bufptr();
198 for (uint32_t n = 0; n < N; ++n)
199 for (uint32_t h = 0; h < H; ++h)
200 for (uint32_t w = 0; w < W; ++w)
201 for (uint32_t c = 0; c < C; ++c)
202 ASSERT_FLOAT_EQ(enc_buf->at(Index{n, h, w, c}), pull_buf.at(Index{c, h, n, w}));
204 ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(enc));
207 auto dec = feature_decode_layer(enc, CHNW);
208 locomotiv::NodeExecution::get().run(dec);
210 // Test FeatureDecode: Encode -> Decode == identity
211 auto dec_data = locomotiv::annot_data(dec);
212 ASSERT_NE(dec_data, nullptr);
213 ASSERT_EQ(loco::DataType::FLOAT32, dec_data->dtype());
214 ASSERT_EQ((Shape{C, H, N, W}), *(dec_data->shape()));
215 auto dec_buf = dec_data->as_f32_bufptr();
216 for (uint32_t n = 0; n < N; ++n)
217 for (uint32_t h = 0; h < H; ++h)
218 for (uint32_t w = 0; w < W; ++w)
219 for (uint32_t c = 0; c < C; ++c)
220 ASSERT_FLOAT_EQ(dec_buf->at(Index{c, h, n, w}), pull_buf.at(Index{c, h, n, w}));
222 ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(dec));