Skip to content

Commit 39a3e1d

Browse files
committed
add tests
1 parent 7929440 commit 39a3e1d

3 files changed

Lines changed: 178 additions & 28 deletions

File tree

include/layers/Shape.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,16 @@ class Shape {
4040
size_t dims() const noexcept { return dims_.size(); }
4141
size_t get_index(const std::vector<size_t>& coords) const;
4242
friend std::ostream& operator<<(std::ostream& os, const Shape& shape);
43+
bool operator==(const Shape& other) const noexcept {
44+
if (dims_.size() != other.dims_.size()) {
45+
return false;
46+
}
47+
return std::equal(dims_.begin(), dims_.end(), other.dims_.begin());
48+
}
4349

50+
bool operator!=(const Shape& other) const noexcept {
51+
return !(*this == other);
52+
}
4453
private:
4554
std::vector<size_t> dims_;
4655
};

src/layers/MulLayer.cpp

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,23 @@
33
namespace itlab_2023 {
44

55
void MulLayer::run(const Tensor& A, const Tensor& B, Tensor& output) {
6-
if (B.get_shape().dims().empty() || B.get_shape() == Shape{1}) {
7-
run_with_scalar(A, B.as<float>()->at(0), output);
6+
if (B.get_shape().dims() == 0 ||
7+
(B.get_shape().dims() == 1 && B.get_shape()[0] == 1)) {
8+
if (B.get_type() == Type::kFloat) {
9+
run_with_scalar(A, B.as<float>()->at(0), output);
10+
} else {
11+
run_with_scalar(A, static_cast<float>(B.as<int>()->at(0)), output);
12+
}
813
return;
914
}
10-
if (A.get_shape().dims().empty() || A.get_shape() == Shape{1}) {
11-
run_with_scalar(B, A.as<float>()->at(0), output);
15+
16+
if (A.get_shape().dims() == 0 ||
17+
(A.get_shape().dims() == 1 && A.get_shape()[0] == 1)) {
18+
if (A.get_type() == Type::kFloat) {
19+
run_with_scalar(B, A.as<float>()->at(0), output);
20+
} else {
21+
run_with_scalar(B, static_cast<float>(A.as<int>()->at(0)), output);
22+
}
1223
return;
1324
}
1425

@@ -19,23 +30,23 @@ void MulLayer::run(const Tensor& A, const Tensor& B, Tensor& output) {
1930
if (A.get_shape() == B.get_shape()) {
2031
switch (A.get_type()) {
2132
case Type::kFloat: {
22-
auto a_data = A.as<float>()->get_data();
23-
auto b_data = B.as<float>()->get_data();
33+
const auto& a_data = *A.as<float>();
34+
const auto& b_data = *B.as<float>();
2435
std::vector<float> result;
2536
result.reserve(a_data.size());
2637
std::transform(a_data.begin(), a_data.end(), b_data.begin(),
2738
std::back_inserter(result), std::multiplies<float>());
28-
output = Tensor(result, A.get_shape());
39+
output = make_tensor(result, A.get_shape());
2940
break;
3041
}
3142
case Type::kInt: {
32-
auto a_data = A.as<int>()->get_data();
33-
auto b_data = B.as<int>()->get_data();
43+
const auto& a_data = *A.as<int>();
44+
const auto& b_data = *B.as<int>();
3445
std::vector<int> result;
3546
result.reserve(a_data.size());
3647
std::transform(a_data.begin(), a_data.end(), b_data.begin(),
3748
std::back_inserter(result), std::multiplies<int>());
38-
output = Tensor(result, A.get_shape());
49+
output = make_tensor(result, A.get_shape());
3950
break;
4051
}
4152
default:
@@ -49,29 +60,29 @@ void MulLayer::run(const Tensor& A, const Tensor& B, Tensor& output) {
4960

5061
switch (A.get_type()) {
5162
case Type::kFloat: {
52-
auto a_data = A.as<float>()->get_data();
53-
auto b_data = B.as<float>()->get_data();
63+
const auto& a_data = *A.as<float>();
64+
const auto& b_data = *B.as<float>();
5465
std::vector<float> result(output_shape.count());
5566

5667
for (size_t i = 0; i < result.size(); ++i) {
5768
size_t a_idx = get_broadcasted_index(i, A.get_shape(), output_shape);
5869
size_t b_idx = get_broadcasted_index(i, B.get_shape(), output_shape);
5970
result[i] = a_data[a_idx] * b_data[b_idx];
6071
}
61-
output = Tensor(result, output_shape);
72+
output = make_tensor(result, output_shape);
6273
break;
6374
}
6475
case Type::kInt: {
65-
auto a_data = A.as<int>()->get_data();
66-
auto b_data = B.as<int>()->get_data();
76+
const auto& a_data = *A.as<int>();
77+
const auto& b_data = *B.as<int>();
6778
std::vector<int> result(output_shape.count());
6879

6980
for (size_t i = 0; i < result.size(); ++i) {
7081
size_t a_idx = get_broadcasted_index(i, A.get_shape(), output_shape);
7182
size_t b_idx = get_broadcasted_index(i, B.get_shape(), output_shape);
7283
result[i] = a_data[a_idx] * b_data[b_idx];
7384
}
74-
output = Tensor(result, output_shape);
85+
output = make_tensor(result, output_shape);
7586
break;
7687
}
7788
default:
@@ -84,21 +95,23 @@ void MulLayer::run_with_scalar(const Tensor& input, float scalar,
8495
const auto& shape = input.get_shape();
8596
switch (input.get_type()) {
8697
case Type::kFloat: {
87-
auto input_data = input.as<float>()->get_data();
88-
std::vector<float> result(shape.count());
89-
for (size_t i = 0; i < result.size(); ++i) {
90-
result[i] = input_data[i] * scalar;
98+
const auto& input_data = *input.as<float>();
99+
std::vector<float> result;
100+
result.reserve(shape.count());
101+
for (auto val : input_data) {
102+
result.push_back(val * scalar);
91103
}
92-
output = Tensor(result, shape);
104+
output = make_tensor(result, shape);
93105
break;
94106
}
95107
case Type::kInt: {
96-
auto input_data = input.as<int>()->get_data();
97-
std::vector<int> result(shape.count());
98-
for (size_t i = 0; i < result.size(); ++i) {
99-
result[i] = input_data[i] * static_cast<int>(scalar);
108+
const auto& input_data = *input.as<int>();
109+
std::vector<int> result;
110+
result.reserve(shape.count());
111+
for (auto val : input_data) {
112+
result.push_back(val * static_cast<int>(scalar));
100113
}
101-
output = Tensor(result, shape);
114+
output = make_tensor(result, shape);
102115
break;
103116
}
104117
default:
@@ -143,7 +156,7 @@ std::vector<size_t> MulLayer::get_strides(const Shape& shape) {
143156
if (strides.empty()) return strides;
144157

145158
strides.back() = 1;
146-
for (int i = shape.dims() - 2; i >= 0; --i) {
159+
for (int i = (int)shape.dims() - 2; i >= 0; --i) {
147160
strides[i] = strides[i + 1] * shape[i + 1];
148161
}
149162
return strides;
Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,129 @@
1-

1+
#include <vector>
2+
3+
#include "layers/Tensor.hpp"
4+
#include "gtest/gtest.h"
5+
#include "layers/MulLayer.hpp"
6+
7+
using namespace itlab_2023;
8+
9+
class MulLayerTests : public ::testing::Test {
10+
protected:
11+
void SetUp() override {
12+
data1 = {1.0f, 2.0f, 3.0f, 4.0f};
13+
data2 = {2.0f, 3.0f, 4.0f, 5.0f};
14+
data_int = {1, 2, 3, 4};
15+
scalar = make_tensor<float>({2.0f});
16+
scalar_int = make_tensor<int>({2});
17+
}
18+
19+
std::vector<float> data1;
20+
std::vector<float> data2;
21+
std::vector<int> data_int;
22+
Tensor scalar;
23+
Tensor scalar_int;
24+
};
25+
26+
// Тест умножения тензоров одинаковой формы (float)
27+
TEST_F(MulLayerTests, MulSameShapeFloat) {
28+
MulLayer layer;
29+
Tensor input1 = make_tensor<float>(data1, {2, 2});
30+
Tensor input2 = make_tensor<float>(data2, {2, 2});
31+
Tensor output;
32+
33+
layer.run(input1, input2, output);
34+
35+
auto* result = output.as<float>();
36+
EXPECT_FLOAT_EQ((*result)[0], 2.0f); // 1*2
37+
EXPECT_FLOAT_EQ((*result)[1], 6.0f); // 2*3
38+
EXPECT_FLOAT_EQ((*result)[2], 12.0f); // 3*4
39+
EXPECT_FLOAT_EQ((*result)[3], 20.0f); // 4*5
40+
}
41+
42+
// Тест умножения тензоров одинаковой формы (int)
43+
TEST_F(MulLayerTests, MulSameShapeInt) {
44+
MulLayer layer;
45+
Tensor input1 = make_tensor<int>(data_int, {2, 2});
46+
Tensor input2 = make_tensor<int>(data_int, {2, 2});
47+
Tensor output;
48+
49+
layer.run(input1, input2, output);
50+
51+
auto* result = output.as<int>();
52+
EXPECT_EQ((*result)[0], 1); // 1*1
53+
EXPECT_EQ((*result)[1], 4); // 2*2
54+
EXPECT_EQ((*result)[2], 9); // 3*3
55+
EXPECT_EQ((*result)[3], 16); // 4*4
56+
}
57+
58+
// Тест умножения тензора на скаляр (float)
59+
TEST_F(MulLayerTests, MulWithScalarFloat) {
60+
MulLayer layer;
61+
Tensor input = make_tensor<float>(data1, {2, 2});
62+
Tensor output;
63+
64+
layer.run(input, scalar, output);
65+
66+
auto* result = output.as<float>();
67+
EXPECT_FLOAT_EQ((*result)[0], 2.0f); // 1*2
68+
EXPECT_FLOAT_EQ((*result)[1], 4.0f); // 2*2
69+
EXPECT_FLOAT_EQ((*result)[2], 6.0f); // 3*2
70+
EXPECT_FLOAT_EQ((*result)[3], 8.0f); // 4*2
71+
}
72+
73+
// Тест умножения тензора на скаляр (int)
74+
TEST_F(MulLayerTests, MulWithScalarInt) {
75+
MulLayer layer;
76+
Tensor input = make_tensor<int>(data_int, {2, 2});
77+
Tensor output;
78+
79+
layer.run(input, scalar_int, output);
80+
81+
auto* result = output.as<int>();
82+
EXPECT_EQ((*result)[0], 2); // 1*2
83+
EXPECT_EQ((*result)[1], 4); // 2*2
84+
EXPECT_EQ((*result)[2], 6); // 3*2
85+
EXPECT_EQ((*result)[3], 8); // 4*2
86+
}
87+
88+
// Тест broadcasting (расширения размерностей)
89+
TEST_F(MulLayerTests, BroadcastingTest) {
90+
MulLayer layer;
91+
Tensor input1 = make_tensor<float>({1.0f, 2.0f}, {2, 1}); // shape [2,1]
92+
Tensor input2 = make_tensor<float>({3.0f, 4.0f}, {1, 2}); // shape [1,2]
93+
Tensor output;
94+
95+
layer.run(input1, input2, output);
96+
97+
// Ожидаемый результат после broadcasting:
98+
// [[1*3, 1*4], [2*3, 2*4]] = [[3,4], [6,8]]
99+
auto* result = output.as<float>();
100+
EXPECT_FLOAT_EQ((*result)[0], 3.0f);
101+
EXPECT_FLOAT_EQ((*result)[1], 4.0f);
102+
EXPECT_FLOAT_EQ((*result)[2], 6.0f);
103+
EXPECT_FLOAT_EQ((*result)[3], 8.0f);
104+
}
105+
106+
// Тест на несовместимые формы
107+
TEST_F(MulLayerTests, IncompatibleShapes) {
108+
MulLayer layer;
109+
Tensor input1 = make_tensor<float>(data1, {4});
110+
Tensor input2 = make_tensor<float>(data2, {2, 2});
111+
Tensor output;
112+
113+
EXPECT_THROW(layer.run(input1, input2, output), std::runtime_error);
114+
}
115+
116+
// Тест имени слоя
117+
TEST_F(MulLayerTests, LayerName) {
118+
EXPECT_EQ(MulLayer::get_name(), "Element-wise Multiplication Layer");
119+
}
120+
121+
// Тест умножения пустых тензоров
122+
TEST_F(MulLayerTests, EmptyTensors) {
123+
MulLayer layer;
124+
Tensor empty1({}, Type::kFloat);
125+
Tensor empty2({}, Type::kFloat);
126+
Tensor output;
127+
128+
EXPECT_NO_THROW(layer.run(empty1, empty2, output));
129+
}

0 commit comments

Comments
 (0)