2 // Copyright (c) 2017 Intel Corporation
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 ///////////////////////////////////////////////////////////////////////////////////////////////////
18 #include <gtest/gtest.h>
19 #include "api/CPP/memory.hpp"
20 #include <api/CPP/input_layout.hpp>
21 #include "api/CPP/split.hpp"
22 #include "api/CPP/scale.hpp"
23 #include <api/CPP/topology.hpp>
24 #include <api/CPP/network.hpp>
25 #include <api/CPP/engine.hpp>
26 #include <api/CPP/reorder.hpp>
27 #include "test_utils/test_utils.h"
32 using namespace cldnn;
33 using namespace tests;
36 std::vector<T> generate_random_input(size_t b, size_t f, size_t y, size_t x, int min, int max) {
37 static std::default_random_engine generator(random_seed);
38 int k = 8; // 1/k is the resolution of the floating point numbers
39 std::uniform_int_distribution<int> distribution(k * min, k * max);
40 std::vector<T> v(b*f*x*y);
41 for (size_t i = 0; i < b*f*x*y; ++i) {
42 v[i] = (T)distribution(generator);
49 void check_feature_map(cldnn::pointer<T> output_ptr, std::vector<T> &input_vec, size_t batch_num, size_t feature_num, size_t y_size, size_t x_size, size_t feature_id, size_t factor)
51 for (size_t b = 0; b < batch_num; ++b) { //B
52 for (size_t y = 0; y < y_size; ++y) { //Y
53 for (size_t x = 0; x < x_size; ++x) { //X
54 auto linear_id = x + x_size * (y + y_size * (feature_id + feature_num * b));
55 auto output_linear_id = x + x_size * (y + y_size * b);
56 EXPECT_EQ(output_ptr[output_linear_id], input_vec[linear_id] * factor);
63 void split_test(int batch_num, int feature_num, int x_size, int y_size, std::vector<cldnn::tensor> split_offsets)
65 const auto& engine = get_test_engine();
66 cldnn::tensor reference_input_size = { batch_num, feature_num, x_size, y_size };
68 cldnn::memory input = memory::allocate(engine, { type_to_data_type<T>::value, format::bfyx, reference_input_size });
69 std::vector<std::pair<primitive_id, cldnn::tensor> > input_ids_offsets;
72 topology.add(input_layout("input", input.get_layout()));
74 // lambda exoression to create the primitive id for the splits
75 auto create_split_id = [](size_t splitNum) {
77 ss << std::setw(5) << std::setfill('0') << splitNum;
82 // Create the splits with the split ids for the topology
83 for (size_t splitNum = 0; splitNum < split_offsets.size(); splitNum++)
85 input_ids_offsets.push_back({ create_split_id(splitNum), split_offsets[splitNum]});
88 topology.add(split("split", "input", input_ids_offsets));
90 std::vector<T> input_vec = generate_random_input<T>(batch_num, feature_num, y_size, x_size, -10, 10);
91 set_values(input, input_vec);
93 network network(engine, topology);
94 network.set_input_data("input", input);
96 auto outputs = network.execute();
98 // The number of splits should match the expected number of splits
99 EXPECT_EQ(outputs.size(), size_t(split_offsets.size()));
101 std::vector<cldnn::tensor> expected_sizes;
102 for (size_t splitNum = 0; splitNum < split_offsets.size(); splitNum++) // Calculate the expected sizes
106 if (splitNum < (split_offsets.size() - 1))
108 size = split_offsets[splitNum + 1] - split_offsets[splitNum];
112 size = reference_input_size - split_offsets[splitNum];
115 // For all the other dimensions, copy from the split_input
116 for (int dimension = 0; dimension < CLDNN_TENSOR_DIM_MAX; dimension++)
119 = (size.raw[dimension] == 0) ? reference_input_size.raw[dimension] : size.raw[dimension];
122 expected_sizes.push_back(size);
125 pointer<T> input_ptr = input.pointer<T>();
127 for (size_t splitNum = 0; splitNum < split_offsets.size(); splitNum++)
129 primitive_id split_id = "split:" + create_split_id(splitNum);
130 cldnn::memory output = outputs.at(split_id).get_memory();
131 auto prim = output.get_layout();
132 EXPECT_EQ(prim.size, expected_sizes[splitNum]);
133 auto output_ptr = output.pointer<T>();
135 // Output tensor size
136 auto output_batch = prim.size.batch[0];
137 auto output_feature = prim.size.feature[0];
138 auto output_x = prim.size.spatial[0];
139 auto output_y = prim.size.spatial[1];
141 // Input offsets, starting from which we will compare the output
142 auto input_batch_offset = split_offsets[splitNum].batch[0];
143 auto input_feature_offset = split_offsets[splitNum].feature[0];
144 auto input_y_offset = split_offsets[splitNum].spatial[1];
145 auto input_x_offset = split_offsets[splitNum].spatial[0];
147 // iterator to iterate through input buffer
148 auto input_batch_itr = input_batch_offset;
149 auto input_feature_itr = input_feature_offset;
150 auto input_y_itr = input_y_offset;
151 auto input_x_itr = input_x_offset;
153 for (auto b = 0; b < output_batch; ++b) { // B
155 // reset the input feature iterator
156 input_feature_itr = input_feature_offset;
157 for (auto f = 0; f < output_feature; f++) { // F
159 // reset the input y iterator
160 input_y_itr = input_y_offset;
161 for (auto y = 0; y < output_y; y++) { // Y
163 // reset the input x iterator
164 input_x_itr = input_x_offset;
165 for (auto x = 0; x < output_x; x++) { // X
166 auto linear_id = input_x_itr + x_size * (input_y_itr + y_size * (input_feature_itr + feature_num * input_batch_itr)); // index in input
167 auto output_linear_id = x + output_x * (y + output_y * (f + output_feature * b)); // index in output
168 EXPECT_EQ(output_ptr[output_linear_id], input_vec[linear_id]);
169 input_x_itr++; // update the input x iterator
171 input_y_itr++; // update the input y iterator
173 input_feature_itr++; // update the input feature iterator
175 input_batch_itr++; // update the input batch iterator
180 TEST(split_gpu, split_1d_uneven_2_splits) {
186 // id: "out0", offsets: { 0, 0, 0, 0 }
187 // id: "out1", offsets: { 0, 1, 0, 0 }
190 auto feature_num = 4;
193 std::vector<cldnn::tensor> split_offsets = {
198 split_test<float>(batch_num, feature_num, x_size, y_size, split_offsets);
202 TEST(split_gpu, basic_split_concat_optimization) {
204 const auto& engine = get_test_engine();
206 auto input = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 25, 1, 256 } });
207 tests::set_random_values<float>(input);
210 topology.add(input_layout("input", input.get_layout()));
211 std::vector<std::pair<primitive_id, tensor>> offsets;
212 std::vector<primitive_id> ids;
213 for (int i = 0; i < 25; i++)
215 auto id = "crop_" + std::to_string(i);
216 ids.push_back("split:" + id);
217 offsets.push_back({ id, {0, i, 0, 0} });
220 topology.add(split("split", "input", offsets));
221 topology.add(concatenation("concat", ids, concatenation::along_f));
222 topology.add(reorder("output", "concat", format::bfyx, data_types::f32));
225 opts.set_option(build_option::optimize_data(true));
226 network network(engine, topology, opts);
228 network.set_input_data("input", input);
230 auto outputs = network.execute();
232 auto output = outputs.at("output").get_memory();
233 auto output_ptr = output.pointer<float>();
234 auto input_ptr = input.pointer<float>();
236 for (int i = 0; i < 25*256; ++i)
238 EXPECT_EQ(output_ptr[i], input_ptr[i]);
242 TEST(split_gpu, split_1d_uneven_3_splits) {
249 // id: "out0", offsets: { 0, 0, 0, 0 }
250 // id: "out1", offsets: { 0, 1, 0, 0 }
251 // id: "out2", offsets: { 0, 4, 0, 0 }
254 auto feature_num = 8;
257 std::vector<cldnn::tensor> split_offsets = {
263 split_test<float>(batch_num, feature_num, x_size, y_size, split_offsets);
266 TEST(split_gpu, split_2d_uneven_2_splits) {
272 // id: "out0", offsets: { 0, 0, 0, 0 }
273 // id: "out1", offsets: { 0, 1, 4, 0 }
276 auto feature_num = 8;
279 std::vector<cldnn::tensor> split_offsets = {
284 split_test<float>(batch_num, feature_num, x_size, y_size, split_offsets);
287 TEST(split_gpu, split_2d_uneven_3_split3) {
294 // id: "out0", offsets: { 0, 0, 0, 0 }
295 // id: "out1", offsets: { 0, 1, 4, 0 }
296 // id: "out2", offsets: { 0, 4, 7, 0 }
299 auto feature_num = 8;
302 std::vector<cldnn::tensor> split_offsets = {
308 split_test<float>(batch_num, feature_num, x_size, y_size, split_offsets);
311 TEST(split_gpu, split_3d_uneven_2_splits) {
317 // id: "out0", offsets: { 0, 0, 0, 0 }
318 // id: "out1", offsets: { 0, 1, 4, 1 }
321 auto feature_num = 8;
324 std::vector<cldnn::tensor> split_offsets = {
329 split_test<float>(batch_num, feature_num, x_size, y_size, split_offsets);
332 TEST(split_gpu, split_3d_uneven_3_splits) {
339 // id: "out0", offsets: { 0, 0, 0, 0 }
340 // id: "out1", offsets: { 0, 1, 4, 1 }
341 // id: "out2", offsets: { 0, 7, 8, 2 }
344 auto feature_num = 8;
347 std::vector<cldnn::tensor> split_offsets = {
353 split_test<float>(batch_num, feature_num, x_size, y_size, split_offsets);
356 TEST(split_gpu, basic_in2x3x2x2_split_feature_bfyx) {
358 // 3 x Outputs: 6x1x4x3
360 // id: "out0", offsets: { 0, 0, 0, 0 }
361 // id: "out1", offsets: { 0, 1, 0, 0 }
362 // id: "out2", offsets: { 0, 2, 0, 0 }
364 const auto& engine = get_test_engine();
367 auto feature_num = 3;
371 auto input = memory::allocate(engine, { data_types::f32,format::bfyx,{ batch_num, feature_num, x_size, y_size } });
374 topology.add(input_layout("input", input.get_layout()));
375 topology.add(split("split", "input",
377 { "out0", { 0, 0, 0, 0 } },
378 { "out1", { 0, 1, 0, 0 } },
379 { "out2", { 0, 2, 0, 0 } }
382 std::vector<float> input_vec = generate_random_input<float>(batch_num, feature_num, y_size, x_size, -10, 10);
383 set_values(input, input_vec);
385 network network(engine, topology);
387 network.set_input_data("input", input);
389 auto outputs = network.execute();
391 EXPECT_EQ(outputs.size(), size_t(3));
393 for (unsigned int i = 0; i < 3; i++)
395 auto split_id = "split:out" + std::to_string(i);
396 auto output = outputs.at(split_id).get_memory();
397 auto output_ptr = output.pointer<float>();
398 check_feature_map<float>(output_ptr, input_vec, batch_num, feature_num, y_size, x_size, i, 1);
402 TEST(split_gpu, basic_in2x3x2x2_split_scale_feature_bfyx) {
404 // 3 x Outputs: 6x1x4x3
406 // id: "out0", offsets: { 0, 0, 0, 0 }
407 // id: "out1", offsets: { 0, 1, 0, 0 }
408 // id: "out2", offsets: { 0, 2, 0, 0 }
409 // Additional scale layer at the end
411 const auto& engine = get_test_engine();
414 auto feature_num = 3;
418 auto input = memory::allocate(engine, { data_types::f32,format::bfyx,{ batch_num, feature_num, x_size, y_size } });
419 auto scale_input0 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 1, 1 } });
420 auto scale_input1 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 1, 1 } });
421 auto scale_input2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 1, 1 } });
424 topology.add(input_layout("input", input.get_layout()));
425 topology.add(input_layout("scale_input0", scale_input0.get_layout()));
426 topology.add(input_layout("scale_input1", scale_input1.get_layout()));
427 topology.add(input_layout("scale_input2", scale_input2.get_layout()));
428 topology.add(split("split", "input",
430 { "out0",{ 0, 0, 0, 0 } },
431 { "out1",{ 0, 1, 0, 0 } },
432 { "out2",{ 0, 2, 0, 0 } }
434 topology.add(scale("scale0", "split:out0", "scale_input0"));
435 topology.add(scale("scale1", "split:out1", "scale_input1"));
436 topology.add(scale("scale2", "split:out2", "scale_input2"));
438 std::vector<float> scale_input_vec0 = { 1.f };
439 set_values(scale_input0, scale_input_vec0);
440 std::vector<float> scale_input_vec1 = { 2.f };
441 set_values(scale_input1, scale_input_vec1);
442 std::vector<float> scale_input_vec2 = { 3.f };
443 set_values(scale_input2, scale_input_vec2);
445 std::vector<float> input_vec = generate_random_input<float>(batch_num, feature_num, y_size, x_size, -10, 10);
446 set_values(input, input_vec);
448 network network(engine, topology);
450 network.set_input_data("input", input);
451 network.set_input_data("scale_input0", scale_input0);
452 network.set_input_data("scale_input1", scale_input1);
453 network.set_input_data("scale_input2", scale_input2);
455 auto outputs = network.execute();
457 EXPECT_EQ(outputs.size(), size_t(3));
459 for (unsigned int i = 0; i < 3; i++)
461 auto split_id = "scale" + std::to_string(i);
462 auto output = outputs.at(split_id).get_memory();
463 auto output_ptr = output.pointer<float>();
464 check_feature_map<float>(output_ptr, input_vec, batch_num, feature_num, y_size, x_size, i, i + 1);