From 62943ae6d0f6cb7a37cef71c28eb784eecb12d3c Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Tue, 12 Aug 2025 22:15:00 +0300 Subject: [PATCH 1/7] split layer --- include/layers/SplitLayer.hpp | 32 ++++++ src/layers/SplitLayer.cpp | 119 ++++++++++++++++++++ test/single_layer/test_splitlayer.cpp | 155 ++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 include/layers/SplitLayer.hpp create mode 100644 src/layers/SplitLayer.cpp create mode 100644 test/single_layer/test_splitlayer.cpp diff --git a/include/layers/SplitLayer.hpp b/include/layers/SplitLayer.hpp new file mode 100644 index 000000000..434e3a051 --- /dev/null +++ b/include/layers/SplitLayer.hpp @@ -0,0 +1,32 @@ +#pragma once +#include + +#include "layers/Layer.hpp" +#include "layers/Tensor.hpp" + +namespace it_lab_ai { + +class SplitLayer : public Layer { + public: + SplitLayer(int axis, const std::vector& splits) + : axis_(axis), splits_(splits) {} + + SplitLayer(int axis, int num_outputs) + : axis_(axis), num_outputs_(num_outputs) {} + void run(const Tensor& input, Tensor& output) override; + void run(const Tensor& input, std::vector& outputs); + + static std::string get_name() { return "SplitLayer"; } + + private: + int axis_; + std::vector splits_; + int num_outputs_ = 0; + + void validate(const Tensor& input) const; + int get_normalized_axis(int rank) const; + template + void split_impl(const Tensor& input, std::vector& outputs) const; +}; + +} // namespace it_lab_ai \ No newline at end of file diff --git a/src/layers/SplitLayer.cpp b/src/layers/SplitLayer.cpp new file mode 100644 index 000000000..4704315f4 --- /dev/null +++ b/src/layers/SplitLayer.cpp @@ -0,0 +1,119 @@ +#include "layers/SplitLayer.hpp" + +namespace it_lab_ai { + +void SplitLayer::run(const Tensor& input, Tensor& output) { output = input; } + +void SplitLayer::run(const Tensor& input, std::vector& outputs) { + validate(input); + const auto& shape = input.get_shape(); + const int axis = get_normalized_axis(static_cast(shape.dims())); + + std::vector part_sizes; + if (!splits_.empty()) { + part_sizes = splits_; + } else { + const int base_size = static_cast(shape[axis]) / num_outputs_; + const int remainder = static_cast(shape[axis]) % num_outputs_; + part_sizes.assign(num_outputs_, base_size); + if (remainder > 0) { + part_sizes.back() += remainder; + } + } + + outputs.clear(); + for (int size : part_sizes) { + Shape out_shape = shape; + out_shape[axis] = static_cast(size); + outputs.emplace_back(out_shape, input.get_type()); + } + + switch (input.get_type()) { + case Type::kFloat: + split_impl(input, outputs); + break; + case Type::kInt: + split_impl(input, outputs); + break; + default: + throw std::runtime_error("Unsupported tensor type"); + } +} + +template +void SplitLayer::split_impl(const Tensor& input, + std::vector& outputs) const { + const auto& input_data = *input.as(); + const Shape& shape = input.get_shape(); + const int axis = get_normalized_axis(static_cast(shape.dims())); + const auto& part_sizes = + splits_.empty() + ? std::vector(num_outputs_, + static_cast(shape[axis]) / num_outputs_) + : splits_; + + size_t outer_size = 1; + for (int i = 0; i < axis; ++i) { + outer_size *= shape[i]; + } + + size_t inner_size = 1; + for (size_t i = axis + 1; i < shape.dims(); ++i) { + inner_size *= shape[i]; + } + + size_t input_offset = 0; + for (auto& output : outputs) { + auto& output_data = *output.as(); + const size_t output_axis_size = output.get_shape()[axis]; + + for (size_t outer = 0; outer < outer_size; ++outer) { + for (size_t a = 0; a < output_axis_size; ++a) { + for (size_t inner = 0; inner < inner_size; ++inner) { + size_t input_pos = outer * shape[axis] * inner_size + + (input_offset + a) * inner_size + inner; + size_t output_pos = + outer * output_axis_size * inner_size + a * inner_size + inner; + output_data[output_pos] = input_data[input_pos]; + } + } + } + input_offset += output_axis_size; + } +} + +void SplitLayer::validate(const Tensor& input) const { + if (input.get_shape().dims() == 0) { + throw std::runtime_error("SplitLayer: Cannot split scalar tensor"); + } + + const int axis = + get_normalized_axis(static_cast(input.get_shape().dims())); + const size_t axis_size = input.get_shape()[axis]; + + if (!splits_.empty()) { + int sum = 0; + for (int s : splits_) { + if (s <= 0) throw std::runtime_error("Split size must be positive"); + sum += s; + } + if (sum != static_cast(axis_size)) { + throw std::runtime_error("Sum of splits must match axis size"); + } + } else if (num_outputs_ <= 0) { + throw std::runtime_error("num_outputs must be positive"); + } +} + +int SplitLayer::get_normalized_axis(int rank) const { + if (axis_ < 0) return axis_ + rank; + if (axis_ >= rank) throw std::runtime_error("Axis out of bounds"); + return axis_; +} + +template void SplitLayer::split_impl(const Tensor&, + std::vector&) const; +template void SplitLayer::split_impl(const Tensor&, + std::vector&) const; + +} // namespace it_lab_ai \ No newline at end of file diff --git a/test/single_layer/test_splitlayer.cpp b/test/single_layer/test_splitlayer.cpp new file mode 100644 index 000000000..df9959576 --- /dev/null +++ b/test/single_layer/test_splitlayer.cpp @@ -0,0 +1,155 @@ +п»ї#include + +#include "gtest/gtest.h" +#include "layers/SplitLayer.hpp" +#include "layers/Tensor.hpp" + +using namespace it_lab_ai; + +TEST(SplitLayerTests, SplitEqualParts1D) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {6}); + SplitLayer splitter(0, 3); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 3); + EXPECT_EQ(outputs[0].get_shape(), Shape({2})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2})); + EXPECT_EQ(outputs[2].get_shape(), Shape({2})); + EXPECT_FLOAT_EQ(outputs[0].get({0}), 1.0f); + EXPECT_FLOAT_EQ(outputs[1].get({0}), 3.0f); + EXPECT_FLOAT_EQ(outputs[2].get({0}), 5.0f); +} + +TEST(SplitLayerTests, SplitVariableParts1D) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {6}); + SplitLayer splitter(0, {2, 4}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({2})); + EXPECT_EQ(outputs[1].get_shape(), Shape({4})); + EXPECT_FLOAT_EQ(outputs[0].get({1}), 2.0f); + EXPECT_FLOAT_EQ(outputs[1].get({3}), 6.0f); +} + +TEST(SplitLayerTests, SplitUnequalParts1D) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6, 7}, {7}); + SplitLayer splitter(0, 4); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 4); + EXPECT_EQ(outputs[0].get_shape(), Shape({2})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2})); + EXPECT_EQ(outputs[2].get_shape(), Shape({2})); + EXPECT_EQ(outputs[3].get_shape(), Shape({1})); +} + +TEST(SplitLayerTests, Split2DAlongAxis0) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {2, 3}); + SplitLayer splitter(0, {1, 1}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({1, 3})); + EXPECT_EQ(outputs[1].get_shape(), Shape({1, 3})); + EXPECT_FLOAT_EQ(outputs[0].get({0, 2}), 3.0f); + EXPECT_FLOAT_EQ(outputs[1].get({0, 0}), 4.0f); +} + +TEST(SplitLayerTests, Split2DAlongAxis1) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {2, 3}); + SplitLayer splitter(1, {1, 2}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({2, 1})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2, 2})); + EXPECT_FLOAT_EQ(outputs[0].get({1, 0}), 4.0f); + EXPECT_FLOAT_EQ(outputs[1].get({0, 1}), 3.0f); +} + +TEST(SplitLayerTests, Split3DEqualParts) { + std::vector data(2 * 3 * 4); + std::iota(data.begin(), data.end(), 0.0f); + Tensor input = make_tensor(data, {2, 3, 4}); + + SplitLayer splitter(1, 3); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 3); + EXPECT_EQ(outputs[0].get_shape(), Shape({2, 1, 4})); + EXPECT_EQ(outputs[1].get({1, 0, 3}), 19.0f); +} + +TEST(SplitLayerTests, Split4DVariableParts) { + std::vector data(1 * 3 * 2 * 4); + std::iota(data.begin(), data.end(), 0.0f); + Tensor input = make_tensor(data, {1, 3, 2, 4}); + + SplitLayer splitter(2, {1, 1}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({1, 3, 1, 4})); + EXPECT_EQ(outputs[1].get({0, 2, 0, 3}), 23.0f); +} + +TEST(SplitLayerTests, SplitNegativeAxis) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {2, 3}); + SplitLayer splitter(-1, {1, 2}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({2, 1})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2, 2})); +} + +TEST(SplitLayerTests, InvalidSplitSizes) { + Tensor input = make_tensor({1, 2, 3, 4}, {4}); + + SplitLayer splitter(0, {1, 2}); + + std::vector outputs; + EXPECT_THROW(splitter.run(input, outputs), std::runtime_error); +} + +TEST(SplitLayerTests, EmptyInputTensor) { + Tensor input = make_tensor({}, {0}); + + SplitLayer splitter(0, {}); + + std::vector outputs; + EXPECT_THROW(splitter.run(input, outputs), std::runtime_error); +} + +TEST(SplitLayerTests, Split192IntoTwo96) { + std::vector input_data(1 * 192 * 56 * 56); + std::iota(input_data.begin(), input_data.end(), 0.0f); + Tensor input = make_tensor(input_data, {1, 192, 56, 56}); + + SplitLayer splitter(1, {96, 96}); + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({1, 96, 56, 56})); + EXPECT_EQ(outputs[1].get_shape(), Shape({1, 96, 56, 56})); + EXPECT_FLOAT_EQ(outputs[0].get({0, 0, 0, 0}), 0.0f); + EXPECT_FLOAT_EQ(outputs[1].get({0, 0, 0, 0}), 96 * 56 * 56); +} \ No newline at end of file From 9b7edbec0400d50b4b6a146a2c96a6ee70c82f17 Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Wed, 13 Aug 2025 18:50:01 +0300 Subject: [PATCH 2/7] del extra test --- test/single_layer/test_splitlayer.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/single_layer/test_splitlayer.cpp b/test/single_layer/test_splitlayer.cpp index df9959576..dbb5a0f7a 100644 --- a/test/single_layer/test_splitlayer.cpp +++ b/test/single_layer/test_splitlayer.cpp @@ -36,20 +36,6 @@ TEST(SplitLayerTests, SplitVariableParts1D) { EXPECT_FLOAT_EQ(outputs[1].get({3}), 6.0f); } -TEST(SplitLayerTests, SplitUnequalParts1D) { - Tensor input = make_tensor({1, 2, 3, 4, 5, 6, 7}, {7}); - SplitLayer splitter(0, 4); - - std::vector outputs; - splitter.run(input, outputs); - - ASSERT_EQ(outputs.size(), 4); - EXPECT_EQ(outputs[0].get_shape(), Shape({2})); - EXPECT_EQ(outputs[1].get_shape(), Shape({2})); - EXPECT_EQ(outputs[2].get_shape(), Shape({2})); - EXPECT_EQ(outputs[3].get_shape(), Shape({1})); -} - TEST(SplitLayerTests, Split2DAlongAxis0) { Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {2, 3}); SplitLayer splitter(0, {1, 1}); From 38a1f2c6a84a200a12c5e6b60c8f54a55254cc65 Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Wed, 13 Aug 2025 19:30:33 +0300 Subject: [PATCH 3/7] add override constructor --- include/layers/SplitLayer.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/layers/SplitLayer.hpp b/include/layers/SplitLayer.hpp index 434e3a051..ce64ff57c 100644 --- a/include/layers/SplitLayer.hpp +++ b/include/layers/SplitLayer.hpp @@ -18,6 +18,10 @@ class SplitLayer : public Layer { static std::string get_name() { return "SplitLayer"; } +#ifdef ENABLE_STATISTIC_WEIGHTS + Tensor get_weights() override { return Tensor(); } +#endif + private: int axis_; std::vector splits_; From c817b3d5481e0640eebf22f5ca3cb521d64f86be Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Thu, 14 Aug 2025 17:47:45 +0300 Subject: [PATCH 4/7] change to optional, add tests, fix check with large negative axis --- include/layers/SplitLayer.hpp | 11 ++-- src/layers/SplitLayer.cpp | 85 ++++++++++++++------------- test/single_layer/test_splitlayer.cpp | 75 +++++++++++++++++++++++ 3 files changed, 127 insertions(+), 44 deletions(-) diff --git a/include/layers/SplitLayer.hpp b/include/layers/SplitLayer.hpp index ce64ff57c..68f695166 100644 --- a/include/layers/SplitLayer.hpp +++ b/include/layers/SplitLayer.hpp @@ -1,4 +1,6 @@ #pragma once +#include +#include #include #include "layers/Layer.hpp" @@ -8,9 +10,10 @@ namespace it_lab_ai { class SplitLayer : public Layer { public: - SplitLayer(int axis, const std::vector& splits) - : axis_(axis), splits_(splits) {} + SplitLayer(int axis, std::vector splits) + : axis_(axis), splits_(std::move(splits)) {} + // Режим 2: количество выходных тензоров SplitLayer(int axis, int num_outputs) : axis_(axis), num_outputs_(num_outputs) {} void run(const Tensor& input, Tensor& output) override; @@ -24,8 +27,8 @@ class SplitLayer : public Layer { private: int axis_; - std::vector splits_; - int num_outputs_ = 0; + std::optional> splits_; + std::optional num_outputs_; void validate(const Tensor& input) const; int get_normalized_axis(int rank) const; diff --git a/src/layers/SplitLayer.cpp b/src/layers/SplitLayer.cpp index 4704315f4..8a2304f56 100644 --- a/src/layers/SplitLayer.cpp +++ b/src/layers/SplitLayer.cpp @@ -6,27 +6,6 @@ void SplitLayer::run(const Tensor& input, Tensor& output) { output = input; } void SplitLayer::run(const Tensor& input, std::vector& outputs) { validate(input); - const auto& shape = input.get_shape(); - const int axis = get_normalized_axis(static_cast(shape.dims())); - - std::vector part_sizes; - if (!splits_.empty()) { - part_sizes = splits_; - } else { - const int base_size = static_cast(shape[axis]) / num_outputs_; - const int remainder = static_cast(shape[axis]) % num_outputs_; - part_sizes.assign(num_outputs_, base_size); - if (remainder > 0) { - part_sizes.back() += remainder; - } - } - - outputs.clear(); - for (int size : part_sizes) { - Shape out_shape = shape; - out_shape[axis] = static_cast(size); - outputs.emplace_back(out_shape, input.get_type()); - } switch (input.get_type()) { case Type::kFloat: @@ -36,7 +15,7 @@ void SplitLayer::run(const Tensor& input, std::vector& outputs) { split_impl(input, outputs); break; default: - throw std::runtime_error("Unsupported tensor type"); + throw std::runtime_error("Unsupported tensor data type"); } } @@ -46,11 +25,23 @@ void SplitLayer::split_impl(const Tensor& input, const auto& input_data = *input.as(); const Shape& shape = input.get_shape(); const int axis = get_normalized_axis(static_cast(shape.dims())); - const auto& part_sizes = - splits_.empty() - ? std::vector(num_outputs_, - static_cast(shape[axis]) / num_outputs_) - : splits_; + + std::vector part_sizes; + if (splits_) { + part_sizes = *splits_; + } else { + const int total_size = static_cast(shape[axis]); + const int base_size = total_size / *num_outputs_; + const int remainder = total_size % *num_outputs_; + + part_sizes.reserve(*num_outputs_); + for (int i = 0; i < *num_outputs_; ++i) { + part_sizes.push_back(i < remainder ? base_size + 1 : base_size); + } + } + + outputs.clear(); + outputs.reserve(part_sizes.size()); size_t outer_size = 1; for (int i = 0; i < axis; ++i) { @@ -63,9 +54,17 @@ void SplitLayer::split_impl(const Tensor& input, } size_t input_offset = 0; - for (auto& output : outputs) { - auto& output_data = *output.as(); - const size_t output_axis_size = output.get_shape()[axis]; + for (size_t part = 0; part < part_sizes.size(); ++part) { + const size_t output_axis_size = part_sizes[part]; + + std::vector output_shape_vec(shape.dims()); + for (size_t i = 0; i < shape.dims(); ++i) { + output_shape_vec[i] = (i == axis) ? output_axis_size : shape[i]; + } + Shape output_shape(output_shape_vec); + + outputs.emplace_back(output_shape, input.get_type()); + auto& output_data = *outputs.back().as(); for (size_t outer = 0; outer < outer_size; ++outer) { for (size_t a = 0; a < output_axis_size; ++a) { @@ -84,31 +83,37 @@ void SplitLayer::split_impl(const Tensor& input, void SplitLayer::validate(const Tensor& input) const { if (input.get_shape().dims() == 0) { - throw std::runtime_error("SplitLayer: Cannot split scalar tensor"); + throw std::runtime_error("Cannot split scalar tensor"); } const int axis = get_normalized_axis(static_cast(input.get_shape().dims())); - const size_t axis_size = input.get_shape()[axis]; + const int axis_size = static_cast(input.get_shape()[axis]); - if (!splits_.empty()) { + if (splits_) { int sum = 0; - for (int s : splits_) { + for (int s : *splits_) { if (s <= 0) throw std::runtime_error("Split size must be positive"); sum += s; } - if (sum != static_cast(axis_size)) { + if (sum != axis_size) { throw std::runtime_error("Sum of splits must match axis size"); } - } else if (num_outputs_ <= 0) { - throw std::runtime_error("num_outputs must be positive"); + } else { + if (*num_outputs_ <= 0) { + throw std::runtime_error("num_outputs must be positive"); + } + if (*num_outputs_ > axis_size) { + throw std::runtime_error("num_outputs cannot be greater than axis size"); + } } } int SplitLayer::get_normalized_axis(int rank) const { - if (axis_ < 0) return axis_ + rank; - if (axis_ >= rank) throw std::runtime_error("Axis out of bounds"); - return axis_; + if (axis_ < -rank || axis_ >= rank) { + throw std::runtime_error("Axis out of bounds"); + } + return (axis_ < 0) ? axis_ + rank : axis_; } template void SplitLayer::split_impl(const Tensor&, diff --git a/test/single_layer/test_splitlayer.cpp b/test/single_layer/test_splitlayer.cpp index dbb5a0f7a..4ca88d84b 100644 --- a/test/single_layer/test_splitlayer.cpp +++ b/test/single_layer/test_splitlayer.cpp @@ -138,4 +138,79 @@ TEST(SplitLayerTests, Split192IntoTwo96) { EXPECT_EQ(outputs[1].get_shape(), Shape({1, 96, 56, 56})); EXPECT_FLOAT_EQ(outputs[0].get({0, 0, 0, 0}), 0.0f); EXPECT_FLOAT_EQ(outputs[1].get({0, 0, 0, 0}), 96 * 56 * 56); +} + +TEST(SplitLayerTests, UnevenSplitWithRemainder) { + Tensor input = make_tensor({1, 2, 3, 4, 5}, {5}); + SplitLayer splitter(0, 3); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 3); + EXPECT_EQ(outputs[0].get_shape(), Shape({2})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2})); + EXPECT_EQ(outputs[2].get_shape(), Shape({1})); + EXPECT_FLOAT_EQ(outputs[0].get({1}), 2.0f); + EXPECT_FLOAT_EQ(outputs[1].get({1}), 4.0f); + EXPECT_FLOAT_EQ(outputs[2].get({0}), + 5.0f); +} + +TEST(SplitLayerTests, NumOutputsGreaterThanAxisSize) { + Tensor input = make_tensor({1, 2, 3}, {3}); + SplitLayer splitter(0, 5); + + std::vector outputs; + EXPECT_THROW(splitter.run(input, outputs), std::runtime_error); +} + +TEST(SplitLayerTests, IntegerDataType) { + Tensor input = make_tensor({1, 2, 3, 4, 5, 6}, {2, 3}); + SplitLayer splitter(1, {1, 2}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({2, 1})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2, 2})); + EXPECT_EQ(outputs[0].get({1, 0}), 4); + EXPECT_EQ(outputs[1].get({0, 1}), 3); +} + +TEST(SplitLayerTests, NegativeAxis2D) { + Tensor input = make_tensor({1, 2, 3, 4}, {2, 2}); + SplitLayer splitter(-2, {1, 1}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({1, 2})); + EXPECT_EQ(outputs[1].get_shape(), Shape({1, 2})); +} + +TEST(SplitLayerTests, NegativeAxis3D) { + std::vector data(2 * 3 * 4); + std::iota(data.begin(), data.end(), 1.0f); + Tensor input = make_tensor(data, {2, 3, 4}); + + SplitLayer splitter(-1, {1, 3}); + + std::vector outputs; + splitter.run(input, outputs); + + ASSERT_EQ(outputs.size(), 2); + EXPECT_EQ(outputs[0].get_shape(), Shape({2, 3, 1})); + EXPECT_EQ(outputs[1].get_shape(), Shape({2, 3, 3})); + EXPECT_FLOAT_EQ(outputs[0].get({1, 2, 0}), 21.0f); +} + +TEST(SplitLayerTests, LargeAxisValue) { + Tensor input = make_tensor({1, 2, 3, 4}, {2, 2}); + + SplitLayer splitter(10, {1, 1}); + std::vector outputs; + EXPECT_THROW(splitter.run(input, outputs), std::runtime_error); } \ No newline at end of file From f96de58eae14d94c0f971e2b6d1f36072de043d9 Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Thu, 14 Aug 2025 17:59:32 +0300 Subject: [PATCH 5/7] use block copies, fix check num_outputs_ > size_along_axis --- include/layers/SplitLayer.hpp | 1 - src/layers/SplitLayer.cpp | 35 ++++++++++++++++++--------- test/single_layer/test_splitlayer.cpp | 3 +-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/include/layers/SplitLayer.hpp b/include/layers/SplitLayer.hpp index 68f695166..246c3b5da 100644 --- a/include/layers/SplitLayer.hpp +++ b/include/layers/SplitLayer.hpp @@ -13,7 +13,6 @@ class SplitLayer : public Layer { SplitLayer(int axis, std::vector splits) : axis_(axis), splits_(std::move(splits)) {} - // Режим 2: количество выходных тензоров SplitLayer(int axis, int num_outputs) : axis_(axis), num_outputs_(num_outputs) {} void run(const Tensor& input, Tensor& output) override; diff --git a/src/layers/SplitLayer.cpp b/src/layers/SplitLayer.cpp index 8a2304f56..3824d513f 100644 --- a/src/layers/SplitLayer.cpp +++ b/src/layers/SplitLayer.cpp @@ -40,9 +40,6 @@ void SplitLayer::split_impl(const Tensor& input, } } - outputs.clear(); - outputs.reserve(part_sizes.size()); - size_t outer_size = 1; for (int i = 0; i < axis; ++i) { outer_size *= shape[i]; @@ -53,6 +50,12 @@ void SplitLayer::split_impl(const Tensor& input, inner_size *= shape[i]; } + const size_t input_axis_stride = shape[axis] * inner_size; + const size_t input_inner_stride = inner_size; + + outputs.clear(); + outputs.reserve(part_sizes.size()); + size_t input_offset = 0; for (size_t part = 0; part < part_sizes.size(); ++part) { const size_t output_axis_size = part_sizes[part]; @@ -66,17 +69,17 @@ void SplitLayer::split_impl(const Tensor& input, outputs.emplace_back(output_shape, input.get_type()); auto& output_data = *outputs.back().as(); + const size_t output_part_size = output_axis_size * inner_size; + const size_t input_part_size = output_part_size; + for (size_t outer = 0; outer < outer_size; ++outer) { - for (size_t a = 0; a < output_axis_size; ++a) { - for (size_t inner = 0; inner < inner_size; ++inner) { - size_t input_pos = outer * shape[axis] * inner_size + - (input_offset + a) * inner_size + inner; - size_t output_pos = - outer * output_axis_size * inner_size + a * inner_size + inner; - output_data[output_pos] = input_data[input_pos]; - } - } + const T* input_start = + &input_data[outer * input_axis_stride + input_offset * inner_size]; + T* output_start = &output_data[outer * output_part_size]; + + std::copy_n(input_start, output_part_size, output_start); } + input_offset += output_axis_size; } } @@ -107,6 +110,14 @@ void SplitLayer::validate(const Tensor& input) const { throw std::runtime_error("num_outputs cannot be greater than axis size"); } } + + if (!splits_ && num_outputs_) { + if (*num_outputs_ > axis_size) { + throw std::runtime_error("num_outputs (" + std::to_string(*num_outputs_) + + ") cannot be greater than axis size (" + + std::to_string(axis_size) + ")"); + } + } } int SplitLayer::get_normalized_axis(int rank) const { diff --git a/test/single_layer/test_splitlayer.cpp b/test/single_layer/test_splitlayer.cpp index 4ca88d84b..93d19d417 100644 --- a/test/single_layer/test_splitlayer.cpp +++ b/test/single_layer/test_splitlayer.cpp @@ -153,8 +153,7 @@ TEST(SplitLayerTests, UnevenSplitWithRemainder) { EXPECT_EQ(outputs[2].get_shape(), Shape({1})); EXPECT_FLOAT_EQ(outputs[0].get({1}), 2.0f); EXPECT_FLOAT_EQ(outputs[1].get({1}), 4.0f); - EXPECT_FLOAT_EQ(outputs[2].get({0}), - 5.0f); + EXPECT_FLOAT_EQ(outputs[2].get({0}), 5.0f); } TEST(SplitLayerTests, NumOutputsGreaterThanAxisSize) { From b983fe299b8b8e4c74b0d2af77bf138ac75bf03d Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Thu, 14 Aug 2025 18:07:01 +0300 Subject: [PATCH 6/7] fix --- src/layers/SplitLayer.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/layers/SplitLayer.cpp b/src/layers/SplitLayer.cpp index 3824d513f..c0f9a8962 100644 --- a/src/layers/SplitLayer.cpp +++ b/src/layers/SplitLayer.cpp @@ -1,5 +1,8 @@ #include "layers/SplitLayer.hpp" +#include +#include + namespace it_lab_ai { void SplitLayer::run(const Tensor& input, Tensor& output) { output = input; } @@ -51,18 +54,18 @@ void SplitLayer::split_impl(const Tensor& input, } const size_t input_axis_stride = shape[axis] * inner_size; - const size_t input_inner_stride = inner_size; outputs.clear(); outputs.reserve(part_sizes.size()); size_t input_offset = 0; for (size_t part = 0; part < part_sizes.size(); ++part) { - const size_t output_axis_size = part_sizes[part]; + const size_t output_axis_size = static_cast(part_sizes[part]); std::vector output_shape_vec(shape.dims()); for (size_t i = 0; i < shape.dims(); ++i) { - output_shape_vec[i] = (i == axis) ? output_axis_size : shape[i]; + output_shape_vec[i] = + (static_cast(i) == axis) ? output_axis_size : shape[i]; } Shape output_shape(output_shape_vec); @@ -70,7 +73,6 @@ void SplitLayer::split_impl(const Tensor& input, auto& output_data = *outputs.back().as(); const size_t output_part_size = output_axis_size * inner_size; - const size_t input_part_size = output_part_size; for (size_t outer = 0; outer < outer_size; ++outer) { const T* input_start = @@ -106,12 +108,6 @@ void SplitLayer::validate(const Tensor& input) const { if (*num_outputs_ <= 0) { throw std::runtime_error("num_outputs must be positive"); } - if (*num_outputs_ > axis_size) { - throw std::runtime_error("num_outputs cannot be greater than axis size"); - } - } - - if (!splits_ && num_outputs_) { if (*num_outputs_ > axis_size) { throw std::runtime_error("num_outputs (" + std::to_string(*num_outputs_) + ") cannot be greater than axis size (" + From a3a29fca1956695ef875c0521611d5707faf6360 Mon Sep 17 00:00:00 2001 From: MikeMuradov Date: Thu, 14 Aug 2025 18:16:58 +0300 Subject: [PATCH 7/7] fix static analyzis --- src/layers/SplitLayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layers/SplitLayer.cpp b/src/layers/SplitLayer.cpp index c0f9a8962..3346b08ef 100644 --- a/src/layers/SplitLayer.cpp +++ b/src/layers/SplitLayer.cpp @@ -59,8 +59,8 @@ void SplitLayer::split_impl(const Tensor& input, outputs.reserve(part_sizes.size()); size_t input_offset = 0; - for (size_t part = 0; part < part_sizes.size(); ++part) { - const size_t output_axis_size = static_cast(part_sizes[part]); + for (const auto part_size : part_sizes) { + const auto output_axis_size = static_cast(part_size); std::vector output_shape_vec(shape.dims()); for (size_t i = 0; i < shape.dims(); ++i) {