Skip to content

Commit

Permalink
CumSum reference implementation revision (openvinotoolkit#6915)
Browse files Browse the repository at this point in the history
* New CumSum implementation init

* Unified ndim approach

* Move transpose to separate function

* Move transpose to original to separate function

* Move slice_count calculation to function

* Negative axes support

* Refactor redundant copy

* Changed copy to move

* Temp more backend tests

* Add const to shape arg

* Use span for slices calculation

* Remove unused headers

* CumSum new ref tests

* Add more ref tests

* Add all cumsum modes ref tests

* new optimized cum_sum reference

* Add reverse mode

* Optimized cumsum ref

* Remove deprecated cumsum backend tests

* Add more CumSum reference tests

* Simplify CumSum shared layer tests SetUp

* Replace auto to size_t in loop

* Change static_cast to T{}
  • Loading branch information
mitruska authored and mryzhov committed Aug 23, 2021
1 parent 4d0c11b commit 790f357
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 264 deletions.
193 changes: 193 additions & 0 deletions docs/template_plugin/tests/functional/op_reference/cum_sum.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright (C) 2018-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#include <gtest/gtest.h>

#include <ie_core.hpp>
#include <ie_ngraph_utils.hpp>
#include <ngraph/ngraph.hpp>
#include <shared_test_classes/base/layer_test_utils.hpp>
#include <tuple>

#include "base_reference_test.hpp"

using namespace reference_tests;
using namespace ngraph;
using namespace InferenceEngine;

namespace {
struct CumSumParams {
// Custom axis input and attributes
template <class IT, class AT>
CumSumParams(const PartialShape& shape, const element::Type& iType, const std::vector<IT>& iValues, const std::vector<IT>& oValues, const bool execlusive,
const bool reverse, const element::Type& axisType, AT axisVal, const PartialShape& axisShape)
: execlusive(execlusive),
reverse(reverse),
axisValue(axisVal),
axisShape(axisShape),
inShape(shape),
axisType(axisType),
inType(iType),
outType(iType),
axisData(CreateBlob(axisType, std::vector<AT> {axisVal})),
inputData(CreateBlob(iType, iValues)),
refData(CreateBlob(iType, oValues)),
testDefaults(false) {}

// Default axis input and attributes
template <class IT>
CumSumParams(const PartialShape& shape, const element::Type& iType, const std::vector<IT>& iValues, const std::vector<IT>& oValues)
: inShape(shape),
axisType(element::i32),
inType(iType),
outType(iType),
inputData(CreateBlob(iType, iValues)),
refData(CreateBlob(iType, oValues)),
testDefaults(true) {}

bool execlusive = false;
bool reverse = false;
int64_t axisValue = 0;

PartialShape axisShape;
PartialShape inShape;
element::Type axisType;
element::Type inType;
element::Type outType;
Blob::Ptr axisData;
Blob::Ptr inputData;
Blob::Ptr refData;

bool testDefaults = false;
};

class ReferenceCumSumLayerTest : public testing::TestWithParam<CumSumParams>, public CommonReferenceTest {
public:
void SetUp() override {
auto params = GetParam();
if (params.testDefaults) {
function = CreateFunction(params.inShape, params.inType);
inputData = {params.inputData};
refOutData = {params.refData};
} else {
function = CreateFunction(params.inShape, params.inType, params.axisShape, params.axisType, params.execlusive, params.reverse);
inputData = {params.inputData, params.axisData};
refOutData = {params.refData};
}
}
static std::string getTestCaseName(const testing::TestParamInfo<CumSumParams>& obj) {
auto param = obj.param;
std::ostringstream result;
result << "testDefaults=" << param.testDefaults << "_";
result << "axisValue=" << param.axisValue << "_";
result << "execlusive=" << param.execlusive << "_";
result << "reverse=" << param.reverse << "_";
result << "inShape=" << param.inShape << "_";
result << "iType=" << param.inType << "_";
result << "axisType=" << param.axisType << "_";
result << "oType=" << param.outType;
return result.str();
}

private:
static std::shared_ptr<Function> CreateFunction(const PartialShape& data_shape, const element::Type& data_type, const PartialShape& axis_shape,
const element::Type& axis_type, const bool execlusive, const bool reverse) {
const auto data_param = std::make_shared<op::Parameter>(data_type, data_shape);
const auto axis_param = std::make_shared<op::Parameter>(axis_type, axis_shape);
const auto cum_sum = std::make_shared<op::v0::CumSum>(data_param, axis_param, execlusive, reverse);
return std::make_shared<Function>(NodeVector {cum_sum}, ParameterVector {data_param, axis_param});
}

static std::shared_ptr<Function> CreateFunction(const PartialShape& data_shape, const element::Type& data_type) {
const auto data_param = std::make_shared<op::Parameter>(data_type, data_shape);
const auto cum_sum = std::make_shared<op::v0::CumSum>(data_param);
return std::make_shared<Function>(NodeVector {cum_sum}, ParameterVector {data_param});
}
};

TEST_P(ReferenceCumSumLayerTest, CompareWithHardcodedRefs) {
Exec();
}

template <element::Type_t IN_ET>
std::vector<CumSumParams> generateCumSumParams(const element::Type& type) {
using T = typename element_type_traits<IN_ET>::value_type;
std::vector<CumSumParams> opParams {
// Default axis input and attributes
CumSumParams(PartialShape {1}, type, std::vector<T> {3}, std::vector<T> {3}),
CumSumParams(PartialShape {6}, type, std::vector<T> {1, 2, 3, 4, 5, 6}, std::vector<T> {1, 3, 6, 10, 15, 21}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {0, 1, 2, 3, 4, 6, 8, 10}),
// Custom axis input and attributes
CumSumParams(PartialShape {6}, type, std::vector<T> {1, 2, 3, 4, 5, 6}, std::vector<T> {1, 3, 6, 10, 15, 21}, false, false, element::i32, int32_t(0),
PartialShape {}), // axis i32
CumSumParams(PartialShape {6}, type, std::vector<T> {1, 2, 3, 4, 5, 6}, std::vector<T> {1, 3, 6, 10, 15, 21}, false, false, element::i64, int64_t(0),
PartialShape {}), // axis i64
CumSumParams(PartialShape {6}, type, std::vector<T> {1, 2, 3, 4, 5, 6}, std::vector<T> {21, 20, 18, 15, 11, 6}, false, true, element::i64, int64_t(0),
PartialShape {}),
CumSumParams(PartialShape {6}, type, std::vector<T> {1, 2, 3, 4, 5, 6}, std::vector<T> {0, 1, 3, 6, 10, 15}, true, false, element::i64, int64_t(0),
PartialShape {}),
CumSumParams(PartialShape {6}, type, std::vector<T> {1, 2, 3, 4, 5, 6}, std::vector<T> {20, 18, 15, 11, 6, 0}, true, true, element::i64, int64_t(0),
PartialShape {}),

CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {0, 1, 2, 3, 4, 6, 8, 10}, false, false, element::i32,
int32_t(0), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {4, 6, 8, 10, 4, 5, 6, 7}, false, true, element::i32,
int32_t(0), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {0, 0, 0, 0, 0, 1, 2, 3}, true, false, element::i32,
int32_t(0), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {4, 5, 6, 7, 0, 0, 0, 0}, true, true, element::i32,
int32_t(0), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {0, 1, 3, 6, 4, 9, 15, 22}, false, false, element::i32,
int32_t(1), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {0, 0, 1, 3, 0, 4, 9, 15}, true, false, element::i32,
int32_t(1), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {6, 6, 5, 3, 22, 18, 13, 7}, false, true, element::i32,
int32_t(1), PartialShape {}),
CumSumParams(PartialShape {2, 4}, type, std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7}, std::vector<T> {6, 5, 3, 0, 18, 13, 7, 0}, true, true, element::i32,
int32_t(1), PartialShape {}),

CumSumParams(PartialShape {3, 2, 4}, type,
std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23},
std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7,
8, 10, 12, 14, 16, 18, 20, 22,
24, 27, 30, 33, 36, 39, 42, 45},
false, false, element::i32, int32_t(0), PartialShape {}),
CumSumParams(PartialShape {3, 2, 4}, type,
std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23},
std::vector<T> {0, 1, 2, 3, 4, 6, 8, 10,
8, 9, 10, 11, 20, 22, 24, 26,
16, 17, 18, 19, 36, 38, 40, 42},
false, false, element::i32, int32_t(1), PartialShape {}),
CumSumParams(PartialShape {3, 2, 4}, type,
std::vector<T> {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23},
std::vector<T> {0, 1, 3, 6, 4, 9, 15, 22,
8, 17, 27, 38, 12, 25, 39, 54,
16, 33, 51, 70, 20, 41, 63, 86},
false, false, element::i32, int32_t(2), PartialShape {}),
};
return opParams;
}

std::vector<CumSumParams> generateCumSumCombinedParams() {
const std::vector<std::vector<CumSumParams>> opTypeParams {
generateCumSumParams<element::Type_t::bf16>(element::bf16), generateCumSumParams<element::Type_t::f16>(element::f16),
generateCumSumParams<element::Type_t::f32>(element::f32), generateCumSumParams<element::Type_t::i32>(element::i32),
generateCumSumParams<element::Type_t::i64>(element::i64), generateCumSumParams<element::Type_t::u32>(element::u32),
generateCumSumParams<element::Type_t::i8>(element::i8)};
std::vector<CumSumParams> combinedParams;
std::for_each(opTypeParams.begin(), opTypeParams.end(), [&](std::vector<CumSumParams> params) {
combinedParams.insert(combinedParams.end(), params.begin(), params.end());
});
return combinedParams;
}

INSTANTIATE_TEST_SUITE_P(smoke_CumSum_With_Hardcoded_Refs, ReferenceCumSumLayerTest, ::testing::ValuesIn(generateCumSumCombinedParams()),
ReferenceCumSumLayerTest::getTestCaseName);
} // namespace
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,12 @@ void CumSumLayerTest::SetUp() {
bool exclusive, reverse;
int64_t axis;
std::tie(inputShapes, inputPrecision, axis, exclusive, reverse, targetDevice) = this->GetParam();
auto inType = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(inputPrecision);
ngraph::ParameterVector paramVector;
auto paramData = std::make_shared<ngraph::opset1::Parameter>(inType, ngraph::Shape(inputShapes));
paramVector.push_back(paramData);
const auto inType = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(inputPrecision);
const auto paramData = std::make_shared<ngraph::op::Parameter>(inType, ngraph::Shape(inputShapes));
const auto axisNode = std::make_shared<ngraph::op::Constant>(ngraph::element::Type_t::i64, ngraph::Shape{}, std::vector<int64_t>{axis})->output(0);
const auto cumSum = std::make_shared<ngraph::op::v0::CumSum>(paramData, axisNode, exclusive, reverse);

auto axisNode = std::make_shared<ngraph::op::Constant>(ngraph::element::Type_t::i64, ngraph::Shape{}, std::vector<int64_t>{axis})->output(0);

auto paramOuts = ngraph::helpers::convert2OutputVector(ngraph::helpers::castOps2Nodes<ngraph::op::Parameter>(paramVector));
auto cumSum = std::dynamic_pointer_cast<ngraph::op::CumSum>(ngraph::builder::makeCumSum(paramOuts[0], axisNode, exclusive, reverse));

ngraph::ResultVector results{std::make_shared<ngraph::opset1::Result>(cumSum)};
function = std::make_shared<ngraph::Function>(results, paramVector, "cumsum");
ngraph::ResultVector results{std::make_shared<ngraph::op::Result>(cumSum)};
function = std::make_shared<ngraph::Function>(results, ngraph::ParameterVector{paramData}, "cumsum");
}
} // namespace LayerTestsDefinitions
108 changes: 21 additions & 87 deletions ngraph/core/reference/include/ngraph/runtime/reference/cum_sum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,107 +5,41 @@
#pragma once

#include <cmath>
#include <map>
#include <utility>
#include <vector>

#include "ngraph/coordinate_transform.hpp"
#include "ngraph/type/bfloat16.hpp"
#include "ngraph/type/float16.hpp"

namespace ngraph {
namespace runtime {
namespace reference {

template <typename T, typename P>
void cumsum(const T* arg,
const P* axis_tensor,
T* out,
const Shape& tensor_shape,
const bool exclusive,
const bool reverse) {
NGRAPH_SUPPRESS_DEPRECATED_START
CoordinateTransform temp_transform(tensor_shape);
for (const Coordinate& output_coord : temp_transform) {
out[temp_transform.index(output_coord)] = 0;
}

P axis = axis_tensor[0];
P rank = tensor_shape.size();

if (axis < -rank || axis > rank) {
throw ngraph_error("axis must be in the range [-rank, rank]");
}
axis = axis < 0 ? rank + axis : axis;

auto get_key = [&, axis](const Coordinate& coord) -> Coordinate {
Coordinate result(coord.size(), 0);
result[axis] = coord[axis];

for (size_t i = 0; i < coord.size(); i++) {
result[i] = coord[i] - result[i];
}
return result;
};

auto update_output_buffer =
[&](size_t input_index, size_t output_index, T& prev, std::vector<std::pair<size_t, T>>& tensor_vec) -> void {
tensor_vec[input_index].second = prev + tensor_vec[input_index].second;
out[tensor_vec[output_index].first] = tensor_vec[input_index].second;

// update prev to hold the last result value to compute ruuning sum for
// subsequent iter
prev = out[tensor_vec[output_index].first];
};

auto cum_sum = [&, exclusive, reverse](std::vector<std::pair<size_t, T>>& tensor_vec) {
if (!reverse) {
T prev = 0;
for (size_t i = 0; i < tensor_vec.size(); i++) {
if (exclusive && i == 0) {
out[tensor_vec[i].first] = prev;
continue;
}
// we will compute running sum of j-1 elements if exlusive=1 or else
// for j elements if exclusive = 0
size_t arg_index = exclusive == 1 ? i - 1 : i;
update_output_buffer(arg_index, i, prev, tensor_vec);
const auto rank = tensor_shape.size();
const auto axis = axis_tensor[0] >= 0 ? axis_tensor[0] : rank + axis_tensor[0];
const auto axis_dim = tensor_shape[axis];

const auto size_before_axis = shape_size(Shape(tensor_shape.begin(), tensor_shape.begin() + axis));
const auto size_after_axis = shape_size(Shape(tensor_shape.begin() + axis + 1, tensor_shape.end()));

const auto reverse_shift = reverse ? -1 : 1;
const auto element_shift = exclusive ? size_after_axis * reverse_shift : 0;

for (size_t i = 0; i < size_before_axis; ++i) {
const auto slice_idx = i * axis_dim * size_after_axis + reverse * size_after_axis * (axis_dim - 1);
for (size_t j = 0; j < size_after_axis; ++j) {
const auto sequence_start_idx = slice_idx + j;
out[sequence_start_idx] = exclusive ? T{0} : arg[sequence_start_idx];
for (size_t k = 1; k < axis_dim; ++k) {
const auto element_idx = sequence_start_idx + (k * size_after_axis) * reverse_shift;
const auto in_idx = element_idx - element_shift;
const auto previous_sum_idx = element_idx - size_after_axis * reverse_shift;
out[element_idx] = out[previous_sum_idx] + arg[in_idx];
}
} else // reverse == true
{
T prev = 0;
for (size_t i = tensor_vec.size(); i-- > 0;) {
if (exclusive && i == tensor_vec.size() - 1) {
out[tensor_vec[i].first] = prev;
continue;
}
// we will compute running sum of j-1 elements if exlusive=1 or else
// for j elements if exclusive = 0
size_t arg_index = exclusive == 1 ? i + 1 : i;
update_output_buffer(arg_index, i, prev, tensor_vec);
}
}
};

// Map to collect tensor elements belonging to the same axis
std::map<Coordinate, std::vector<std::pair<size_t, T>>> map_cooord_to_val;
CoordinateTransform input_transform(tensor_shape);
for (const Coordinate& input_coord : input_transform) {
// points to the current element in the input tensor
T current = arg[input_transform.index(input_coord)];
auto key = get_key(input_coord);
auto index = input_transform.index(input_coord);
if (map_cooord_to_val.find(key) != map_cooord_to_val.end()) {
map_cooord_to_val[key].push_back(std::make_pair(index, current));
} else {
map_cooord_to_val.insert({key, std::vector<std::pair<size_t, T>>()});
map_cooord_to_val[key].push_back(std::make_pair(index, current));
}
}
// iterate the map and perform cumulative sum over the give axis
for (auto& it : map_cooord_to_val) {
cum_sum(it.second);
}
NGRAPH_SUPPRESS_DEPRECATED_END
}
} // namespace reference
} // namespace runtime
Expand Down
1 change: 0 additions & 1 deletion ngraph/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,6 @@ set(MULTI_TEST_SRC
backend/cosh.in.cpp
backend/ctc_greedy_decoder.in.cpp
backend/ctc_greedy_decoder_seq_len.in.cpp
backend/cum_sum.in.cpp
backend/deformable_psroi_pooling.in.cpp
backend/detection_output.in.cpp
backend/dft.in.cpp
Expand Down
Loading

0 comments on commit 790f357

Please sign in to comment.