Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[luci/service] Infer dynamic shape for pad #13780

Merged
merged 13 commits into from
Aug 29, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class Algorithm final : public luci::CircleNodeVisitor<loco::TensorShape>
// loco::TensorShape visit(const luci::CircleNotEqual *node) final;
// loco::TensorShape visit(const luci::CircleOneHot *node) final;
// loco::TensorShape visit(const luci::CirclePack *node) final;
// loco::TensorShape visit(const luci::CirclePad *node) final;
loco::TensorShape visit(const luci::CirclePad *node) final;
// loco::TensorShape visit(const luci::CirclePadV2 *node) final;
// loco::TensorShape visit(const luci::CirclePow *node) final;
// loco::TensorShape visit(const luci::CirclePRelu *node) final;
Expand Down
9 changes: 0 additions & 9 deletions compiler/luci/service/src/CircleShapeInferenceRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,13 +977,6 @@ loco::NodeShape infer_pack(const luci::CirclePack *node)
return loco::NodeShape{output_shape};
}

loco::NodeShape infer_pad(const luci::CirclePad *node)
{
// TODO support non-const case
auto paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
return use_paddings(node, paddings);
}

loco::NodeShape infer_pad_v2(const luci::CirclePadV2 *node)
{
// TODO support non-const case
Expand Down Expand Up @@ -2225,8 +2218,6 @@ class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeS

loco::NodeShape visit(const luci::CirclePack *node) final { return infer_pack(node); }

loco::NodeShape visit(const luci::CirclePad *node) final { return infer_pad(node); }

loco::NodeShape visit(const luci::CirclePadV2 *node) final { return infer_pad_v2(node); }

loco::NodeShape visit(const luci::CirclePow *node) final { return broadcast_xy(node); }
Expand Down
88 changes: 88 additions & 0 deletions compiler/luci/service/src/HelperPads.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __LUCI_SERVICE_HELPER_PADS_H__
#define __LUCI_SERVICE_HELPER_PADS_H__

#include "Check.h"

#include <loco/IR/TensorShape.h>
#include <luci/IR/CircleNodes.h>

#include <limits>

namespace luci
{
namespace sinf
{

template <class CIRCLENODE>
loco::TensorShape use_paddings(const CIRCLENODE *node, const luci::CircleConst *paddings)
{
const loco::DataType S32 = loco::DataType::S32;
const loco::DataType S64 = loco::DataType::S64;

auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
// TODO support other data type
LUCI_ASSERT(paddings->dtype() == S32 || paddings->dtype() == S64, "Support int 32/64 for now");
LUCI_ASSERT(paddings->rank() == 2, "paddings should be rank 2");

int32_t n = paddings->dim(0).value();
int32_t v = paddings->dim(1).value();

LUCI_ASSERT(v == 2, "paddings should be [n, 2]");
LUCI_ASSERT(n == int32_t(input_shape.rank()),
"paddings [n, 2] should have same value of input rank");

loco::TensorShape output_shape;

output_shape.rank(input_shape.rank());
for (int32_t ni = 0; ni < n; ++ni)
{
if (not input_shape.dim(ni).known())
{
output_shape.dim(ni).unset();
continue;
}
int32_t idx = ni * 2;
int value = input_shape.dim(ni).value();
if (paddings->dtype() == S32)
{
value += paddings->at<S32>(idx + 0); // left
value += paddings->at<S32>(idx + 1); // right
}
else
{
auto pl = paddings->at<S64>(idx + 0);
auto pr = paddings->at<S64>(idx + 1);
auto max = static_cast<int64_t>(std::numeric_limits<int32_t>::max());
auto low = static_cast<int64_t>(std::numeric_limits<int32_t>::lowest());
LUCI_ASSERT(pl <= max, "paddings is over 32 bit limit");
LUCI_ASSERT(pl >= low, "paddings is over 32 bit limit");
LUCI_ASSERT(pr <= max, "paddings is over 32 bit limit");
LUCI_ASSERT(pr >= low, "paddings is over 32 bit limit");
value += static_cast<int32_t>(pl); // left
value += static_cast<int32_t>(pr); // right
}
output_shape.dim(ni) = value;
}

return output_shape;
}

} // namespace sinf
} // namespace luci

#endif // __LUCI_SERVICE_HELPER_PADS_H__
14 changes: 14 additions & 0 deletions compiler/luci/service/src/Nodes/CirclePad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
* limitations under the License.
*/

#include "luci/Service/CircleShapeInference.h"

#include "CircleCloneNode.h"
#include "CircleShapeInferenceHelper.h"
#include "HelperPads.h"

namespace luci
{
Expand All @@ -24,4 +28,14 @@ luci::CircleNode *CloneNodeLet<CN::OPQR>::visit(const luci::CirclePad *)
return _graph->nodes()->create<luci::CirclePad>();
}

namespace sinf
{

loco::TensorShape Algorithm::visit(const luci::CirclePad *node)
{
auto paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
return use_paddings(node, paddings);
}

} // namespace sinf
} // namespace luci
58 changes: 58 additions & 0 deletions compiler/luci/service/src/Nodes/CirclePad.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "luci/Service/CircleNodeClone.h"

#include "luci/Service/CircleShapeInference.h"

#include <gtest/gtest.h>

TEST(CloneNodeTest, clone_Pad)
Expand All @@ -31,3 +33,59 @@ TEST(CloneNodeTest, clone_Pad)
auto cloned_pad = dynamic_cast<luci::CirclePad *>(cloned);
ASSERT_NE(nullptr, cloned_pad);
}

TEST(ShapeRuleTest, pad_dynamic_shape)
{
luci::CirclePad pad;
luci::CircleInput input;
luci::CircleConst padddings;

loco::TensorShape shape;
luci::sinf::Rule shape_inf_rule;

input.shape({1, 2, 3, 4});
input.shape_status(luci::ShapeStatus::VALID);
input.dim(2).unset();

padddings.dtype(loco::DataType::S64);
padddings.shape({4, 2});
padddings.shape_status(luci::ShapeStatus::VALID);

const loco::DataType S64 = loco::DataType::S64;
uint32_t t = 64 * 8;
padddings.size<S64>(t);

pad.input(&input);
pad.paddings(&padddings);

ASSERT_TRUE(shape_inf_rule.infer(&pad, shape));
ASSERT_EQ(shape.rank(), 4);
ASSERT_TRUE(shape.dim(0).known());
ASSERT_TRUE(shape.dim(1).known());
ASSERT_FALSE(shape.dim(2).known());
ASSERT_TRUE(shape.dim(3).known());

ASSERT_EQ(1, shape.dim(0).value());
ASSERT_EQ(2, shape.dim(1).value());
ASSERT_EQ(0, shape.dim(2).value());
ASSERT_EQ(4, shape.dim(3).value());
pad.drop();
}

TEST(ShapeRuleTest, pad_without_padding_NEG)
{
luci::CirclePad pad;
luci::CircleInput input;

loco::TensorShape shape;
luci::sinf::Rule shape_inf_rule;

input.shape({1, 2, 3, 4});
input.shape_status(luci::ShapeStatus::VALID);
input.dim(2).unset();

pad.input(&input);
ASSERT_ANY_THROW(shape_inf_rule.infer(&pad, shape));

pad.drop();
}