/**
* @brief Calculate sum according to the axis.
*/
-Tensor Tensor::sum(int axis, float alpha) const {
+Tensor Tensor::sum(unsigned int axis, float alpha) const {
Tensor ret;
const float *data = getData();
if (axis >= 4)
- throw std::out_of_range("Error: Dimension cannot exceed 3");
+ throw std::out_of_range("Error: axis is invalid");
if (dim.getDim()[axis] == 1 and alpha == 1.0)
return this->clone();
return ret;
}
+Tensor Tensor::sum(const std::vector<unsigned int> &axes, float alpha) const {
+ if (axes.empty())
+ throw std::invalid_argument("empty axes given");
+
+ Tensor ret = this->sum(axes[0], alpha);
+
+ for (unsigned int i = 1; i < axes.size(); ++i)
+ ret = ret.sum(axes[i]);
+
+ return ret;
+}
+
/**
* @note: This dot product flattens the fist 3 axis for the purpose of
* computation. So, while performing, these matrices are behaving as 2-D
/**
* @brief Calculate average value according to the axis.
*/
-Tensor Tensor::average(int axis) const {
+Tensor Tensor::average(unsigned int axis) const {
+ if (axis >= MAXDIM)
+ throw std::out_of_range(
+ "negative axis or axis more then MAXDIM is invalid");
+
unsigned int axis_size = dim.getDim()[axis];
if (axis_size == 1)
return this->clone();
return this->sum(axis, 1.0 / ((float)axis_size));
}
+Tensor Tensor::average(const std::vector<unsigned int> &axes) const {
+ if (axes.empty())
+ return this->average();
+
+ TensorDim ret_shape;
+ for (const auto &idx : axes) {
+ if (idx >= MAXDIM) {
+ throw std::out_of_range("axis more then MAXDIM is invalid");
+ }
+ ret_shape.setTensorDim(idx, dim.getTensorDim(idx));
+ }
+
+ return this->sum(axes, 1.0 / (float)ret_shape.getDataLen());
+}
+
/**
* @brief Calculate average value according to the axis.
*/
EXPECT_THROW({ input.sum(4); }, std::out_of_range);
}
+TEST(nntrainer_Tensor, sum_02_n) {
+ int batch = 3;
+ int channel = 1;
+ int height = 3;
+ int width = 10;
+
+ nntrainer::Tensor input(batch, channel, height, width);
+ GEN_TEST_INPUT(input, i * (batch * height) + j * (width) + k);
+
+ EXPECT_THROW({ input.sum(-1); }, std::out_of_range);
+}
+
TEST(nntrainer_Tensor, sum_02_p) {
int status = ML_ERROR_NONE;
int batch = 3;
EXPECT_EQ(status, ML_ERROR_NONE);
}
+TEST(nntrainer_Tensor, multiple_sum_invalid_args_01_n) {
+ nntrainer::Tensor t = constant(1.0, 1, 1, 1, 1);
+ EXPECT_THROW(t.sum(std::vector<unsigned int>()), std::invalid_argument);
+}
+
+TEST(nntrainer_Tensor, multiple_sum_out_of_range_n) {
+ nntrainer::Tensor t = constant(1.0, 1, 1, 1, 1);
+ EXPECT_THROW(t.sum({7}), std::out_of_range);
+}
+
+TEST(nntrainer_Tensor, multiple_sum_p) {
+ nntrainer::Tensor t = constant(1.0, 2, 3, 5, 7);
+ nntrainer::Tensor actual, expected;
+
+ actual = t.sum({0, 1});
+ expected = constant(2 * 3, 1, 1, 5, 7);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.sum({1, 2, 3});
+ expected = constant(3 * 5 * 7, 2, 1, 1, 1);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.sum({3, 1});
+ expected = constant(7 * 3, 2, 1, 5, 1);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.sum({3, 1}, 0.5);
+ expected = constant(7 * 3 * 0.5, 2, 1, 5, 1);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST(nntrainer_Tensor, average_p) {
+ nntrainer::Tensor t = constant(1.0, 2, 3, 5, 7);
+
+ nntrainer::Tensor actual, expected;
+
+ actual = t.average();
+ expected = constant(1.0, 1, 1, 1, 1);
+ EXPECT_EQ(actual, expected);
+
+ int idx = 0;
+ t = t.apply([&](float in) { return idx++ % 2; });
+
+ actual = t.average();
+ expected = constant(0.5, 1, 1, 1, 1);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST(nntrainer_Tensor, average_axis_p) {
+ nntrainer::Tensor t = constant(1.0, 2, 2, 2, 2);
+ int idx = 0;
+ std::function<float(float)> f = [&](float in) { return idx++ % 2; };
+ t = t.apply(f);
+
+ nntrainer::Tensor actual, expected;
+
+ actual = t.average(0);
+ expected = constant(0, 1, 2, 2, 2).apply(f);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.average(1);
+ expected = constant(0, 2, 1, 2, 2).apply(f);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.average(2);
+ expected = constant(0, 2, 2, 1, 2).apply(f);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.average(3);
+ expected = constant(0.5, 2, 2, 2, 1);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST(nntrainer_Tensor, average_axis_out_of_range_01_n) {
+ nntrainer::Tensor t = constant(1.0, 2, 2, 2, 2);
+ EXPECT_THROW(t.average(-1), std::out_of_range);
+}
+
+TEST(nntrainer_Tensor, average_axis_out_of_range_02_n) {
+ nntrainer::Tensor t = constant(1.0, 2, 2, 2, 2);
+ EXPECT_THROW(t.average(7), std::out_of_range);
+}
+
+TEST(nntrainer_Tensor, average_multiple_axes_p) {
+ nntrainer::Tensor t = constant(1.0, 2, 3, 5, 7);
+ nntrainer::Tensor actual, expected;
+
+ actual = t.average({0, 1, 2});
+ expected = constant(1.0, 1, 1, 1, 7);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.average({0, 1, 2, 3});
+ expected = constant(1.0, 1, 1, 1, 1);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.average({3, 1});
+ expected = constant(1.0, 2, 1, 5, 1);
+ EXPECT_EQ(actual, expected);
+
+ actual = t.average({3, 1, 1, 1, 3});
+ expected = constant(1.0, 2, 1, 5, 1);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST(nntrainer_Tensor, average_multiple_axes_01_n) {
+ nntrainer::Tensor t = constant(1.0, 2, 3, 5, 7);
+ EXPECT_THROW(t.average({5, 7}), std::out_of_range);
+}
+
TEST(nntrainer_Tensor, dot_01_n) {
nntrainer::Tensor input(2, 3, 4, 5);
nntrainer::Tensor m(1, 3, 4, 5);