From 06c0d3a5cc2f07710467eb50994485fdb52dcb4a Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 27 Feb 2026 17:57:30 +0300 Subject: [PATCH 01/14] Changes --- include/CMakeLists.txt | 9 +- include/layers/Layer.hpp | 3 +- include/layers_fused/ConvRelu.hpp | 69 +++ src/CMakeLists.txt | 1 + src/layers_fused/CMakeLists.txt | 8 + src/layers_fused/ConvRelu.cpp | 175 ++++++ test/CMakeLists.txt | 2 +- test/single_layer_fused/test_convrelu.cpp | 707 ++++++++++++++++++++++ 8 files changed, 968 insertions(+), 6 deletions(-) create mode 100644 include/layers_fused/ConvRelu.hpp create mode 100644 src/layers_fused/CMakeLists.txt create mode 100644 src/layers_fused/ConvRelu.cpp create mode 100644 test/single_layer_fused/test_convrelu.cpp diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index c9ac0850c..f280d2f30 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -5,16 +5,17 @@ file(GLOB_RECURSE graphT_headers graph_transformations/*.h graph_transformations set(GRAPHT_HEADERS "${graphT_headers}" PARENT_SCOPE) file(GLOB_RECURSE layers_headers layers/*.h layers/*.hpp) -set(LAYERS_HEADERS "${layers_headers}" PARENT_SCOPE) +file(GLOB_RECURSE parallel_headers parallel/*.h parallel/*.hpp) +set(LAYERS_HEADERS "${layers_headers}" "${parallel_headers}" PARENT_SCOPE) file(GLOB_RECURSE layers_oneDNN_headers layers_oneDNN/*.h layers_oneDNN/*.hpp) set(LAYERS_ONEDNN_HEADERS "${layers_oneDNN_headers}" PARENT_SCOPE) +file(GLOB_RECURSE layers_fused_headers layers_fused/*.h layers_fused/*.hpp) +set(LAYERS_FUSED_HEADERS "${layers_fused_headers}" PARENT_SCOPE) + file(GLOB_RECURSE perf_headers perf/*.h perf/*.hpp) set(PERF_HEADERS "${perf_headers}" PARENT_SCOPE) file(GLOB_RECURSE reader_headers Weights_Reader/*.h Weights_Reader/*.hpp) set(READER_HEADERS "${reader_headers}" PARENT_SCOPE) - -file(GLOB_RECURSE parallel_headers parallel/*.h parallel/*.hpp) -set(LAYERS_HEADERS "${parallel_headers}" PARENT_SCOPE) diff --git a/include/layers/Layer.hpp b/include/layers/Layer.hpp index 0139912f8..706d419f0 100644 --- a/include/layers/Layer.hpp +++ b/include/layers/Layer.hpp @@ -34,7 +34,8 @@ enum LayerType : uint8_t { kReshape, kSoftmax, kMatmul, - kBatchNormalization + kBatchNormalization, + kConvRelu }; enum ImplType : uint8_t { kDefault, kTBB, kSTL }; diff --git a/include/layers_fused/ConvRelu.hpp b/include/layers_fused/ConvRelu.hpp new file mode 100644 index 000000000..e1fdb94bc --- /dev/null +++ b/include/layers_fused/ConvRelu.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#include "layers/Layer.hpp" +#include "layers/Tensor.hpp" + +namespace it_lab_ai { + +template +void relu(Tensor& t) { + Shape sh = t.get_shape(); + for (size_t i = 0; i < sh.count(); i++) { + if ((*t.as())[i] < 0) { + (*t.as())[i] = 0; + } + } +} + +class ConvReluLayer : Layer { + private: + size_t stride_; + size_t pads_; + size_t dilations_; + std::shared_ptr kernel_; + std::shared_ptr bias_; + size_t group_; + bool useLegacyImpl_; + + public: + ConvReluLayer() : Layer(kConvRelu), kernel_(nullptr), bias_(nullptr) { + stride_ = 0; + pads_ = 0; + dilations_ = 0; + } + ConvReluLayer(size_t step, size_t pads, size_t dilations, + const Tensor& kernel, const Tensor& bias = Tensor(), + size_t group = 1, bool useLegacyImpl = false) + : Layer(kConvRelu), + kernel_(std::make_shared(kernel)), + bias_(std::make_shared(bias)) { + stride_ = step; + pads_ = pads; + group_ = group; + dilations_ = dilations; + useLegacyImpl_ = useLegacyImpl; + } + ConvReluLayer(size_t step, size_t pads, size_t dilations, + std::shared_ptr kernel, + std::shared_ptr bias = std::make_shared(), + size_t group = 1, bool useLegacyImpl = false) + : Layer(kConvRelu), kernel_(std::move(kernel)), bias_(std::move(bias)) { + stride_ = step; + pads_ = pads; + group_ = group; + dilations_ = dilations; + useLegacyImpl_ = useLegacyImpl; + } + void run(const std::vector& input, + std::vector& output) override; + void run(const std::vector& input, std::vector& output, + const RuntimeOptions& options) override; +#ifdef ENABLE_STATISTIC_WEIGHTS + Tensor get_weights() override { return *kernel_; } +#endif +}; +} // namespace it_lab_ai \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ab526f5b8..01e4acb04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,5 +2,6 @@ add_subdirectory(graph) add_subdirectory(graph_transformations) add_subdirectory(perf) add_subdirectory(layers) +add_subdirectory(layers_fused) add_subdirectory(layers_oneDNN) add_subdirectory(Weights_Reader) diff --git a/src/layers_fused/CMakeLists.txt b/src/layers_fused/CMakeLists.txt new file mode 100644 index 000000000..029a4c24e --- /dev/null +++ b/src/layers_fused/CMakeLists.txt @@ -0,0 +1,8 @@ +file(GLOB_RECURSE layers_fused_src *.cpp) +add_library(layers_fused_lib STATIC "${LAYERS_FUSED_HEADERS}" "${layers_fused_src}") + +target_link_libraries(layers_fused_lib PUBLIC layers_lib) +target_link_libraries(layers_fused_lib PUBLIC TBB_unified) +target_link_libraries(layers_fused_lib PUBLIC OpenMP::OpenMP_CXX) +target_link_libraries(layers_fused_lib PUBLIC dnnl) +target_link_libraries(layers_fused_lib PUBLIC Kokkos_imported) \ No newline at end of file diff --git a/src/layers_fused/ConvRelu.cpp b/src/layers_fused/ConvRelu.cpp new file mode 100644 index 000000000..4cd6db94d --- /dev/null +++ b/src/layers_fused/ConvRelu.cpp @@ -0,0 +1,175 @@ +#include "layers_fused/ConvRelu.hpp" +#include "layers/ConvLayer.hpp" + +namespace it_lab_ai { + +void ConvReluLayer::run(const std::vector& input, + std::vector& output) { + RuntimeOptions default_options; + run(input, output, default_options); +} + +void ConvReluLayer::run(const std::vector& input, + std::vector& output, + const RuntimeOptions& options) { + if (kernel_ == nullptr || bias_ == nullptr) { + throw std::runtime_error("ConvReluLayer: no weights or bias"); + } + if (input.size() != 1) { + throw std::runtime_error("ConvReluLayer: Input tensors not 1"); + } + if (input[0].get_shape().dims() != 4) { + throw std::out_of_range("input must be 4-dimensional"); + } + + ParBackend backend = options.par_backend; + + if (group_ > 1) { + if (group_ == input[0].get_shape()[1] && + group_ == kernel_->get_shape()[0]) { + switch (input[0].get_type()) { + case Type::kFloat: + DepthwiseConv4D(input[0], *kernel_, *bias_, output[0], stride_, + pads_, dilations_, backend); + relu(output[0]); + break; + case Type::kInt: + DepthwiseConv4D(input[0], *kernel_, *bias_, output[0], stride_, + pads_, dilations_, backend); + relu(output[0]); + break; + default: + throw std::runtime_error( + "Unsupported type for depthwise convolution"); + } + return; + } + } + + switch (input[0].get_type()) { + case Type::kInt: { + if (kernel_->get_shape().dims() == 2) { + if (dilations_ > 0) { + dilations_--; + } + ConvImpl used_impl( + stride_, pads_, dilations_, + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 3]), + input[0].get_shape()[input[0].get_shape().dims() - 1] * + input[0].get_shape()[input[0].get_shape().dims() - 2], + bias_->empty() ? std::vector() : *bias_->as()); + auto sizeforshape = static_cast( + ((static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) - + 1 - + static_cast( + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - 1)) / + static_cast(stride_)) + + 1); + + Shape sh({1, 3, sizeforshape, sizeforshape}); + output[0] = make_tensor( + used_impl.run( + *input[0].as(), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) + + 2 * static_cast(pads_), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]) + + 2 * static_cast(pads_), + *kernel_->as(), + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + static_cast( + ((1 + + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - + 1) / + 2)), + sh); + } else { + Conv4D(input[0], *kernel_, *bias_, output[0], stride_, pads_, + group_, dilations_, backend); + } + relu(output[0]); + break; + } + case Type::kFloat: { + if (kernel_->get_shape().dims() == 2) { + if (dilations_ > 0) { + dilations_--; + } + ConvImpl used_impl( + stride_, pads_, dilations_, + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 3]), + input[0].get_shape()[input[0].get_shape().dims() - 1] * + input[0].get_shape()[input[0].get_shape().dims() - 2], + bias_->empty() ? std::vector() : *bias_->as()); + auto sizeforshape = static_cast( + ((static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) - + 1 - + static_cast( + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - 1)) / + static_cast(stride_)) + + 1); + + Shape sh({1, 3, sizeforshape, sizeforshape}); + output[0] = make_tensor( + used_impl.run( + *input[0].as(), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) + + 2 * static_cast(pads_), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]) + + 2 * static_cast(pads_), + *kernel_->as(), + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + static_cast( + ((1 + + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - + 1) / + 2)), + sh); + } else { + if (useLegacyImpl_) { + Conv4D_Legacy(input[0], *kernel_, *bias_, output[0], stride_, + pads_, dilations_, backend); + } else { + Conv4D(input[0], *kernel_, *bias_, output[0], stride_, pads_, + group_, dilations_, backend); + } + } + relu(output[0]); + break; + } + default: { + throw std::runtime_error("Unsupported tensor type"); + } + } +} + +} // namespace it_lab_ai diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6e776981..142773ef3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,7 @@ file(GLOB_RECURSE TEST_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) add_executable(run_test ${TEST_SRC_FILES}) target_link_libraries(run_test PUBLIC OpenMP::OpenMP_CXX) -target_link_libraries(run_test PUBLIC perf_lib layers_lib layers_oneDNN_lib) +target_link_libraries(run_test PUBLIC perf_lib layers_lib layers_oneDNN_lib layers_fused_lib) target_link_libraries(run_test PUBLIC gtest) target_link_libraries(run_test PUBLIC ReadLib) target_link_libraries(run_test PUBLIC reader_lib) diff --git a/test/single_layer_fused/test_convrelu.cpp b/test/single_layer_fused/test_convrelu.cpp new file mode 100644 index 000000000..144c8f437 --- /dev/null +++ b/test/single_layer_fused/test_convrelu.cpp @@ -0,0 +1,707 @@ +#include + +#include "layers_fused/ConvRelu.hpp" +#include "layers/EWLayer.hpp" + +using namespace it_lab_ai; + +TEST(ConvReluLayerTest, IncompatibleInput) { + int step = 2; + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape sh2({3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + ConvReluLayer layer(step, 0, 1, kernel); + std::vector vec = {1, 2, 3, 4}; + Tensor input1 = make_tensor(vec, {4}); + Tensor input2 = make_tensor(vec, {2, 2}); + std::vector in{input1, input2}; + std::vector output{input1}; + EXPECT_THROW(layer.run(in, output), std::runtime_error); +} + +TEST(ConvReluLayerTest, FStep2) { + std::vector image; + image.reserve(75); + for (int i = 0; i < 75; ++i) { + image.push_back(1); + } + Shape sh1({1, 3, 5, 5}); + Tensor input = make_tensor(image, sh1); + int step = 2; + std::vector kernelvec; + kernelvec.reserve(3 * 3 * 3 * 3); + for (int i = 0; i < 81; ++i) { + kernelvec.push_back((i % 9) % 2 == 0 ? 1.0f : 0.0f); + } + Shape sh2({3, 3, 3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + size_t out_height = (5 + 2 * 0 - 1 * (3 - 1) - 1) / 2 + 1; + size_t out_width = (5 + 2 * 0 - 1 * (3 - 1) - 1) / 2 + 1; + size_t expected_size = 1 * 3 * out_height * out_width; + std::vector expected_output(expected_size, 15.0f); + Shape output_shape({1, 3, out_height, out_width}); + std::vector output_vec(expected_size, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + ConvReluLayer layer(step, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); + for (size_t i = 0; i < tmp.size(); ++i) { + ASSERT_FLOAT_EQ(tmp[i], expected_output[i]); + } +} +TEST(ConvReluLayerTest, FStep1) { + std::vector image; + image.reserve(75); + for (int i = 0; i < 75; ++i) { + image.push_back(1); + } + Shape sh1({1, 3, 5, 5}); + Tensor input = make_tensor(image, sh1); + int step = 1; + std::vector kernelvec; + kernelvec.reserve(3 * 3 * 3 * 3); + for (int i = 0; i < 81; ++i) { + kernelvec.push_back((i % 9) % 2 == 0 ? 1.0f : 0.0f); + } + Shape sh2({3, 3, 3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + size_t out_height = (5 + 2 * 0 - 1 * (3 - 1) - 1) / 1 + 1; + size_t out_width = (5 + 2 * 0 - 1 * (3 - 1) - 1) / 1 + 1; + size_t expected_size = 1 * 3 * out_height * out_width; + std::vector expected_output(expected_size, 15.0f); + Shape output_shape({1, 3, out_height, out_width}); + std::vector output_vec(expected_size, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + ConvReluLayer layer(step, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); + for (size_t i = 0; i < tmp.size(); ++i) { + ASSERT_FLOAT_EQ(tmp[i], expected_output[i]); + } +} +TEST(ConvReluLayerTest, IntStep2) { + std::vector image; + image.reserve(75); + for (int i = 0; i < 75; ++i) { + image.push_back(1); + } + Shape sh({2, 2}); + std::vector vec = {1, 2, 3, 4}; + Shape sh1({1, 3, 5, 5}); + Tensor input = make_tensor(image, sh1); + Tensor output = make_tensor(vec, sh); + int step = 2; + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + std::vector expected_output(12, 5); + Shape sh2({3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + ConvReluLayer layer(step, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); + for (size_t i = 0; i < tmp.size(); ++i) { + ASSERT_EQ(tmp[i], expected_output[i]); + } +} +TEST(ConvReluLayerTest, IntStep1) { + std::vector image; + image.reserve(75); + for (int i = 0; i < 75; ++i) { + image.push_back(1); + } + Shape sh({2, 2}); + std::vector vec = {1, 2, 3, 4}; + Shape sh1({1, 3, 5, 5}); + Tensor input = make_tensor(image, sh1); + Tensor output = make_tensor(vec, sh); + int step = 1; + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + std::vector expected_output(27, 5); + Shape sh2({3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + ConvReluLayer layer(step, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); + for (size_t i = 0; i < tmp.size(); ++i) { + ASSERT_EQ(tmp[i], expected_output[i]); + } +} +TEST(ConvReluLayerTest, FloatWithBias) { + std::vector image(75, 1.0f); + Shape input_shape({1, 3, 5, 5}); + Tensor input = make_tensor(image, input_shape); + std::vector kernelvec; + kernelvec.reserve(3 * 3 * 3 * 3); + for (int i = 0; i < 81; ++i) { + kernelvec.push_back((i % 9) % 2 == 0 ? 1.0f : 0.0f); + } + Shape kernel_shape({3, 3, 3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + std::vector biasvec = {0.5f, 0.5f, 0.5f}; + Tensor bias = make_tensor(biasvec, Shape({3})); + size_t out_height = 3; + size_t out_width = 3; + size_t expected_size = 1 * 3 * out_height * out_width; + Shape output_shape({1, 3, out_height, out_width}); + std::vector output_vec(expected_size, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + std::vector expected_output(expected_size, 15.5f); + ConvReluLayer layer(1, 0, 1, kernel, bias); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); + for (size_t i = 0; i < tmp.size(); ++i) { + ASSERT_FLOAT_EQ(tmp[i], expected_output[i]); + } +} +TEST(ConvReluLayerTest, InvalidInputShapeDims) { + std::vector image(15, 1.0f); + Shape invalid_shape({1, 3, 5}); + Tensor input = make_tensor(image, invalid_shape); + + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + Shape output_shape({1, 3, 3, 3}); + std::vector output_vec(27, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel); + + std::vector in{input}; + std::vector out{output}; + + EXPECT_THROW(layer.run(in, out), std::out_of_range); +} +TEST(ConvReluLayerTest, Conv4DKern) { + std::vector image; + image.reserve(75); + for (int i = 0; i < 75; ++i) { + image.push_back(1); + } + Shape sh1({1, 3, 5, 5}); + Tensor input = make_tensor(image, sh1); + int step = 1; + std::vector kernelvec; + kernelvec.reserve(54); + for (int i = 0; i < 54; ++i) { + kernelvec.push_back(1); + } + Shape sh2({2, 3, 3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + size_t out_height = (5 + 2 * 1 - 1 * (3 - 1) - 1) / 1 + 1; + size_t out_width = (5 + 2 * 1 - 1 * (3 - 1) - 1) / 1 + 1; + size_t expected_size = 1 * 2 * out_height * out_width; + std::vector expected_output(expected_size, 9); + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(expected_size, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + ConvReluLayer layer(step, 1, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); +} +TEST(ConvReluLayerTest, Conv4DKern_int) { + std::vector image; + image.reserve(784); + for (int i = 0; i < 784; ++i) { + image.push_back(1); + } + Shape sh1({1, 1, 28, 28}); + Tensor input = make_tensor(image, sh1); + + int step = 1; + std::vector kernelvec; + kernelvec.reserve(400); + for (int i = 0; i < 400; ++i) { + kernelvec.push_back(1); + } + Shape sh2({16, 1, 5, 5}); + Tensor kernel = make_tensor(kernelvec, sh2); + size_t out_height = (28 + 2 * 0 - 1 * (5 - 1) - 1) / 1 + 1; + size_t out_width = (28 + 2 * 0 - 1 * (5 - 1) - 1) / 1 + 1; + size_t expected_size = 1 * 16 * out_height * out_width; + std::vector expected_output(expected_size, 25); + Shape output_shape({1, 16, out_height, out_width}); + std::vector output_vec(expected_size, 0); + Tensor output = make_tensor(output_vec, output_shape); + ConvReluLayer layer(step, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); + for (size_t i = 0; i < tmp.size(); ++i) { + ASSERT_EQ(tmp[i], expected_output[i]); + } +} +TEST(ConvReluLayerTest, Conv4DKern_int_36) { + std::vector image; + image.reserve(16 * 784); + for (int i = 0; i < 16 * 784; ++i) { + image.push_back(1); + } + Shape sh1({1, 16, 28, 28}); + Tensor input = make_tensor(image, sh1); + int step = 1; + std::vector kernelvec; + kernelvec.reserve(5 * 5 * 16 * 36); + for (int i = 0; i < 5 * 5 * 16 * 36; ++i) { + kernelvec.push_back(1); + } + Shape sh2({36, 16, 5, 5}); + Tensor kernel = make_tensor(kernelvec, sh2); + size_t pads = (kernel.get_shape()[2] - 1) / 2; + size_t out_height = (28 + 2 * pads - 1 * (5 - 1) - 1) / 1 + 1; + size_t out_width = (28 + 2 * pads - 1 * (5 - 1) - 1) / 1 + 1; + size_t expected_size = 1 * 36 * out_height * out_width; + std::vector expected_output(expected_size, 5 * 5 * 16); + Shape output_shape({1, 36, out_height, out_width}); + std::vector output_vec(expected_size, 0); + Tensor output = make_tensor(output_vec, output_shape); + ConvReluLayer layer(step, pads, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector tmp = *out[0].as(); + ASSERT_EQ(tmp.size(), expected_output.size()); +} + +TEST(ConvReluLayerTest, DepthwiseIntegration) { + std::vector image(32, 1.0f); + Shape input_shape({1, 2, 4, 4}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec(18, 1.0f); + Shape kernel_shape({2, 1, 3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + Tensor bias; + + size_t out_height = (4 + 2 * 1 - 1 * (3 - 1) - 1) / 1 + 1; + size_t out_width = (4 + 2 * 1 - 1 * (3 - 1) - 1) / 1 + 1; + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(32, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 1, 1, kernel, bias, 2); + std::vector in{input}; + std::vector out{output}; + + EXPECT_NO_THROW(layer.run(in, out)); + + std::vector result = *out[0].as(); + ASSERT_EQ(result.size(), 32); +} + +TEST(ConvReluLayerTest, DepthwiseViaConvolutionalLayer) { + std::vector image(32, -1.0f); + Shape input_shape({1, 2, 4, 4}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec(18, 1.0f); + Shape kernel_shape({2, 1, 3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + Tensor bias; + + Shape output_shape({1, 2, 2, 2}); + std::vector output_vec(8, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel, bias, 2); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + + std::vector result = *out[0].as(); + + float expected_value = 0.0f; + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_NEAR(result[i], expected_value, 1e-5f); + } +} + +TEST(ConvReluLayerTest, Conv4DLegacyViaConvolutionalLayer) { + std::vector image(48, 1.0f); + Shape input_shape({1, 3, 4, 4}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec(54, 1.0f); + Shape kernel_shape({3, 3, 3, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + Tensor bias; + + size_t out_height = (4 + 2 * 0 - 1 * (3 - 1) - 1) / 1 + 1; + size_t out_width = (4 + 2 * 0 - 1 * (3 - 1) - 1) / 1 + 1; + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(8, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel, bias, 1, true); + std::vector in{input}; + std::vector out{output}; + + layer.run(in, out); + + std::vector result = *out[0].as(); + + ASSERT_EQ(result.size(), 8); + float expected_value = 27.0f; + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_NEAR(result[i], expected_value, 1e-5f); + } +} + +TEST(ConvReluLayerTest, DepthwiseConv4DIntPathCoverage) { + std::vector image = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + Shape input_shape({1, 2, 2, 4}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 1, 1, 1, 2, 2, 2, 2}; + Shape kernel_shape({2, 1, 2, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector biasvec = {10, 20}; + Tensor bias = make_tensor(biasvec, Shape({2})); + + size_t out_height = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + size_t out_width = (4 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(6, 0); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel, bias, 2); + std::vector in{input}; + std::vector out{output}; + + EXPECT_NO_THROW(layer.run(in, out)); + + std::vector result = *out[0].as(); + EXPECT_FALSE(result.empty()); +} + +TEST(ConvReluLayerTest, DepthwiseConv4DFloatPathCoverage) { + std::vector image = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f}; + Shape input_shape({1, 2, 2, 2}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.5f}; + Shape kernel_shape({2, 1, 2, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector biasvec = {0.1f, 0.2f}; + Tensor bias = make_tensor(biasvec, Shape({2})); + + size_t out_height = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + size_t out_width = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(2, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel, bias, 2); + std::vector in{input}; + std::vector out{output}; + + EXPECT_NO_THROW(layer.run(in, out)); + + std::vector result = *out[0].as(); + EXPECT_FALSE(result.empty()); +} + +TEST(ConvReluLayerTest, DepthwiseConv4DNoBiasIntPathCoverage) { + std::vector image = {1, 2, 3, 4, 5, 6, 7, 8}; + Shape input_shape({1, 2, 2, 2}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 1, 1, 1, 2, 2, 2, 2}; + Shape kernel_shape({2, 1, 2, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + Tensor bias; + + size_t out_height = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + size_t out_width = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(2, 0); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel, bias, 2); + std::vector in{input}; + std::vector out{output}; + + EXPECT_NO_THROW(layer.run(in, out)); + + std::vector result = *out[0].as(); + EXPECT_FALSE(result.empty()); +} + +TEST(ConvReluLayerTest, DepthwiseConv4DNoBiasFloatPathCoverage) { + std::vector image = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f}; + Shape input_shape({1, 2, 2, 2}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.5f}; + Shape kernel_shape({2, 1, 2, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + Tensor bias; + + size_t out_height = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + size_t out_width = (2 + 2 * 0 - 1 * (2 - 1) - 1) / 1 + 1; + Shape output_shape({1, 2, out_height, out_width}); + std::vector output_vec(2, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel, bias, 2); + std::vector in{input}; + std::vector out{output}; + + EXPECT_NO_THROW(layer.run(in, out)); + + std::vector result = *out[0].as(); + EXPECT_FALSE(result.empty()); +} + +TEST(ConvReluLayerTest, ConvImplInt2DKernel) { + std::vector image(75, -1); + Shape input_shape({1, 3, 5, 5}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector output_vec(27, 0); + Tensor output = make_tensor(output_vec, Shape({1, 3, 3, 3})); + + ConvReluLayer layer(1, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out); + std::vector result = *out[0].as(); + ASSERT_EQ(result.size(), 27); + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_EQ(result[i], 0); + } +} +TEST(ConvReluLayerTest, ConvImplInt2DKernelBasic) { + std::vector image(75, 1); + Shape input_shape({1, 3, 5, 5}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector output_vec(27, 0); + Tensor output = make_tensor(output_vec, Shape({1, 3, 3, 3})); + + ConvReluLayer layer(1, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + + layer.run(in, out); + + std::vector result = *out[0].as(); + + ASSERT_EQ(result.size(), 27); + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_EQ(result[i], 5); + } +} + +TEST(ConvReluLayerTest, ConvImplInt2DKernelWithStride) { + std::vector image(75, 1); + Shape input_shape({1, 3, 5, 5}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector output_vec(12, 0); + Tensor output = make_tensor(output_vec, Shape({1, 3, 2, 2})); + + ConvReluLayer layer(2, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + + layer.run(in, out); + + std::vector result = *out[0].as(); + + ASSERT_EQ(result.size(), 12); + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_EQ(result[i], 5); + } +} + +TEST(ConvReluLayerTest, ConvImplInt2DKernelWithBias) { + std::vector image(75, 1); + Shape input_shape({1, 3, 5, 5}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector biasvec = {1, 1, 1}; + Tensor bias = make_tensor(biasvec, Shape({3})); + std::vector output_vec(27, 0); + Tensor output = make_tensor(output_vec, Shape({1, 3, 3, 3})); + + ConvReluLayer layer(1, 0, 1, kernel, bias); + std::vector in{input}; + std::vector out{output}; + + layer.run(in, out); + + std::vector result = *out[0].as(); + + ASSERT_EQ(result.size(), 27); + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_EQ(result[i], 6); + } +} + +TEST(ConvReluLayerTest, ConvImplInt2DKernelSmallInput) { + std::vector image(27, 2); + Shape input_shape({1, 3, 3, 3}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 1, 1, 1, 1, 1, 1, 1, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + std::vector output_vec(3, 0); + Tensor output = make_tensor(output_vec, Shape({1, 3, 1, 1})); + + ConvReluLayer layer(1, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + + layer.run(in, out); + + std::vector result = *out[0].as(); + + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_EQ(result[i], 18); + } +} + +TEST(ConvReluLayerTest, ConvImplInt2DKernelComplexPattern) { + std::vector image = {1, 2, 1, 2, 3, 4, 3, 4, 1, 2, 1, 2, 3, 4, 3, 4, + + 2, 3, 2, 3, 4, 5, 4, 5, 2, 3, 2, 3, 4, 5, 4, 5, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + Shape input_shape({1, 3, 4, 4}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1, 1, 1, 1, 1, 1, 1, 1, 1}; + Shape kernel_shape({3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector output_vec(12, 0); + Tensor output = make_tensor(output_vec, Shape({1, 3, 2, 2})); + + ConvReluLayer layer(1, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + + layer.run(in, out); + + std::vector result = *out[0].as(); + + ASSERT_EQ(result.size(), 12); + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_GT(result[i], 0); + } +} + +TEST(ConvReluLayerTest, Float2DKernelPathCoverage) { + std::vector image = {1.0f, 2.0f, 3.0f, 4.0f}; + Shape input_shape({1, 1, 2, 2}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1.0f, 0.0f, 1.0f, 0.0f}; + Shape kernel_shape({2, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector output_vec(1, 0.0f); + Tensor output = make_tensor(output_vec, Shape({1, 1, 1, 1})); + + ConvReluLayer layer(1, 0, 0, kernel); + std::vector in{input}; + std::vector out{output}; + + EXPECT_THROW(layer.run(in, out), std::exception); +} + +TEST(ConvReluLayerTest, Float4DKernelWorking) { + std::vector image = {1.0f, 2.0f, 3.0f, 4.0f}; + Shape input_shape({1, 1, 2, 2}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec = {1.0f, 0.0f, 1.0f, 0.0f}; + Shape kernel_shape({1, 1, 2, 2}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + std::vector output_vec(1, 0.0f); + Tensor output = make_tensor(output_vec, Shape({1, 1, 1, 1})); + + ConvReluLayer layer(1, 0, 0, kernel); + std::vector in{input}; + std::vector out{output}; + + EXPECT_NO_THROW(layer.run(in, out)); + + std::vector result = *out[0].as(); + ASSERT_EQ(result.size(), 4); +} + +TEST(ConvReluLayerTest, Conv4DWithParallelDefaultFallback) { + RuntimeOptions options; + options.backend = Backend::kNaive; + + std::vector image(48, 1.0f); + Shape input_shape({1, 3, 4, 4}); + Tensor input = make_tensor(image, input_shape); + + std::vector kernelvec(54, 1.0f); + Shape kernel_shape({2, 3, 3, 3}); + Tensor kernel = make_tensor(kernelvec, kernel_shape); + + Shape output_shape({1, 2, 2, 2}); + std::vector output_vec(8, 0.0f); + Tensor output = make_tensor(output_vec, output_shape); + + ConvReluLayer layer(1, 0, 1, kernel); + std::vector in{input}; + std::vector out{output}; + layer.run(in, out, options); + + std::vector result = *out[0].as(); + + float expected_value = 27.0f; + for (size_t i = 0; i < result.size(); ++i) { + ASSERT_NEAR(result[i], expected_value, 1e-5f); + } +} From 4bdbcde041c8161d95470b85a75c076e3ac2c73d Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 27 Feb 2026 19:04:49 +0300 Subject: [PATCH 02/14] Fix --- src/layers_fused/ConvRelu.cpp | 152 +--------------------- test/single_layer_fused/test_convrelu.cpp | 2 +- 2 files changed, 6 insertions(+), 148 deletions(-) diff --git a/src/layers_fused/ConvRelu.cpp b/src/layers_fused/ConvRelu.cpp index 4cd6db94d..a5afaf042 100644 --- a/src/layers_fused/ConvRelu.cpp +++ b/src/layers_fused/ConvRelu.cpp @@ -10,159 +10,17 @@ void ConvReluLayer::run(const std::vector& input, } void ConvReluLayer::run(const std::vector& input, - std::vector& output, - const RuntimeOptions& options) { - if (kernel_ == nullptr || bias_ == nullptr) { - throw std::runtime_error("ConvReluLayer: no weights or bias"); - } - if (input.size() != 1) { - throw std::runtime_error("ConvReluLayer: Input tensors not 1"); - } - if (input[0].get_shape().dims() != 4) { - throw std::out_of_range("input must be 4-dimensional"); - } - - ParBackend backend = options.par_backend; - - if (group_ > 1) { - if (group_ == input[0].get_shape()[1] && - group_ == kernel_->get_shape()[0]) { - switch (input[0].get_type()) { - case Type::kFloat: - DepthwiseConv4D(input[0], *kernel_, *bias_, output[0], stride_, - pads_, dilations_, backend); - relu(output[0]); - break; - case Type::kInt: - DepthwiseConv4D(input[0], *kernel_, *bias_, output[0], stride_, - pads_, dilations_, backend); - relu(output[0]); - break; - default: - throw std::runtime_error( - "Unsupported type for depthwise convolution"); - } - return; - } - } - + std::vector& output, + const RuntimeOptions& options) { + ConvolutionalLayer conv(stride_, pads_, dilations_, kernel_, bias_, group_, + useLegacyImpl_); + conv.run(input, output, options); switch (input[0].get_type()) { case Type::kInt: { - if (kernel_->get_shape().dims() == 2) { - if (dilations_ > 0) { - dilations_--; - } - ConvImpl used_impl( - stride_, pads_, dilations_, - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 1]), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 2]), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 3]), - input[0].get_shape()[input[0].get_shape().dims() - 1] * - input[0].get_shape()[input[0].get_shape().dims() - 2], - bias_->empty() ? std::vector() : *bias_->as()); - auto sizeforshape = static_cast( - ((static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 1]) - - 1 - - static_cast( - (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * - dilations_ + - kernel_->get_shape()[kernel_->get_shape().dims() - 1] - 1)) / - static_cast(stride_)) + - 1); - - Shape sh({1, 3, sizeforshape, sizeforshape}); - output[0] = make_tensor( - used_impl.run( - *input[0].as(), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 1]) + - 2 * static_cast(pads_), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 2]) + - 2 * static_cast(pads_), - *kernel_->as(), - kernel_->get_shape()[kernel_->get_shape().dims() - 1], - (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * - dilations_ + - kernel_->get_shape()[kernel_->get_shape().dims() - 1], - static_cast( - ((1 + - kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * - dilations_ + - kernel_->get_shape()[kernel_->get_shape().dims() - 1] - - 1) / - 2)), - sh); - } else { - Conv4D(input[0], *kernel_, *bias_, output[0], stride_, pads_, - group_, dilations_, backend); - } relu(output[0]); break; } case Type::kFloat: { - if (kernel_->get_shape().dims() == 2) { - if (dilations_ > 0) { - dilations_--; - } - ConvImpl used_impl( - stride_, pads_, dilations_, - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 1]), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 2]), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 3]), - input[0].get_shape()[input[0].get_shape().dims() - 1] * - input[0].get_shape()[input[0].get_shape().dims() - 2], - bias_->empty() ? std::vector() : *bias_->as()); - auto sizeforshape = static_cast( - ((static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 1]) - - 1 - - static_cast( - (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * - dilations_ + - kernel_->get_shape()[kernel_->get_shape().dims() - 1] - 1)) / - static_cast(stride_)) + - 1); - - Shape sh({1, 3, sizeforshape, sizeforshape}); - output[0] = make_tensor( - used_impl.run( - *input[0].as(), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 1]) + - 2 * static_cast(pads_), - static_cast( - input[0].get_shape()[input[0].get_shape().dims() - 2]) + - 2 * static_cast(pads_), - *kernel_->as(), - kernel_->get_shape()[kernel_->get_shape().dims() - 1], - (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * - dilations_ + - kernel_->get_shape()[kernel_->get_shape().dims() - 1], - static_cast( - ((1 + - kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * - dilations_ + - kernel_->get_shape()[kernel_->get_shape().dims() - 1] - - 1) / - 2)), - sh); - } else { - if (useLegacyImpl_) { - Conv4D_Legacy(input[0], *kernel_, *bias_, output[0], stride_, - pads_, dilations_, backend); - } else { - Conv4D(input[0], *kernel_, *bias_, output[0], stride_, pads_, - group_, dilations_, backend); - } - } relu(output[0]); break; } diff --git a/test/single_layer_fused/test_convrelu.cpp b/test/single_layer_fused/test_convrelu.cpp index 144c8f437..d5e8198af 100644 --- a/test/single_layer_fused/test_convrelu.cpp +++ b/test/single_layer_fused/test_convrelu.cpp @@ -1,7 +1,7 @@ #include -#include "layers_fused/ConvRelu.hpp" #include "layers/EWLayer.hpp" +#include "layers_fused/ConvRelu.hpp" using namespace it_lab_ai; From d0e2d9f63b3b5d19c69b03088d24dc02e0f43504 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 27 Feb 2026 19:09:35 +0300 Subject: [PATCH 03/14] Clang --- src/layers_fused/ConvRelu.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layers_fused/ConvRelu.cpp b/src/layers_fused/ConvRelu.cpp index a5afaf042..76b359497 100644 --- a/src/layers_fused/ConvRelu.cpp +++ b/src/layers_fused/ConvRelu.cpp @@ -1,4 +1,5 @@ #include "layers_fused/ConvRelu.hpp" + #include "layers/ConvLayer.hpp" namespace it_lab_ai { From e305545ab67cbb722feb24f5aad9bf5412b9d2ae Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 27 Feb 2026 19:37:54 +0300 Subject: [PATCH 04/14] Clang again --- include/layers/Tensor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/layers/Tensor.hpp b/include/layers/Tensor.hpp index 783d0c9fb..c39c7562e 100644 --- a/include/layers/Tensor.hpp +++ b/include/layers/Tensor.hpp @@ -170,7 +170,7 @@ std::vector* Tensor::as() { } template -const std::vector* Tensor::as() const { +[[nodiscard]] const std::vector* Tensor::as() const { if (GetTypeEnum() != type_) { throw std::invalid_argument("Template type doesn't fit this Tensor"); } From 0ff9504759f27a108c8917dc3e5f27e53685e51f Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 16:07:53 +0300 Subject: [PATCH 05/14] Changes --- app/Graph/CMakeLists.txt | 2 + app/Graph/build.cpp | 4 +- app/Graph/build.hpp | 3 +- app/Graph/onnx_subgraphs.cpp | 56 +++++++++++++++++++ include/graph/graph.hpp | 26 ++++++++- .../graph_transformations.hpp | 2 + include/layers/ConvLayer.hpp | 13 +++++ include/layers_fused/ConvRelu.hpp | 15 ++++- .../graph_transformations.cpp | 10 +++- 9 files changed, 125 insertions(+), 6 deletions(-) diff --git a/app/Graph/CMakeLists.txt b/app/Graph/CMakeLists.txt index 66612643a..218e511bf 100644 --- a/app/Graph/CMakeLists.txt +++ b/app/Graph/CMakeLists.txt @@ -11,6 +11,7 @@ target_link_libraries(BuildGraph PUBLIC ${OpenCV_LIBS}) target_link_libraries(BuildGraph PUBLIC reader_lib) target_link_libraries(BuildGraph PUBLIC TBB_unified) target_link_libraries(BuildGraph PUBLIC layers_lib) +target_link_libraries(BuildGraph PUBLIC layers_fused_lib) target_link_libraries(BuildGraph PUBLIC layers_oneDNN_lib) target_link_libraries(BuildGraph PUBLIC gtest_main) @@ -25,6 +26,7 @@ target_link_libraries(ACC BuildGraph) add_executable(onnx_subgraphs onnx_subgraphs.cpp) target_link_libraries(onnx_subgraphs BuildGraph) target_link_libraries(onnx_subgraphs OpenMP::OpenMP_CXX) +target_link_libraries(onnx_subgraphs ${OpenCV_LIBS}) target_link_libraries(onnx_subgraphs graphT_lib) file(DOWNLOAD diff --git a/app/Graph/build.cpp b/app/Graph/build.cpp index 381e8fcd5..e17c56e12 100644 --- a/app/Graph/build.cpp +++ b/app/Graph/build.cpp @@ -13,7 +13,7 @@ std::unordered_map model_paths = { void build_graph_linear(it_lab_ai::Graph& graph, it_lab_ai::Tensor& input, it_lab_ai::Tensor& output, RuntimeOptions options, - bool comments) { + bool comments, bool enable_postops) { if (comments) { for (size_t i = 0; i < input.get_shape().dims(); i++) { std::cout << input.get_shape()[i] << ' '; @@ -75,7 +75,7 @@ void build_graph_linear(it_lab_ai::Graph& graph, it_lab_ai::Tensor& input, if (layer_type.find("relu") != std::string::npos) { auto ew_layer = LayerFactory::createEwLayer("relu", options); layers.push_back(ew_layer); - layerpostop.push_back(true); + layerpostop.push_back(enable_postops); if (comments) { std::cout << "Element wise (relu) added to layers" << '\n'; } diff --git a/app/Graph/build.hpp b/app/Graph/build.hpp index 5628e9d19..5d98c4bc8 100644 --- a/app/Graph/build.hpp +++ b/app/Graph/build.hpp @@ -61,7 +61,8 @@ void build_graph(it_lab_ai::Graph& graph, it_lab_ai::Tensor& input, it_lab_ai::RuntimeOptions options, bool comments); void build_graph_linear(it_lab_ai::Graph& graph, it_lab_ai::Tensor& input, it_lab_ai::Tensor& output, - it_lab_ai::RuntimeOptions options, bool comments); + it_lab_ai::RuntimeOptions options, bool comments, + bool enable_postops = true); std::unordered_map load_class_names( const std::string& filename); diff --git a/app/Graph/onnx_subgraphs.cpp b/app/Graph/onnx_subgraphs.cpp index 1647206f5..d4106d867 100644 --- a/app/Graph/onnx_subgraphs.cpp +++ b/app/Graph/onnx_subgraphs.cpp @@ -1,20 +1,76 @@ #include #include #include +#include #include +#include #include #include #include "build.hpp" #include "graph_transformations/graph_transformations.hpp" #include "perf/benchmarking.hpp" +#include "layers_fused/ConvRelu.hpp" using namespace it_lab_ai; +void alexnet_inf_careless(Graph& graph, const RuntimeOptions& options, Tensor& input, Tensor& output) { + Tensor* o = new Tensor(output); + Tensor* i = new Tensor(input); + graph.inference(options); + graph.setOutput(*o); + graph.setInput(*i); +} + +void alexnet_comparison() { + + std::vector counts = {979, 1134, 1031, 1009, 981, + 891, 957, 1027, 973, 1008}; + int stat = 0; + size_t sum = std::accumulate(counts.begin(), counts.end(), size_t{0}); + int count_pic = static_cast(sum) + 10; + std::vector res(count_pic * 28 * 28, 1.0f); + Tensor input; + Shape sh1({1, 5, 5, 3}); + std::vector vec; + vec.reserve(75); + for (int i = 0; i < 75; ++i) { + vec.push_back(3); + } + Tensor output = make_tensor(vec, sh1); + + Shape sh({static_cast(count_pic), 1, 28, 28}); + Tensor t = make_tensor(res, sh); + input = t; + + RuntimeOptions options; + Graph graph1; + Graph graph2; + build_graph_linear(graph1, input, output, options, true); + Graph subgraph; + std::shared_ptr layer_0 = std::make_shared(); + std::shared_ptr layer_1 = std::make_shared("relu"); + subgraph.setInput(layer_0, input); + subgraph.makeConnection(layer_0, layer_1); + std::shared_ptr layer_to = std::make_shared( + std::dynamic_pointer_cast(layer_0)); + changed_subgraphs(graph1, subgraph, layer_to, graph2, input, options); + Tensor input_c = input; + Tensor output_c = output; + double time1 = elapsed_time_avg( + 2, alexnet_inf_careless, graph1, options, input_c, output_c); + double time2 = elapsed_time_avg( + 2, alexnet_inf_careless, graph2, options, input_c, output_c); + std::cout << time1 << " for unchanged graph\n"; + std::cout << time2 << " for convrelu graph\n"; +} + + int main() { int type = 2; Tensor input = make_tensor(std::vector({0})); RuntimeOptions options; + alexnet_comparison(); if (type == 0) { Graph graph1; build_graph(graph1, input, input, MODEL_PATH_DENSENET_ONNX, options, false); diff --git a/include/graph/graph.hpp b/include/graph/graph.hpp index 4743d9968..758ecc73f 100644 --- a/include/graph/graph.hpp +++ b/include/graph/graph.hpp @@ -31,7 +31,8 @@ static std::unordered_map label_map = { {kReshape, "Reshape"}, {kSoftmax, "Softmax"}, {kReduce, "Reduce"}, - {kBatchNormalization, "Normalization"}}; + {kBatchNormalization, "Normalization"}, + {kConvRelu, "ConvRelu"}}; struct LayerTimeStats { std::string layer_name; @@ -175,6 +176,16 @@ class Graph { start_ = layer->getID(); } + void setInput(Tensor& vec) { + if (layers_.empty()) { + throw std::invalid_argument("No layers in graph"); + } + int id = layers_.front()->getID(); + + inten_ = {vec}; + start_ = id; + } + void addSingleLayer(const std::shared_ptr& layer) { if (!layer) return; @@ -448,6 +459,19 @@ class Graph { } } + void setOutput(Tensor& vec) { + if (layers_.empty()) { + throw std::invalid_argument("No layers in graph"); + } + end_ = layers_.back()->getID(); + outtenres_ = &vec; + if (outten_.empty()) { + std::vector vec1 = {1, 7, 1, 0}; + Tensor start = make_tensor(vec1); + outten_.push_back(start); + } + } + #ifdef ENABLE_STATISTIC_TENSORS std::vector getTensors() { return tensors_; } #endif diff --git a/include/graph_transformations/graph_transformations.hpp b/include/graph_transformations/graph_transformations.hpp index f0adf6fdd..16178364e 100644 --- a/include/graph_transformations/graph_transformations.hpp +++ b/include/graph_transformations/graph_transformations.hpp @@ -4,6 +4,8 @@ #include "graph/graph.hpp" #include "layers/EWLayer.hpp" #include "layers/Layer.hpp" +#include "layers/ConvLayer.hpp" +#include "layers_fused/ConvRelu.hpp" namespace it_lab_ai { diff --git a/include/layers/ConvLayer.hpp b/include/layers/ConvLayer.hpp index 6087c5e4c..cdc0c8576 100644 --- a/include/layers/ConvLayer.hpp +++ b/include/layers/ConvLayer.hpp @@ -58,6 +58,19 @@ class ConvolutionalLayer : public Layer { dilations_ = dilations; useLegacyImpl_ = useLegacyImpl; } + + std::vector get_numeric_params() { + std::vector res = {stride_, pads_, dilations_, group_}; + return res; + } + + std::vector> get_tensor_params() { + std::vector> res = {kernel_, bias_}; + return res; + } + + bool getLegacyImplBool() { return useLegacyImpl_; } + void run(const std::vector& input, std::vector& output) override; void run(const std::vector& input, std::vector& output, diff --git a/include/layers_fused/ConvRelu.hpp b/include/layers_fused/ConvRelu.hpp index e1fdb94bc..912fbf52a 100644 --- a/include/layers_fused/ConvRelu.hpp +++ b/include/layers_fused/ConvRelu.hpp @@ -5,6 +5,7 @@ #include #include "layers/Layer.hpp" +#include "layers/ConvLayer.hpp" #include "layers/Tensor.hpp" namespace it_lab_ai { @@ -19,7 +20,7 @@ void relu(Tensor& t) { } } -class ConvReluLayer : Layer { +class ConvReluLayer : public Layer { private: size_t stride_; size_t pads_; @@ -58,6 +59,18 @@ class ConvReluLayer : Layer { dilations_ = dilations; useLegacyImpl_ = useLegacyImpl; } + ConvReluLayer(const std::shared_ptr& conv) + : Layer(kConvRelu) { + auto numerics = conv->get_numeric_params(); + auto tensors = conv->get_tensor_params(); + stride_ = numerics[0]; + pads_ = numerics[1]; + dilations_ = numerics[2]; + group_ = numerics[3]; + kernel_ = tensors[0]; + bias_ = tensors[1]; + useLegacyImpl_ = conv->getLegacyImplBool(); + } void run(const std::vector& input, std::vector& output) override; void run(const std::vector& input, std::vector& output, diff --git a/src/graph_transformations/graph_transformations.cpp b/src/graph_transformations/graph_transformations.cpp index 6d86d5f8f..8d688f441 100644 --- a/src/graph_transformations/graph_transformations.cpp +++ b/src/graph_transformations/graph_transformations.cpp @@ -160,7 +160,15 @@ void changed_subgraphs(const Graph& graph, const Graph& subgraph_from, sub_used[i] = false; continue; } - std::shared_ptr layer = layer_based_shared_copy(layer_to, options); + std::shared_ptr layer; + if (layer_to->getName() == kConvRelu && + graph.getLayerFromID(subs_c[i][0])->getName() == kConvolution) { + layer = std::static_pointer_cast(std::make_shared( + std::dynamic_pointer_cast( + graph.getLayerFromID(subs_c[i][0])))); // convrelu case + } else { + layer = layer_based_shared_copy(layer_to, options); + } std::vector is_root_special(roots.size(), false); roots_inps_final.clear(); leaves_outs_final.clear(); From a0b4d7fd8eb9ac03d6bbb00222edb2f426fc61d6 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 16:13:32 +0300 Subject: [PATCH 06/14] clang --- app/Graph/onnx_subgraphs.cpp | 8 +++----- include/graph_transformations/graph_transformations.hpp | 2 +- include/layers_fused/ConvRelu.hpp | 4 ++-- src/layers_fused/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/Graph/onnx_subgraphs.cpp b/app/Graph/onnx_subgraphs.cpp index d4106d867..31dd17ea4 100644 --- a/app/Graph/onnx_subgraphs.cpp +++ b/app/Graph/onnx_subgraphs.cpp @@ -9,12 +9,13 @@ #include "build.hpp" #include "graph_transformations/graph_transformations.hpp" -#include "perf/benchmarking.hpp" #include "layers_fused/ConvRelu.hpp" +#include "perf/benchmarking.hpp" using namespace it_lab_ai; -void alexnet_inf_careless(Graph& graph, const RuntimeOptions& options, Tensor& input, Tensor& output) { +void alexnet_inf_careless(Graph& graph, const RuntimeOptions& options, + Tensor& input, Tensor& output) { Tensor* o = new Tensor(output); Tensor* i = new Tensor(input); graph.inference(options); @@ -23,10 +24,8 @@ void alexnet_inf_careless(Graph& graph, const RuntimeOptions& options, Tensor& i } void alexnet_comparison() { - std::vector counts = {979, 1134, 1031, 1009, 981, 891, 957, 1027, 973, 1008}; - int stat = 0; size_t sum = std::accumulate(counts.begin(), counts.end(), size_t{0}); int count_pic = static_cast(sum) + 10; std::vector res(count_pic * 28 * 28, 1.0f); @@ -65,7 +64,6 @@ void alexnet_comparison() { std::cout << time2 << " for convrelu graph\n"; } - int main() { int type = 2; Tensor input = make_tensor(std::vector({0})); diff --git a/include/graph_transformations/graph_transformations.hpp b/include/graph_transformations/graph_transformations.hpp index 16178364e..f1dd2ea81 100644 --- a/include/graph_transformations/graph_transformations.hpp +++ b/include/graph_transformations/graph_transformations.hpp @@ -2,9 +2,9 @@ #include #include "graph/graph.hpp" +#include "layers/ConvLayer.hpp" #include "layers/EWLayer.hpp" #include "layers/Layer.hpp" -#include "layers/ConvLayer.hpp" #include "layers_fused/ConvRelu.hpp" namespace it_lab_ai { diff --git a/include/layers_fused/ConvRelu.hpp b/include/layers_fused/ConvRelu.hpp index 912fbf52a..ff218e1ce 100644 --- a/include/layers_fused/ConvRelu.hpp +++ b/include/layers_fused/ConvRelu.hpp @@ -4,8 +4,8 @@ #include #include -#include "layers/Layer.hpp" #include "layers/ConvLayer.hpp" +#include "layers/Layer.hpp" #include "layers/Tensor.hpp" namespace it_lab_ai { @@ -79,4 +79,4 @@ class ConvReluLayer : public Layer { Tensor get_weights() override { return *kernel_; } #endif }; -} // namespace it_lab_ai \ No newline at end of file +} // namespace it_lab_ai diff --git a/src/layers_fused/CMakeLists.txt b/src/layers_fused/CMakeLists.txt index 029a4c24e..d66b72bfc 100644 --- a/src/layers_fused/CMakeLists.txt +++ b/src/layers_fused/CMakeLists.txt @@ -5,4 +5,4 @@ target_link_libraries(layers_fused_lib PUBLIC layers_lib) target_link_libraries(layers_fused_lib PUBLIC TBB_unified) target_link_libraries(layers_fused_lib PUBLIC OpenMP::OpenMP_CXX) target_link_libraries(layers_fused_lib PUBLIC dnnl) -target_link_libraries(layers_fused_lib PUBLIC Kokkos_imported) \ No newline at end of file +target_link_libraries(layers_fused_lib PUBLIC Kokkos_imported) From cebf82cff2c231ece868fae67b1400447912f6a6 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 17:27:29 +0300 Subject: [PATCH 07/14] Tidy --- app/Graph/onnx_subgraphs.cpp | 16 +++++----- include/layers/ConvLayer.hpp | 6 ++-- include/layers_fused/ConvRelu.hpp | 4 +-- test/graph/test_graph.cpp | 38 +++++++++++++++++++++++ test/single_layer/test_convlayer.cpp | 16 ++++++++++ test/single_layer_fused/test_convrelu.cpp | 15 +++++++++ 6 files changed, 82 insertions(+), 13 deletions(-) diff --git a/app/Graph/onnx_subgraphs.cpp b/app/Graph/onnx_subgraphs.cpp index 31dd17ea4..54de021a4 100644 --- a/app/Graph/onnx_subgraphs.cpp +++ b/app/Graph/onnx_subgraphs.cpp @@ -16,8 +16,8 @@ using namespace it_lab_ai; void alexnet_inf_careless(Graph& graph, const RuntimeOptions& options, Tensor& input, Tensor& output) { - Tensor* o = new Tensor(output); - Tensor* i = new Tensor(input); + auto* o = new Tensor(output); + auto* i = new Tensor(input); graph.inference(options); graph.setOutput(*o); graph.setInput(*i); @@ -28,7 +28,7 @@ void alexnet_comparison() { 891, 957, 1027, 973, 1008}; size_t sum = std::accumulate(counts.begin(), counts.end(), size_t{0}); int count_pic = static_cast(sum) + 10; - std::vector res(count_pic * 28 * 28, 1.0f); + std::vector res(count_pic * 28 * 28, 1.0F); Tensor input; Shape sh1({1, 5, 5, 3}); std::vector vec; @@ -43,9 +43,9 @@ void alexnet_comparison() { input = t; RuntimeOptions options; - Graph graph1; + Graph graph; Graph graph2; - build_graph_linear(graph1, input, output, options, true); + build_graph_linear(graph, input, output, options, true); Graph subgraph; std::shared_ptr layer_0 = std::make_shared(); std::shared_ptr layer_1 = std::make_shared("relu"); @@ -53,11 +53,11 @@ void alexnet_comparison() { subgraph.makeConnection(layer_0, layer_1); std::shared_ptr layer_to = std::make_shared( std::dynamic_pointer_cast(layer_0)); - changed_subgraphs(graph1, subgraph, layer_to, graph2, input, options); + changed_subgraphs(graph, subgraph, layer_to, graph2, input, options); Tensor input_c = input; Tensor output_c = output; double time1 = elapsed_time_avg( - 2, alexnet_inf_careless, graph1, options, input_c, output_c); + 2, alexnet_inf_careless, graph, options, input_c, output_c); double time2 = elapsed_time_avg( 2, alexnet_inf_careless, graph2, options, input_c, output_c); std::cout << time1 << " for unchanged graph\n"; @@ -74,7 +74,7 @@ int main() { build_graph(graph1, input, input, MODEL_PATH_DENSENET_ONNX, options, false); Graph subgraph; - Tensor scale = make_tensor(std::vector({1.0})); + Tensor scale = make_tensor(std::vector({1.0F})); std::shared_ptr layer_0 = std::make_shared(scale, scale, scale, scale); std::shared_ptr layer_1 = std::make_shared("relu"); diff --git a/include/layers/ConvLayer.hpp b/include/layers/ConvLayer.hpp index cdc0c8576..d0ca692e0 100644 --- a/include/layers/ConvLayer.hpp +++ b/include/layers/ConvLayer.hpp @@ -59,17 +59,17 @@ class ConvolutionalLayer : public Layer { useLegacyImpl_ = useLegacyImpl; } - std::vector get_numeric_params() { + std::vector getNumericParams() const { std::vector res = {stride_, pads_, dilations_, group_}; return res; } - std::vector> get_tensor_params() { + std::vector> getTensorParams() { std::vector> res = {kernel_, bias_}; return res; } - bool getLegacyImplBool() { return useLegacyImpl_; } + bool getLegacyImplBool() const { return useLegacyImpl_; } void run(const std::vector& input, std::vector& output) override; diff --git a/include/layers_fused/ConvRelu.hpp b/include/layers_fused/ConvRelu.hpp index ff218e1ce..016c15b58 100644 --- a/include/layers_fused/ConvRelu.hpp +++ b/include/layers_fused/ConvRelu.hpp @@ -61,8 +61,8 @@ class ConvReluLayer : public Layer { } ConvReluLayer(const std::shared_ptr& conv) : Layer(kConvRelu) { - auto numerics = conv->get_numeric_params(); - auto tensors = conv->get_tensor_params(); + auto numerics = conv->getNumericParams(); + auto tensors = conv->getTensorParams(); stride_ = numerics[0]; pads_ = numerics[1]; dilations_ = numerics[2]; diff --git a/test/graph/test_graph.cpp b/test/graph/test_graph.cpp index 5f8551039..fac203708 100644 --- a/test/graph/test_graph.cpp +++ b/test/graph/test_graph.cpp @@ -32,6 +32,44 @@ using namespace it_lab_ai; +TEST(graph, test_new_setInput) { + const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; + Tensor weights = make_tensor(vec1, {3, 2}); + Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); + Tensor input = make_tensor({1.0F, 2.0F}, {2}); + Tensor output; + Graph graph; + + auto fcLayer = std::make_shared(weights, bias); + auto inputLayer = std::make_shared(); + auto ewLayer = std::make_shared(); + + graph.addSingleLayer(inputLayer); + graph.makeConnection(inputLayer, fcLayer); + graph.makeConnection(fcLayer, ewLayer); + + ASSERT_NO_THROW(graph.setInput(input)); +} + +TEST(graph, test_new_setOutput) { + const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; + Tensor weights = make_tensor(vec1, {3, 2}); + Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); + Tensor input = make_tensor({1.0F, 2.0F}, {2}); + Tensor output; + Graph graph; + + auto fcLayer = std::make_shared(weights, bias); + auto inputLayer = std::make_shared(); + auto ewLayer = std::make_shared(); + + graph.addSingleLayer(inputLayer); + graph.makeConnection(inputLayer, fcLayer); + graph.makeConnection(fcLayer, ewLayer); + + ASSERT_NO_THROW(graph.setOutput(output)); +} + TEST(graph, test_deep_copy) { Graph graph; Graph graph2; diff --git a/test/single_layer/test_convlayer.cpp b/test/single_layer/test_convlayer.cpp index f4c2cbe48..e1df8eafe 100644 --- a/test/single_layer/test_convlayer.cpp +++ b/test/single_layer/test_convlayer.cpp @@ -7,6 +7,22 @@ using namespace it_lab_ai; class ConvTestFixture : public BaseTestFixture {}; +TEST(ConvolutionalLayerTest, Getters) { + int step = 2; + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape sh2({3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + ConvolutionalLayer layer(step, 0, 1, kernel); + std::vector vec = {1, 2, 3, 4}; + Tensor input1 = make_tensor(vec, {4}); + Tensor input2 = make_tensor(vec, {2, 2}); + std::vector in{input1, input2}; + std::vector output{input1}; + EXPECT_NO_THROW(layer.getLegacyImplBool()); + EXPECT_NO_THROW(layer.getNumericParams()); + EXPECT_NO_THROW(layer.getTensorParams()); +} + TEST(ConvolutionalLayerTest, IncompatibleInput) { int step = 2; std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; diff --git a/test/single_layer_fused/test_convrelu.cpp b/test/single_layer_fused/test_convrelu.cpp index d5e8198af..1090a499c 100644 --- a/test/single_layer_fused/test_convrelu.cpp +++ b/test/single_layer_fused/test_convrelu.cpp @@ -5,6 +5,21 @@ using namespace it_lab_ai; +TEST(ConvReluLayerTest, CopyFromConv) { + int step = 2; + std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Shape sh2({3, 3}); + Tensor kernel = make_tensor(kernelvec, sh2); + std::shared_ptr layer = + std::make_shared(step, 0, 1, kernel); + std::vector vec = {1, 2, 3, 4}; + Tensor input1 = make_tensor(vec, {4}); + Tensor input2 = make_tensor(vec, {2, 2}); + std::vector in{input1, input2}; + std::vector output{input1}; + EXPECT_NO_THROW(ConvReluLayer layer2(layer)); +} + TEST(ConvReluLayerTest, IncompatibleInput) { int step = 2; std::vector kernelvec = {1, 0, 1, 0, 1, 0, 1, 0, 1}; From 66b8cc51b381528f903cca489298cef2ed835565 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 18:02:49 +0300 Subject: [PATCH 08/14] Tidy --- app/Graph/onnx_subgraphs.cpp | 4 ++-- include/layers/ConvLayer.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Graph/onnx_subgraphs.cpp b/app/Graph/onnx_subgraphs.cpp index 54de021a4..9502f5c22 100644 --- a/app/Graph/onnx_subgraphs.cpp +++ b/app/Graph/onnx_subgraphs.cpp @@ -56,9 +56,9 @@ void alexnet_comparison() { changed_subgraphs(graph, subgraph, layer_to, graph2, input, options); Tensor input_c = input; Tensor output_c = output; - double time1 = elapsed_time_avg( + auto time1 = elapsed_time_avg( 2, alexnet_inf_careless, graph, options, input_c, output_c); - double time2 = elapsed_time_avg( + auto time2 = elapsed_time_avg( 2, alexnet_inf_careless, graph2, options, input_c, output_c); std::cout << time1 << " for unchanged graph\n"; std::cout << time2 << " for convrelu graph\n"; diff --git a/include/layers/ConvLayer.hpp b/include/layers/ConvLayer.hpp index d0ca692e0..776be8739 100644 --- a/include/layers/ConvLayer.hpp +++ b/include/layers/ConvLayer.hpp @@ -59,12 +59,12 @@ class ConvolutionalLayer : public Layer { useLegacyImpl_ = useLegacyImpl; } - std::vector getNumericParams() const { + [[nodiscard]] std::vector getNumericParams() const { std::vector res = {stride_, pads_, dilations_, group_}; return res; } - std::vector> getTensorParams() { + [[nodiscard]] std::vector> getTensorParams() { std::vector> res = {kernel_, bias_}; return res; } From 82a6b00bcd9af2c2de12d8b2a5ae700ec80daa41 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 18:07:54 +0300 Subject: [PATCH 09/14] Tidy --- test/graph/test_graph.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/graph/test_graph.cpp b/test/graph/test_graph.cpp index fac203708..48845b310 100644 --- a/test/graph/test_graph.cpp +++ b/test/graph/test_graph.cpp @@ -70,6 +70,28 @@ TEST(graph, test_new_setOutput) { ASSERT_NO_THROW(graph.setOutput(output)); } +TEST(graph, test_new_setInput_throw) { + const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; + Tensor weights = make_tensor(vec1, {3, 2}); + Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); + Tensor input = make_tensor({1.0F, 2.0F}, {2}); + Tensor output; + Graph graph; + + ASSERT_ANY_THROW(graph.setInput(input)); +} + +TEST(graph, test_new_setOutput_throw) { + const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; + Tensor weights = make_tensor(vec1, {3, 2}); + Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); + Tensor input = make_tensor({1.0F, 2.0F}, {2}); + Tensor output; + Graph graph; + + ASSERT_ANY_THROW(graph.setOutput(output)); +} + TEST(graph, test_deep_copy) { Graph graph; Graph graph2; From a9d207bb51c481ccea4f893e975ec6a0c6c74ea2 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 18:12:13 +0300 Subject: [PATCH 10/14] nodiscard --- include/layers/ConvLayer.hpp | 2 +- test/single_layer/test_convlayer.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/layers/ConvLayer.hpp b/include/layers/ConvLayer.hpp index 776be8739..b053ba3ba 100644 --- a/include/layers/ConvLayer.hpp +++ b/include/layers/ConvLayer.hpp @@ -69,7 +69,7 @@ class ConvolutionalLayer : public Layer { return res; } - bool getLegacyImplBool() const { return useLegacyImpl_; } + [[nodiscard]] bool getLegacyImplBool() const { return useLegacyImpl_; } void run(const std::vector& input, std::vector& output) override; diff --git a/test/single_layer/test_convlayer.cpp b/test/single_layer/test_convlayer.cpp index e1df8eafe..f6fbef4d8 100644 --- a/test/single_layer/test_convlayer.cpp +++ b/test/single_layer/test_convlayer.cpp @@ -18,9 +18,9 @@ TEST(ConvolutionalLayerTest, Getters) { Tensor input2 = make_tensor(vec, {2, 2}); std::vector in{input1, input2}; std::vector output{input1}; - EXPECT_NO_THROW(layer.getLegacyImplBool()); - EXPECT_NO_THROW(layer.getNumericParams()); - EXPECT_NO_THROW(layer.getTensorParams()); + EXPECT_NO_THROW(auto ret1 = layer.getLegacyImplBool()); + EXPECT_NO_THROW(auto ret2 = layer.getNumericParams()); + EXPECT_NO_THROW(auto ret3 = layer.getTensorParams()); } TEST(ConvolutionalLayerTest, IncompatibleInput) { From 6e0a91b2b11ad934fad678547015e68d46920290 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 18:27:06 +0300 Subject: [PATCH 11/14] Test fix --- include/layers/ConvLayer.hpp | 6 +++--- test/single_layer/test_convlayer.cpp | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/layers/ConvLayer.hpp b/include/layers/ConvLayer.hpp index b053ba3ba..d0ca692e0 100644 --- a/include/layers/ConvLayer.hpp +++ b/include/layers/ConvLayer.hpp @@ -59,17 +59,17 @@ class ConvolutionalLayer : public Layer { useLegacyImpl_ = useLegacyImpl; } - [[nodiscard]] std::vector getNumericParams() const { + std::vector getNumericParams() const { std::vector res = {stride_, pads_, dilations_, group_}; return res; } - [[nodiscard]] std::vector> getTensorParams() { + std::vector> getTensorParams() { std::vector> res = {kernel_, bias_}; return res; } - [[nodiscard]] bool getLegacyImplBool() const { return useLegacyImpl_; } + bool getLegacyImplBool() const { return useLegacyImpl_; } void run(const std::vector& input, std::vector& output) override; diff --git a/test/single_layer/test_convlayer.cpp b/test/single_layer/test_convlayer.cpp index f6fbef4d8..989db86d5 100644 --- a/test/single_layer/test_convlayer.cpp +++ b/test/single_layer/test_convlayer.cpp @@ -18,9 +18,13 @@ TEST(ConvolutionalLayerTest, Getters) { Tensor input2 = make_tensor(vec, {2, 2}); std::vector in{input1, input2}; std::vector output{input1}; - EXPECT_NO_THROW(auto ret1 = layer.getLegacyImplBool()); - EXPECT_NO_THROW(auto ret2 = layer.getNumericParams()); - EXPECT_NO_THROW(auto ret3 = layer.getTensorParams()); + + bool is_legacy = layer.getLegacyImplBool(); + std::vector nums = layer.getNumericParams(); + auto tens = layer.getTensorParams(); + EXPECT_EQ(is_legacy, false); + EXPECT_FALSE(nums.empty()); + EXPECT_FALSE(tens.empty()); } TEST(ConvolutionalLayerTest, IncompatibleInput) { From 0cbff4ec9a2385fd944e91751b558e259382d28a Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 6 Mar 2026 18:59:52 +0300 Subject: [PATCH 12/14] Tidy again --- include/layers/ConvLayer.hpp | 6 +++--- test/single_layer/test_convlayer.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/layers/ConvLayer.hpp b/include/layers/ConvLayer.hpp index d0ca692e0..b053ba3ba 100644 --- a/include/layers/ConvLayer.hpp +++ b/include/layers/ConvLayer.hpp @@ -59,17 +59,17 @@ class ConvolutionalLayer : public Layer { useLegacyImpl_ = useLegacyImpl; } - std::vector getNumericParams() const { + [[nodiscard]] std::vector getNumericParams() const { std::vector res = {stride_, pads_, dilations_, group_}; return res; } - std::vector> getTensorParams() { + [[nodiscard]] std::vector> getTensorParams() { std::vector> res = {kernel_, bias_}; return res; } - bool getLegacyImplBool() const { return useLegacyImpl_; } + [[nodiscard]] bool getLegacyImplBool() const { return useLegacyImpl_; } void run(const std::vector& input, std::vector& output) override; diff --git a/test/single_layer/test_convlayer.cpp b/test/single_layer/test_convlayer.cpp index 989db86d5..42320a102 100644 --- a/test/single_layer/test_convlayer.cpp +++ b/test/single_layer/test_convlayer.cpp @@ -18,7 +18,7 @@ TEST(ConvolutionalLayerTest, Getters) { Tensor input2 = make_tensor(vec, {2, 2}); std::vector in{input1, input2}; std::vector output{input1}; - + bool is_legacy = layer.getLegacyImplBool(); std::vector nums = layer.getNumericParams(); auto tens = layer.getTensorParams(); From d1a499a415725f1cddf9a42201d83a24ba3dea77 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 13 Mar 2026 02:08:44 +0300 Subject: [PATCH 13/14] Changes --- app/Graph/onnx_subgraphs.cpp | 15 +- include/graph/graph.hpp | 23 -- include/layers_fused/ConvRelu.hpp | 349 ++++++++++++++++++++++++++++++ src/layers_fused/ConvRelu.cpp | 150 ++++++++++++- test/graph/test_graph.cpp | 60 ----- 5 files changed, 504 insertions(+), 93 deletions(-) diff --git a/app/Graph/onnx_subgraphs.cpp b/app/Graph/onnx_subgraphs.cpp index 9502f5c22..d666195db 100644 --- a/app/Graph/onnx_subgraphs.cpp +++ b/app/Graph/onnx_subgraphs.cpp @@ -19,8 +19,11 @@ void alexnet_inf_careless(Graph& graph, const RuntimeOptions& options, auto* o = new Tensor(output); auto* i = new Tensor(input); graph.inference(options); - graph.setOutput(*o); - graph.setInput(*i); + if (graph.getLayersCount() == 0) { + throw std::runtime_error("No layers"); + } + graph.setOutput(graph.getLayerFromID(graph.getLayersCount() - 1), *o); + graph.setInput(graph.getLayerFromID(0), *i); } void alexnet_comparison() { @@ -45,7 +48,7 @@ void alexnet_comparison() { RuntimeOptions options; Graph graph; Graph graph2; - build_graph_linear(graph, input, output, options, true); + build_graph_linear(graph, input, output, options, true, false); Graph subgraph; std::shared_ptr layer_0 = std::make_shared(); std::shared_ptr layer_1 = std::make_shared("relu"); @@ -57,9 +60,11 @@ void alexnet_comparison() { Tensor input_c = input; Tensor output_c = output; auto time1 = elapsed_time_avg( - 2, alexnet_inf_careless, graph, options, input_c, output_c); + 4, alexnet_inf_careless, graph, options, input_c, output_c); + print_time_stats(graph); auto time2 = elapsed_time_avg( - 2, alexnet_inf_careless, graph2, options, input_c, output_c); + 4, alexnet_inf_careless, graph2, options, input_c, output_c); + print_time_stats(graph2); std::cout << time1 << " for unchanged graph\n"; std::cout << time2 << " for convrelu graph\n"; } diff --git a/include/graph/graph.hpp b/include/graph/graph.hpp index 758ecc73f..3e95a4fb1 100644 --- a/include/graph/graph.hpp +++ b/include/graph/graph.hpp @@ -176,16 +176,6 @@ class Graph { start_ = layer->getID(); } - void setInput(Tensor& vec) { - if (layers_.empty()) { - throw std::invalid_argument("No layers in graph"); - } - int id = layers_.front()->getID(); - - inten_ = {vec}; - start_ = id; - } - void addSingleLayer(const std::shared_ptr& layer) { if (!layer) return; @@ -459,19 +449,6 @@ class Graph { } } - void setOutput(Tensor& vec) { - if (layers_.empty()) { - throw std::invalid_argument("No layers in graph"); - } - end_ = layers_.back()->getID(); - outtenres_ = &vec; - if (outten_.empty()) { - std::vector vec1 = {1, 7, 1, 0}; - Tensor start = make_tensor(vec1); - outten_.push_back(start); - } - } - #ifdef ENABLE_STATISTIC_TENSORS std::vector getTensors() { return tensors_; } #endif diff --git a/include/layers_fused/ConvRelu.hpp b/include/layers_fused/ConvRelu.hpp index 016c15b58..d9123134e 100644 --- a/include/layers_fused/ConvRelu.hpp +++ b/include/layers_fused/ConvRelu.hpp @@ -79,4 +79,353 @@ class ConvReluLayer : public Layer { Tensor get_weights() override { return *kernel_; } #endif }; + +// NCHW -> NCHW only +template +void Conv4DRelu(const Tensor& input, const Tensor& kernel_, const Tensor& bias_, + Tensor& output, size_t stride_, size_t pads_, size_t group_, + size_t dilations_, ParBackend backend = ParBackend::kSeq) { + size_t batch_size = input.get_shape()[0]; + size_t in_channels = input.get_shape()[1]; + size_t in_height = input.get_shape()[2]; + size_t in_width = input.get_shape()[3]; + + size_t out_channels = kernel_.get_shape()[0]; + size_t kernel_in_channels = kernel_.get_shape()[1]; + size_t kernel_height = kernel_.get_shape()[2]; + size_t kernel_width = kernel_.get_shape()[3]; + + if (group_ > 1) { + if (in_channels % group_ != 0 || out_channels % group_ != 0) { + throw std::runtime_error("Channels must be divisible by group"); + } + if (kernel_in_channels != in_channels / group_) { + throw std::runtime_error( + "Kernel input channels don't match group configuration"); + } + } + + size_t out_height = ComputeConvOutputDim(in_height, kernel_height, stride_, + pads_, dilations_); + size_t out_width = + ComputeConvOutputDim(in_width, kernel_width, stride_, pads_, dilations_); + + std::vector>>> padded_input( + batch_size, + std::vector>>( + in_height + 2 * pads_, + std::vector>( + in_width + 2 * pads_, std::vector(in_channels, 0)))); + + parallel::Options options; + options.backend = backend; + + parallel::parallel_for( + batch_size, + [&](size_t b) { + for (size_t h = 0; h < in_height; ++h) { + for (size_t w = 0; w < in_width; ++w) { + for (size_t c = 0; c < in_channels; ++c) { + padded_input[b][h + pads_][w + pads_][c] = + input.get({b, c, h, w}); + } + } + } + }, + options); + + size_t dilated_kernel_height = (kernel_height - 1) * dilations_ + 1; + size_t dilated_kernel_width = (kernel_width - 1) * dilations_ + 1; + + std::vector>>> dil_kernel( + out_channels, std::vector>>( + kernel_in_channels, + std::vector>( + dilated_kernel_height, + std::vector(dilated_kernel_width, 0)))); + + parallel::parallel_for( + out_channels, + [&](size_t oc) { + for (size_t ic = 0; ic < kernel_in_channels; ++ic) { + for (size_t kh = 0; kh < kernel_height; ++kh) { + for (size_t kw = 0; kw < kernel_width; ++kw) { + dil_kernel[oc][ic][kh * dilations_][kw * dilations_] = + kernel_.get({oc, ic, kh, kw}); + } + } + } + }, + options); + + std::vector>>> output_tensor( + batch_size, + std::vector>>( + out_channels, std::vector>( + out_height, std::vector(out_width, 0)))); + + size_t total_work = batch_size * out_channels; + parallel::parallel_for( + total_work, + [&](size_t idx) { + size_t b = idx / out_channels; + size_t oc = idx % out_channels; + + for (size_t oh = 0; oh < out_height; ++oh) { + for (size_t ow = 0; ow < out_width; ++ow) { + ValueType value = 0; + size_t h_start = oh * stride_; + size_t w_start = ow * stride_; + + size_t group = (group_ > 1) ? oc / (out_channels / group_) : 0; + size_t group_start_channel = group * (in_channels / group_); + size_t group_end_channel = (group + 1) * (in_channels / group_); + + for (size_t ic = group_start_channel; ic < group_end_channel; + ++ic) { + size_t kernel_ic = ic - group_start_channel; + + for (size_t kh = 0; kh < dilated_kernel_height; ++kh) { + for (size_t kw = 0; kw < dilated_kernel_width; ++kw) { + size_t h_index = h_start + kh; + size_t w_index = w_start + kw; + + if (h_index < padded_input[b].size() && + w_index < padded_input[b][h_index].size()) { + value += padded_input[b][h_index][w_index][ic] * + dil_kernel[oc][kernel_ic][kh][kw]; + } + } + } + } + + if (!bias_.empty() && oc < bias_.get_shape()[0]) { + value += bias_.get({oc}); + } + + output_tensor[b][oc][oh][ow] = value > 0 ? value : 0; + } + } + }, + options); + + Shape output_shape({batch_size, out_channels, out_height, out_width}); + std::vector flat_output(batch_size * out_channels * out_height * + out_width); + + parallel::parallel_for( + batch_size, + [&](size_t b) { + size_t base_idx = b * out_channels * out_height * out_width; + for (size_t oc = 0; oc < out_channels; ++oc) { + for (size_t h = 0; h < out_height; ++h) { + for (size_t w = 0; w < out_width; ++w) { + flat_output[base_idx++] = output_tensor[b][oc][h][w]; + } + } + } + }, + options); + + output = make_tensor(flat_output, output_shape); +} + +template +void DepthwiseConv4DRelu(const Tensor& input, const Tensor& kernel_, + const Tensor& bias_, Tensor& output, size_t stride_, + size_t pads_, size_t dilations_, + ParBackend backend = ParBackend::kSeq) { + size_t batch_size = input.get_shape()[0]; + size_t channels = input.get_shape()[1]; + size_t in_height = input.get_shape()[2]; + size_t in_width = input.get_shape()[3]; + + size_t kernel_out_channels = kernel_.get_shape()[0]; + size_t kernel_in_channels = kernel_.get_shape()[1]; + size_t kernel_height = kernel_.get_shape()[2]; + size_t kernel_width = kernel_.get_shape()[3]; + + if (kernel_out_channels != channels || kernel_in_channels != 1) { + throw std::runtime_error("Invalid kernel shape for depthwise convolution"); + } + + size_t out_height = ComputeConvOutputDim(in_height, kernel_height, stride_, + pads_, dilations_); + size_t out_width = + ComputeConvOutputDim(in_width, kernel_width, stride_, pads_, dilations_); + + Tensor output_tensor(Shape({batch_size, channels, out_height, out_width}), + input.get_type()); + + parallel::Options options; + options.backend = backend; + + size_t total_work = batch_size * channels; + + parallel::parallel_for( + total_work, + [&](size_t idx) { + size_t b = idx / channels; + size_t c = idx % channels; + + for (size_t oh = 0; oh < out_height; ++oh) { + for (size_t ow = 0; ow < out_width; ++ow) { + ValueType sum = 0; + + for (size_t kh = 0; kh < kernel_height; ++kh) { + for (size_t kw = 0; kw < kernel_width; ++kw) { + size_t ih = oh * stride_ + kh * dilations_; + size_t iw = ow * stride_ + kw * dilations_; + + if (ih >= pads_ && iw >= pads_ && (ih - pads_) < in_height && + (iw - pads_) < in_width) { + auto input_val = + input.get({b, c, ih - pads_, iw - pads_}); + auto kernel_val = kernel_.get({c, 0, kh, kw}); + sum += input_val * kernel_val; + } + } + } + + if (!bias_.empty() && c < bias_.get_shape()[0]) { + sum += bias_.get({c}); + } + + output_tensor.set({b, c, oh, ow}, sum > 0 ? sum : 0); + } + } + }, + options); + + output = output_tensor; +} + +// NCHW -> NCHW only (Legacy version) +template +void Conv4D_LegacyRelu(const Tensor& input, const Tensor& kernel_, + const Tensor& bias_, Tensor& output, size_t stride_, + size_t pads_, size_t dilations_, + ParBackend backend = ParBackend::kSeq) { + size_t batch_size = input.get_shape()[0]; + size_t in_height = input.get_shape()[2]; + size_t in_width = input.get_shape()[3]; + size_t in_channels = input.get_shape()[1]; + + size_t kernel_height = kernel_.get_shape()[0]; + size_t kernel_width = kernel_.get_shape()[1]; + size_t kernel_in_channels = kernel_.get_shape()[2]; + size_t kernel_out_channels = kernel_.get_shape()[3]; + + parallel::Options options; + options.backend = backend; + + std::vector>>> padded_input( + batch_size, + std::vector>>( + in_height + 2 * pads_, + std::vector>( + in_width + 2 * pads_, std::vector(in_channels, 0)))); + + parallel::parallel_for( + batch_size, + [&](size_t b) { + for (size_t h = 0; h < in_height; ++h) { + for (size_t w = 0; w < in_width; ++w) { + for (size_t c = 0; c < in_channels; ++c) { + padded_input[b][h + pads_][w + pads_][c] = + input.get({b, c, h, w}); + } + } + } + }, + options); + + size_t dilated_kernel_height = kernel_height * dilations_ + 1 - dilations_; + size_t dilated_kernel_width = kernel_width * dilations_ + 1 - dilations_; + + std::vector>>> dil_kernel( + dilated_kernel_height, + std::vector>>( + dilated_kernel_width, + std::vector>( + kernel_in_channels, + std::vector(kernel_out_channels, 0)))); + + parallel::parallel_for( + kernel_out_channels, + [&](size_t b) { + for (size_t h = 0; h < kernel_height; ++h) { + for (size_t w = 0; w < kernel_width; ++w) { + for (size_t c = 0; c < kernel_in_channels; ++c) { + dil_kernel[h * dilations_][w * dilations_][c][b] = + kernel_.get({h, w, c, b}); + } + } + } + }, + options); + + size_t out_height = ComputeConvOutputDim(in_height, kernel_height, stride_, + pads_, dilations_); + size_t out_width = + ComputeConvOutputDim(in_width, kernel_width, stride_, pads_, dilations_); + + std::vector>>> output_tensor( + batch_size, std::vector>>( + kernel_out_channels, + std::vector>( + out_height, std::vector(out_width, 0)))); + + size_t total_work = batch_size * kernel_out_channels; + + parallel::parallel_for( + total_work, + [&](size_t idx) { + size_t b = idx / kernel_out_channels; + size_t c = idx % kernel_out_channels; + + for (size_t i = 0; i < out_height; i += stride_) { + for (size_t j = 0; j < out_width; j += stride_) { + ValueType value = 0; + for (size_t ic = 0; ic < in_channels; ++ic) { + for (size_t h = 0; h < dilated_kernel_height; ++h) { + for (size_t w = 0; w < dilated_kernel_width; ++w) { + value += padded_input[b][i + h][j + w][ic] * + dil_kernel[h][w][ic][c]; + } + } + } + if (!bias_.empty()) { + output_tensor[b][c][i][j] = + value + (*bias_.as())[c] > 0 + ? (value + (*bias_.as())[c]) + : 0; + } else { + output_tensor[b][c][i][j] = value > 0 ? value : 0; + } + } + } + }, + options); + + Shape sh({batch_size, kernel_out_channels, out_height, out_width}); + std::vector one_d_vector(batch_size * out_height * out_width * + kernel_out_channels); + + parallel::parallel_for( + batch_size, + [&](size_t i) { + size_t base_idx = i * kernel_out_channels * out_height * out_width; + for (size_t l = 0; l < kernel_out_channels; ++l) { + for (size_t j = 0; j < out_height; ++j) { + for (size_t k = 0; k < out_width; ++k) { + one_d_vector[base_idx++] = output_tensor[i][l][j][k]; + } + } + } + }, + options); + + output = make_tensor(one_d_vector, sh); +} } // namespace it_lab_ai diff --git a/src/layers_fused/ConvRelu.cpp b/src/layers_fused/ConvRelu.cpp index 76b359497..14009b9f5 100644 --- a/src/layers_fused/ConvRelu.cpp +++ b/src/layers_fused/ConvRelu.cpp @@ -13,16 +13,156 @@ void ConvReluLayer::run(const std::vector& input, void ConvReluLayer::run(const std::vector& input, std::vector& output, const RuntimeOptions& options) { - ConvolutionalLayer conv(stride_, pads_, dilations_, kernel_, bias_, group_, - useLegacyImpl_); - conv.run(input, output, options); + if (kernel_ == nullptr || bias_ == nullptr) { + throw std::runtime_error("ConvolutionalLayer: no weights or bias"); + } + if (input.size() != 1) { + throw std::runtime_error("ConvolutionalLayer: Input tensors not 1"); + } + if (input[0].get_shape().dims() != 4) { + throw std::out_of_range("input must be 4-dimensional"); + } + + ParBackend backend = options.par_backend; + + if (group_ > 1) { + if (group_ == input[0].get_shape()[1] && + group_ == kernel_->get_shape()[0]) { + switch (input[0].get_type()) { + case Type::kFloat: + DepthwiseConv4DRelu(input[0], *kernel_, *bias_, output[0], + stride_, pads_, dilations_, backend); + break; + case Type::kInt: + DepthwiseConv4DRelu(input[0], *kernel_, *bias_, output[0], + stride_, pads_, dilations_, backend); + break; + default: + throw std::runtime_error( + "Unsupported type for depthwise convolution"); + } + return; + } + } + switch (input[0].get_type()) { case Type::kInt: { - relu(output[0]); + if (kernel_->get_shape().dims() == 2) { + if (dilations_ > 0) { + dilations_--; + } + ConvImpl used_impl( + stride_, pads_, dilations_, + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 3]), + input[0].get_shape()[input[0].get_shape().dims() - 1] * + input[0].get_shape()[input[0].get_shape().dims() - 2], + bias_->empty() ? std::vector() : *bias_->as()); + auto sizeforshape = static_cast( + ((static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) - + 1 - + static_cast( + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - 1)) / + static_cast(stride_)) + + 1); + + Shape sh({1, 3, sizeforshape, sizeforshape}); + output[0] = make_tensor( + used_impl.run( + *input[0].as(), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) + + 2 * static_cast(pads_), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]) + + 2 * static_cast(pads_), + *kernel_->as(), + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + static_cast( + ((1 + + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - + 1) / + 2)), + sh); + relu(output[0]); + } else { + Conv4DRelu(input[0], *kernel_, *bias_, output[0], stride_, pads_, + group_, dilations_, backend); + } break; } case Type::kFloat: { - relu(output[0]); + if (kernel_->get_shape().dims() == 2) { + if (dilations_ > 0) { + dilations_--; + } + ConvImpl used_impl( + stride_, pads_, dilations_, + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 3]), + input[0].get_shape()[input[0].get_shape().dims() - 1] * + input[0].get_shape()[input[0].get_shape().dims() - 2], + bias_->empty() ? std::vector() : *bias_->as()); + auto sizeforshape = static_cast( + ((static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) - + 1 - + static_cast( + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - 1)) / + static_cast(stride_)) + + 1); + + Shape sh({1, 3, sizeforshape, sizeforshape}); + output[0] = make_tensor( + used_impl.run( + *input[0].as(), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 1]) + + 2 * static_cast(pads_), + static_cast( + input[0].get_shape()[input[0].get_shape().dims() - 2]) + + 2 * static_cast(pads_), + *kernel_->as(), + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + (1 + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1], + static_cast( + ((1 + + kernel_->get_shape()[kernel_->get_shape().dims() - 1]) * + dilations_ + + kernel_->get_shape()[kernel_->get_shape().dims() - 1] - + 1) / + 2)), + sh); + relu(output[0]); + } else { + if (useLegacyImpl_) { + Conv4D_LegacyRelu(input[0], *kernel_, *bias_, output[0], + stride_, pads_, dilations_, backend); + } else { + Conv4DRelu(input[0], *kernel_, *bias_, output[0], stride_, + pads_, group_, dilations_, backend); + } + } break; } default: { diff --git a/test/graph/test_graph.cpp b/test/graph/test_graph.cpp index 48845b310..5f8551039 100644 --- a/test/graph/test_graph.cpp +++ b/test/graph/test_graph.cpp @@ -32,66 +32,6 @@ using namespace it_lab_ai; -TEST(graph, test_new_setInput) { - const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; - Tensor weights = make_tensor(vec1, {3, 2}); - Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); - Tensor input = make_tensor({1.0F, 2.0F}, {2}); - Tensor output; - Graph graph; - - auto fcLayer = std::make_shared(weights, bias); - auto inputLayer = std::make_shared(); - auto ewLayer = std::make_shared(); - - graph.addSingleLayer(inputLayer); - graph.makeConnection(inputLayer, fcLayer); - graph.makeConnection(fcLayer, ewLayer); - - ASSERT_NO_THROW(graph.setInput(input)); -} - -TEST(graph, test_new_setOutput) { - const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; - Tensor weights = make_tensor(vec1, {3, 2}); - Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); - Tensor input = make_tensor({1.0F, 2.0F}, {2}); - Tensor output; - Graph graph; - - auto fcLayer = std::make_shared(weights, bias); - auto inputLayer = std::make_shared(); - auto ewLayer = std::make_shared(); - - graph.addSingleLayer(inputLayer); - graph.makeConnection(inputLayer, fcLayer); - graph.makeConnection(fcLayer, ewLayer); - - ASSERT_NO_THROW(graph.setOutput(output)); -} - -TEST(graph, test_new_setInput_throw) { - const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; - Tensor weights = make_tensor(vec1, {3, 2}); - Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); - Tensor input = make_tensor({1.0F, 2.0F}, {2}); - Tensor output; - Graph graph; - - ASSERT_ANY_THROW(graph.setInput(input)); -} - -TEST(graph, test_new_setOutput_throw) { - const std::vector vec1 = {2.0F, 1.5F, 0.1F, 1.9F, 0.0F, 5.5F}; - Tensor weights = make_tensor(vec1, {3, 2}); - Tensor bias = make_tensor({0.5F, 0.5F, 1.0F}); - Tensor input = make_tensor({1.0F, 2.0F}, {2}); - Tensor output; - Graph graph; - - ASSERT_ANY_THROW(graph.setOutput(output)); -} - TEST(graph, test_deep_copy) { Graph graph; Graph graph2; From e27d1752d6a88e305767faf0f506788ccf74ae81 Mon Sep 17 00:00:00 2001 From: NeiroYT Date: Fri, 13 Mar 2026 12:55:30 +0300 Subject: [PATCH 14/14] Clang --- include/layers_fused/ConvRelu.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/layers_fused/ConvRelu.hpp b/include/layers_fused/ConvRelu.hpp index d9123134e..e2c815303 100644 --- a/include/layers_fused/ConvRelu.hpp +++ b/include/layers_fused/ConvRelu.hpp @@ -59,7 +59,7 @@ class ConvReluLayer : public Layer { dilations_ = dilations; useLegacyImpl_ = useLegacyImpl; } - ConvReluLayer(const std::shared_ptr& conv) + explicit ConvReluLayer(const std::shared_ptr& conv) : Layer(kConvRelu) { auto numerics = conv->getNumericParams(); auto tensors = conv->getTensorParams();