"docs/source/Tuner/AnnealTuner.rst" did not exist on "19e737c91fc4c8dae44bfda4817a7f9d45f7d5e3"
Unverified Commit 4fe71058 authored by Shucai Xiao's avatar Shucai Xiao Committed by GitHub
Browse files

Resize linear mode support (#819)



* backup implementation of resize enhancement

* clang format

* code backup for the resize

* clang format

* fix build error for resize operator

* clang format

* tmp code backup

* clang format

* code backup

* clang format

* remove changes in parse_resize

* remove unnecessary changes

* clang format

* add unit test for the bug

* clang format

* remove print code

* remove a semi-colon

* clang format

* fix a tidy error

* clang format

* add contiguous for nonstd input for the resize operator

* clang format

* code backup

* clang format

* fix build error

* code backup

* clang format

* code backup

* code backup

* clang format

* add unit tests for resize_linear

* clang format

* refine a function name

* clang format

* fix cppcheck error

* clang format

* fix cppcheck error

* fix review comments

* clang format

* backup code changes

* clang format

* add unit tests for resize operator

* clang format

* remove an unused header file

* remove an unused header file

* remove unrelated unit tests

* refine parsing resize inputs

* clang format

* fix cppcheck error

* fix cppcheck error

* remove unnecessary code

* clang format

* fix cppcheck error

* clang format

* fixed a bug

* clang format

* fix review comments

* clang format
Co-authored-by: default avatarmvermeulen <5479696+mvermeulen@users.noreply.github.com>
parent 39bc6161
...@@ -55,7 +55,7 @@ const auto& get_original_idx_op(const std::string& mode) ...@@ -55,7 +55,7 @@ const auto& get_original_idx_op(const std::string& mode)
}}, }},
{"align_corners", {"align_corners",
[=](std::size_t l_in, std::size_t l_out, std::size_t idx, double) { [=](std::size_t l_in, std::size_t l_out, std::size_t idx, double) {
return 1.0 * idx * (l_in - 1.0) / (l_out - 1.0); return (l_out == 1) ? 0.0 : (1.0 * idx * (l_in - 1.0) / (l_out - 1.0));
}}, }},
{"asymmetric", {"asymmetric",
[=](std::size_t, std::size_t, std::size_t idx, double scale) { return idx / scale; }}, [=](std::size_t, std::size_t, std::size_t idx, double scale) { return idx / scale; }},
...@@ -71,19 +71,60 @@ const auto& get_original_idx_op(const std::string& mode) ...@@ -71,19 +71,60 @@ const auto& get_original_idx_op(const std::string& mode)
return idx_ops.at(mode); return idx_ops.at(mode);
} }
struct parse_resize : op_parser<parse_resize> static std::vector<int>
calc_neighbor_points(const std::vector<std::vector<std::vector<std::size_t>>>& vvv_ind,
int i_dim,
const std::vector<std::vector<std::size_t>>& vec_dims,
const shape& in_s)
{ {
std::vector<op_desc> operators() const { return {{"Resize"}}; } if(i_dim == vvv_ind.size())
{
std::vector<int> vec_ind;
vec_ind.resize(vec_dims.size());
std::transform(vec_dims.begin(), vec_dims.end(), vec_ind.begin(), [&](auto idx) {
return static_cast<int>(in_s.index(idx));
});
instruction_ref parse(const op_desc& /*opd*/, return vec_ind;
const onnx_parser& /*parser*/, }
onnx_parser::node_info info,
std::vector<instruction_ref> args) const const auto& vv_ind = vvv_ind[i_dim];
const auto& vv_lo = vv_ind.at(0);
std::vector<std::vector<std::size_t>> vec_dims1;
for(std::size_t start = 0; start < vec_dims.size(); start += vv_lo.size())
{ {
std::transform(vv_lo.begin(),
vv_lo.end(),
vec_dims.begin() + start,
std::back_inserter(vec_dims1),
[](auto i, auto dim) {
dim.push_back(i);
return dim;
});
}
const auto& vv_hi = vv_ind.at(1);
for(std::size_t start = 0; start < vec_dims.size(); start += vv_lo.size())
{
std::transform(vv_hi.begin(),
vv_hi.end(),
vec_dims.begin() + start,
std::back_inserter(vec_dims1),
[](auto i, auto dim) {
dim.push_back(i);
return dim;
});
}
return calc_neighbor_points(vvv_ind, i_dim + 1, vec_dims1, in_s);
}
static std::string get_coord_trans_mode(const onnx_parser::attribute_map& attr)
{
std::string coord_trans_mode = "half_pixel"; std::string coord_trans_mode = "half_pixel";
if(contains(info.attributes, "coordinate_transformation_mode")) if(contains(attr, "coordinate_transformation_mode"))
{ {
coord_trans_mode = info.attributes.at("coordinate_transformation_mode").s(); coord_trans_mode = attr.at("coordinate_transformation_mode").s();
// does not support transformation mode "tf_crop_and_resize" // does not support transformation mode "tf_crop_and_resize"
if(coord_trans_mode == "tf_crop_and_resize") if(coord_trans_mode == "tf_crop_and_resize")
{ {
...@@ -91,32 +132,59 @@ struct parse_resize : op_parser<parse_resize> ...@@ -91,32 +132,59 @@ struct parse_resize : op_parser<parse_resize>
} }
} }
// mode: only nearest mode is supported for now return coord_trans_mode;
if(contains(info.attributes, "mode")) }
static std::string get_mode(const onnx_parser::attribute_map& attr)
{
std::string mode = "nearest";
if(contains(attr, "mode"))
{ {
auto mode = info.attributes.at("mode").s(); mode = attr.at("mode").s();
if(mode != "nearest") if(mode != "nearest" and mode != "linear")
{ {
MIGRAPHX_THROW("PARSE_RESIZE: only nearest mode is supported!"); MIGRAPHX_THROW("PARSE_RESIZE: only nearest and linear modes are supported!");
} }
} }
// nearest mode return mode;
}
static std::string get_nearest_mode(const onnx_parser::attribute_map& attr)
{
std::string nearest_mode = "round_prefer_floor"; std::string nearest_mode = "round_prefer_floor";
if(contains(info.attributes, "nearest_mode")) if(contains(attr, "nearest_mode"))
{ {
nearest_mode = info.attributes.at("nearest_mode").s(); nearest_mode = attr.at("nearest_mode").s();
} }
// check exclude_outside, only support 0 return nearest_mode;
if(contains(info.attributes, "exclude_outside")) }
struct parse_resize : op_parser<parse_resize>
{
std::vector<op_desc> operators() const { return {{"Resize"}}; }
instruction_ref parse(const op_desc& /*opd*/,
const onnx_parser& /*parser*/,
onnx_parser::node_info info,
std::vector<instruction_ref> args) const
{ {
int exclude_outside = info.attributes.at("exclude_outside").i(); // coord transform mode
if(exclude_outside == 1) std::string coord_trans_mode = get_coord_trans_mode(info.attributes);
// mode: only nearest and linear modes are supported for now
std::string mode = get_mode(info.attributes);
// nearest mode
std::string nearest_mode = get_nearest_mode(info.attributes);
// check exclude_outside, only support 0
if(contains(info.attributes, "exclude_outside") and
info.attributes.at("exclude_outside").i() == 1)
{ {
MIGRAPHX_THROW("PARSE_RESIZE: exclude_outside 1 is not supported!"); MIGRAPHX_THROW("PARSE_RESIZE: exclude_outside 1 is not supported!");
} }
}
// input data shape info // input data shape info
auto in_s = args[0]->get_shape(); auto in_s = args[0]->get_shape();
...@@ -128,10 +196,25 @@ struct parse_resize : op_parser<parse_resize> ...@@ -128,10 +196,25 @@ struct parse_resize : op_parser<parse_resize>
// scale // scale
std::vector<double> vec_scale; std::vector<double> vec_scale;
// output size is specified in input, so use it as output size for(const auto& arg : args)
if(args.size() == 4 and args.back()->name() != "undefined") {
if(arg->name() == "undefined" or arg == args.front())
{ {
auto arg_out_s = args[3]->eval(); continue;
}
// skipped empty input
auto lens = arg->get_shape().lens();
if(lens.empty())
{
continue;
}
auto type = arg->get_shape().type();
// output size
if(type == shape::int64_type)
{
auto arg_out_s = arg->eval();
check_arg_empty(arg_out_s, "PARSE_RESIZE: dynamic output size is not supported!"); check_arg_empty(arg_out_s, "PARSE_RESIZE: dynamic output size is not supported!");
arg_out_s.visit([&](auto ol) { out_lens.assign(ol.begin(), ol.end()); }); arg_out_s.visit([&](auto ol) { out_lens.assign(ol.begin(), ol.end()); });
...@@ -148,11 +231,15 @@ struct parse_resize : op_parser<parse_resize> ...@@ -148,11 +231,15 @@ struct parse_resize : op_parser<parse_resize>
vec_scale.begin(), vec_scale.begin(),
[](auto iss, auto oss) { return 1.0 * oss / iss; }); [](auto iss, auto oss) { return 1.0 * oss / iss; });
} }
// need to compute the output lens from input
else else
{ {
auto arg_scale = args[2]->eval();
check_arg_empty(arg_scale, "PARSE_RESIZE: dynamic input scale is not supported!"); // scale input
if(lens[0] == in_lens.size())
{
auto arg_scale = arg->eval();
check_arg_empty(arg_scale,
"PARSE_RESIZE: dynamic input scale is not supported!");
arg_scale.visit([&](auto v) { vec_scale.assign(v.begin(), v.end()); }); arg_scale.visit([&](auto v) { vec_scale.assign(v.begin(), v.end()); });
if(in_lens.size() != vec_scale.size()) if(in_lens.size() != vec_scale.size())
...@@ -160,42 +247,113 @@ struct parse_resize : op_parser<parse_resize> ...@@ -160,42 +247,113 @@ struct parse_resize : op_parser<parse_resize>
MIGRAPHX_THROW("PARSE_RESIZE: ranks of input and scale are different!"); MIGRAPHX_THROW("PARSE_RESIZE: ranks of input and scale are different!");
} }
std::transform( std::transform(in_lens.begin(),
in_lens.begin(),
in_lens.end(), in_lens.end(),
vec_scale.begin(), vec_scale.begin(),
out_lens.begin(), out_lens.begin(),
[&](auto idx, auto scale) { return static_cast<std::size_t>(idx * scale); }); [&](auto idx, auto scale) {
return static_cast<std::size_t>(idx * scale);
});
}
}
} }
shape out_s{in_s.type(), out_lens}; shape out_s{in_s.type(), out_lens};
std::vector<int> ind(out_s.elements()); std::size_t out_elements = out_s.elements();
auto idx_op = get_original_idx_op(coord_trans_mode);
// reshape input to one-dimension
std::vector<int64_t> rsp_lens = {static_cast<int64_t>(in_s.elements())};
args[0] = info.make_contiguous(args[0]);
auto rsp = info.add_instruction(make_op("reshape", {{"dims", rsp_lens}}), args[0]);
if(mode == "nearest")
{
std::vector<int> ind(out_elements);
// map out_idx to in_idx // map out_idx to in_idx
auto nearest_op = get_nearest_op(nearest_mode); auto nearest_op = get_nearest_op(nearest_mode);
auto idx_op = get_original_idx_op(coord_trans_mode);
shape_for_each(out_s, [&](auto idx) { shape_for_each(out_s, [&](auto idx) {
auto in_idx = idx; auto in_idx = idx;
for(auto ii = 0; ii < in_lens.size(); ++ii) for(auto ii = 0; ii < in_lens.size(); ++ii)
{ {
auto idx_val = idx_op(in_lens[ii], out_lens[ii], in_idx[ii], vec_scale[ii]); auto idx_val = idx_op(in_lens[ii], out_lens[ii], idx[ii], vec_scale[ii]);
in_idx[ii] = nearest_op(in_lens[ii], idx_val); in_idx[ii] = nearest_op(in_lens[ii], idx_val);
} }
ind[out_s.index(idx)] = static_cast<int64_t>(in_s.index(in_idx)); ind[out_s.index(idx)] = static_cast<int64_t>(in_s.index(in_idx));
}); });
// reshape input to one-dimension
std::vector<int64_t> rsp_lens = {static_cast<int64_t>(in_s.elements())};
shape ind_s{shape::int32_type, out_lens}; shape ind_s{shape::int32_type, out_lens};
auto arg_cont = info.make_contiguous(args[0]);
auto rsp = info.add_instruction(make_op("reshape", {{"dims", rsp_lens}}), arg_cont);
auto ins_ind = info.add_literal(literal(ind_s, ind)); auto ins_ind = info.add_literal(literal(ind_s, ind));
return info.add_instruction(make_op("gather", {{"axis", 0}}), rsp, ins_ind); return info.add_instruction(make_op("gather", {{"axis", 0}}), rsp, ins_ind);
} }
// linear mode
else
{
auto nearest_floor = get_nearest_op("floor");
auto nearest_ceil = get_nearest_op("ceil");
// get the number of dimensions
std::size_t n_dim = out_lens.size();
std::vector<std::vector<std::size_t>> vv_ind(2, std::vector<std::size_t>(out_elements));
std::vector<std::vector<std::vector<std::size_t>>> vvv_ind(n_dim, vv_ind);
std::vector<std::vector<float>> delta(n_dim, std::vector<float>(out_elements));
shape_for_each(out_s, [&](auto idx) {
auto in_idx = idx;
auto out_idx = out_s.index(idx);
for(auto ii = 0; ii < in_lens.size(); ++ii)
{
auto idx_val = idx_op(in_lens[ii], out_lens[ii], idx[ii], vec_scale[ii]);
vvv_ind[ii][0][out_idx] = nearest_floor(in_lens[ii], idx_val);
vvv_ind[ii][1][out_idx] = nearest_ceil(in_lens[ii], idx_val);
delta[ii][out_idx] = idx_val - vvv_ind[ii][0][out_idx];
}
});
std::vector<std::vector<std::size_t>> vec_dims(out_elements);
auto ind = calc_neighbor_points(vvv_ind, 0, vec_dims, in_s);
auto ind_lens = out_lens;
ind_lens[0] *= (std::size_t{1} << n_dim);
shape ind_s{shape::int32_type, ind_lens};
auto ins_ind = info.add_literal(literal(ind_s, ind));
auto data = info.add_instruction(make_op("gather", {{"axis", 0}}), rsp, ins_ind);
auto dim_lens = out_lens;
dim_lens[0] *= (std::size_t{1} << (n_dim - 1));
for(std::size_t i = 0; i < n_dim; ++i)
{
shape dim_s{shape::float_type, dim_lens};
const auto& dim_delta = delta[n_dim - i - 1];
std::vector<float> delta_data;
for(std::size_t j = 0; j < dim_lens[0] / out_lens[0]; ++j)
{
delta_data.insert(delta_data.begin(), dim_delta.begin(), dim_delta.end());
}
auto ins_delta = info.add_literal(dim_s, delta_data);
// slice the data
int64_t slc_stride = static_cast<int64_t>(dim_lens[0]);
auto low = info.add_instruction(
make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {slc_stride}}}),
data);
auto hi = info.add_instruction(
make_op("slice",
{{"axes", {0}}, {"starts", {slc_stride}}, {"ends", {2 * slc_stride}}}),
data);
auto diff = info.add_instruction(make_op("sub"), hi, low);
auto ddf = info.add_instruction(make_op("mul"), diff, ins_delta);
data = info.add_instruction(make_op("add"), ddf, low);
dim_lens[0] /= 2;
}
return data;
}
}
}; };
} // namespace onnx } // namespace onnx
} // namespace MIGRAPHX_INLINE_NS } // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx } // namespace migraphx
...@@ -3102,7 +3102,7 @@ def resize_downsample_f_test(): ...@@ -3102,7 +3102,7 @@ def resize_downsample_f_test():
vals=scales.flatten().astype(np.float32)) vals=scales.flatten().astype(np.float32))
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 1, 2, 4]) X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 1, 2, 4])
Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [1, 1, 1, 2]) Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [])
node = onnx.helper.make_node( node = onnx.helper.make_node(
'Resize', 'Resize',
...@@ -3136,6 +3136,25 @@ def resize_downsample_c_test(): ...@@ -3136,6 +3136,25 @@ def resize_downsample_c_test():
return ([node], [X], [Y], [scale_tensor]) return ([node], [X], [Y], [scale_tensor])
@onnx_test
def resize_downsample_linear_test():
scales = np.array([1.0, 1.0, 0.6, 0.5], dtype=np.float32)
scale_tensor = helper.make_tensor(name='scales',
data_type=TensorProto.FLOAT,
dims=scales.shape,
vals=scales.flatten().astype(np.float32))
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 1, 2, 4])
Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [])
node = onnx.helper.make_node('Resize',
inputs=['X', '', 'scales'],
outputs=['Y'],
mode='linear')
return ([node], [X], [Y], [scale_tensor])
@onnx_test @onnx_test
def resize_nonstd_input_test(): def resize_nonstd_input_test():
scales = np.array([1.0, 1.0, 0.6, 0.6], dtype=np.float32) scales = np.array([1.0, 1.0, 0.6, 0.6], dtype=np.float32)
...@@ -3185,6 +3204,46 @@ def resize_outsize_test(): ...@@ -3185,6 +3204,46 @@ def resize_outsize_test():
return ([node], [X], [Y], [out_lens_tensor]) return ([node], [X], [Y], [out_lens_tensor])
@onnx_test
def resize_upsample_linear_ac_test():
scales = np.array([1.0, 1.0, 2.0, 2.0], dtype=np.float32)
scales_tensor = helper.make_tensor(name='scales',
data_type=TensorProto.FLOAT,
dims=scales.shape,
vals=scales.flatten().astype(
np.float32))
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 1, 2, 2])
Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [])
node = onnx.helper.make_node(
'Resize',
inputs=['X', '', 'scales'],
outputs=['Y'],
mode='linear',
coordinate_transformation_mode='align_corners')
return ([node], [X], [Y], [scales_tensor])
@onnx_test
def resize_upsample_linear_test():
scales = np.array([1.0, 1.0, 2.0, 2.0], dtype=np.float32)
scales_tensor = helper.make_tensor(name='scales',
data_type=TensorProto.FLOAT,
dims=scales.shape,
vals=scales.flatten().astype(
np.float32))
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 1, 2, 2])
Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [])
node = onnx.helper.make_node('Resize',
inputs=['X', '', 'scales'],
outputs=['Y'],
mode='linear')
return ([node], [X], [Y], [scales_tensor])
@onnx_test @onnx_test
def resize_upsample_pf_test(): def resize_upsample_pf_test():
scales = np.array([1.0, 1.0, 2.0, 3.0], dtype=np.float32) scales = np.array([1.0, 1.0, 2.0, 3.0], dtype=np.float32)
......
...@@ -2670,10 +2670,11 @@ TEST_CASE(reshape_non_standard_test) ...@@ -2670,10 +2670,11 @@ TEST_CASE(reshape_non_standard_test)
EXPECT(p == prog); EXPECT(p == prog);
} }
TEST_CASE(resize_downsample_f_test) TEST_CASE(resize_downsample_c_test)
{ {
migraphx::program p; migraphx::program p;
auto* mm = p.get_main_module(); auto* mm = p.get_main_module();
std::vector<float> ds = {1.0f, 1.0f, 0.6f, 0.6f}; std::vector<float> ds = {1.0f, 1.0f, 0.6f, 0.6f};
migraphx::shape ss{migraphx::shape::float_type, {4}}; migraphx::shape ss{migraphx::shape::float_type, {4}};
mm->add_literal(migraphx::literal{ss, ds}); mm->add_literal(migraphx::literal{ss, ds});
...@@ -2684,23 +2685,22 @@ TEST_CASE(resize_downsample_f_test) ...@@ -2684,23 +2685,22 @@ TEST_CASE(resize_downsample_f_test)
mm->add_instruction(migraphx::make_op("undefined")); mm->add_instruction(migraphx::make_op("undefined"));
migraphx::shape si{migraphx::shape::int32_type, {1, 1, 1, 2}}; migraphx::shape si{migraphx::shape::int32_type, {1, 1, 1, 2}};
std::vector<int> ind = {4, 7}; std::vector<int> ind = {0, 2};
auto li = mm->add_literal(migraphx::literal(si, ind)); auto li = mm->add_literal(migraphx::literal(si, ind));
auto lrsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {8}}}), inx); auto lrsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {8}}}), inx);
auto r = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), lrsp, li); auto r = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), lrsp, li);
mm->add_return({r}); mm->add_return({r});
auto prog = migraphx::parse_onnx("resize_downsample_f_test.onnx"); auto prog = migraphx::parse_onnx("resize_downsample_c_test.onnx");
EXPECT(p == prog); EXPECT(p == prog);
} }
TEST_CASE(resize_downsample_c_test) TEST_CASE(resize_downsample_f_test)
{ {
migraphx::program p; migraphx::program p;
auto* mm = p.get_main_module(); auto* mm = p.get_main_module();
std::vector<float> ds = {1.0f, 1.0f, 0.6f, 0.6f}; std::vector<float> ds = {1.0f, 1.0f, 0.6f, 0.6f};
migraphx::shape ss{migraphx::shape::float_type, {4}}; migraphx::shape ss{migraphx::shape::float_type, {4}};
mm->add_literal(migraphx::literal{ss, ds}); mm->add_literal(migraphx::literal{ss, ds});
...@@ -2711,15 +2711,83 @@ TEST_CASE(resize_downsample_c_test) ...@@ -2711,15 +2711,83 @@ TEST_CASE(resize_downsample_c_test)
mm->add_instruction(migraphx::make_op("undefined")); mm->add_instruction(migraphx::make_op("undefined"));
migraphx::shape si{migraphx::shape::int32_type, {1, 1, 1, 2}}; migraphx::shape si{migraphx::shape::int32_type, {1, 1, 1, 2}};
std::vector<int> ind = {0, 2}; std::vector<int> ind = {0, 3};
auto li = mm->add_literal(migraphx::literal(si, ind)); auto li = mm->add_literal(migraphx::literal(si, ind));
auto lrsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {8}}}), inx); auto lrsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {8}}}), inx);
auto r = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), lrsp, li); auto r = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), lrsp, li);
mm->add_return({r}); mm->add_return({r});
auto prog = migraphx::parse_onnx("resize_downsample_c_test.onnx"); auto prog = migraphx::parse_onnx("resize_downsample_f_test.onnx");
EXPECT(p == prog);
}
TEST_CASE(resize_downsample_linear_test)
{
migraphx::program p;
auto* mm = p.get_main_module();
migraphx::shape ss{migraphx::shape::float_type, {4}};
std::vector<float> ds = {1, 1, 0.6, 0.5};
mm->add_literal(migraphx::literal(ss, ds));
migraphx::shape sx{migraphx::shape::float_type, {1, 1, 2, 4}};
auto x = mm->add_parameter("X", sx);
migraphx::shape s_ind{migraphx::shape::int32_type, {16, 1, 1, 2}};
std::vector<int> d_ind = {0, 2, 0, 2, 0, 2, 0, 2, 4, 6, 4, 6, 4, 6, 4, 6,
1, 3, 1, 3, 1, 3, 1, 3, 5, 7, 5, 7, 5, 7, 5, 7};
auto l_ind = mm->add_literal(migraphx::literal(s_ind, d_ind));
migraphx::shape s8{migraphx::shape::float_type, {8, 1, 1, 2}};
std::vector<float> d8(16, 0.5f);
auto l8 = mm->add_literal(migraphx::literal(s8, d8));
migraphx::shape s4{migraphx::shape::float_type, {4, 1, 1, 2}};
std::vector<float> d4(8, 1.0f / 3.0f);
auto l4 = mm->add_literal(migraphx::literal(s4, d4));
migraphx::shape s2{migraphx::shape::float_type, {2, 1, 1, 2}};
std::vector<float> d2(4, 0);
auto l2 = mm->add_literal(migraphx::literal(s2, d2));
migraphx::shape s1{migraphx::shape::float_type, {1, 1, 1, 2}};
std::vector<float> d1(2, 0.0f);
auto l1 = mm->add_literal(migraphx::literal(s1, d1));
mm->add_instruction(migraphx::make_op("undefined"));
auto rsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {8}}}), x);
auto data = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), rsp, l_ind);
auto slc80 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {8}}}), data);
auto slc81 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {8}}, {"ends", {16}}}), data);
auto diff8 = mm->add_instruction(migraphx::make_op("sub"), slc81, slc80);
auto mul8 = mm->add_instruction(migraphx::make_op("mul"), diff8, l8);
auto add8 = mm->add_instruction(migraphx::make_op("add"), mul8, slc80);
auto slc40 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {4}}}), add8);
auto slc41 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {4}}, {"ends", {8}}}), add8);
auto diff4 = mm->add_instruction(migraphx::make_op("sub"), slc41, slc40);
auto mul4 = mm->add_instruction(migraphx::make_op("mul"), diff4, l4);
auto add4 = mm->add_instruction(migraphx::make_op("add"), mul4, slc40);
auto slc20 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {2}}}), add4);
auto slc21 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {2}}, {"ends", {4}}}), add4);
auto diff2 = mm->add_instruction(migraphx::make_op("sub"), slc21, slc20);
auto mul2 = mm->add_instruction(migraphx::make_op("mul"), diff2, l2);
auto add2 = mm->add_instruction(migraphx::make_op("add"), mul2, slc20);
auto slc10 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {1}}}), add2);
auto slc11 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {1}}, {"ends", {2}}}), add2);
auto diff1 = mm->add_instruction(migraphx::make_op("sub"), slc11, slc10);
auto mul1 = mm->add_instruction(migraphx::make_op("mul"), diff1, l1);
auto add1 = mm->add_instruction(migraphx::make_op("add"), mul1, slc10);
mm->add_return({add1});
auto prog = migraphx::parse_onnx("resize_downsample_linear_test.onnx");
EXPECT(p == prog); EXPECT(p == prog);
} }
...@@ -2779,6 +2847,196 @@ TEST_CASE(resize_nonstd_input_test) ...@@ -2779,6 +2847,196 @@ TEST_CASE(resize_nonstd_input_test)
EXPECT(p == prog); EXPECT(p == prog);
} }
TEST_CASE(resize_upsample_linear_ac_test)
{
migraphx::program p;
auto* mm = p.get_main_module();
migraphx::shape ss{migraphx::shape::float_type, {4}};
std::vector<float> ds = {1, 1, 2, 2};
mm->add_literal(migraphx::literal(ss, ds));
migraphx::shape sx{migraphx::shape::float_type, {1, 1, 2, 2}};
auto x = mm->add_parameter("X", sx);
migraphx::shape s_ind{migraphx::shape::int32_type, {16, 1, 4, 4}};
std::vector<int> d_ind = {
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2,
2, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
0, 1, 2, 2, 2, 3, 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1, 2, 2, 2,
3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1,
2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0,
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3,
3, 3, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3,
3, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3,
2, 3, 3, 3, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3};
auto l_ind = mm->add_literal(migraphx::literal(s_ind, d_ind));
migraphx::shape s8{migraphx::shape::float_type, {8, 1, 4, 4}};
std::vector<float> d8 = {
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0};
auto l8 = mm->add_literal(migraphx::literal(s8, d8));
migraphx::shape s4{migraphx::shape::float_type, {4, 1, 4, 4}};
std::vector<float> d4 = {
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0,
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0,
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0,
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0};
auto l4 = mm->add_literal(migraphx::literal(s4, d4));
migraphx::shape s2{migraphx::shape::float_type, {2, 1, 4, 4}};
std::vector<float> d2(32, 0);
auto l2 = mm->add_literal(migraphx::literal(s2, d2));
migraphx::shape s1{migraphx::shape::float_type, {1, 1, 4, 4}};
std::vector<float> d1(16, 0.0f);
auto l1 = mm->add_literal(migraphx::literal(s1, d1));
mm->add_instruction(migraphx::make_op("undefined"));
auto rsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {4}}}), x);
auto data = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), rsp, l_ind);
auto slc80 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {8}}}), data);
auto slc81 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {8}}, {"ends", {16}}}), data);
auto diff8 = mm->add_instruction(migraphx::make_op("sub"), slc81, slc80);
auto mul8 = mm->add_instruction(migraphx::make_op("mul"), diff8, l8);
auto add8 = mm->add_instruction(migraphx::make_op("add"), mul8, slc80);
auto slc40 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {4}}}), add8);
auto slc41 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {4}}, {"ends", {8}}}), add8);
auto diff4 = mm->add_instruction(migraphx::make_op("sub"), slc41, slc40);
auto mul4 = mm->add_instruction(migraphx::make_op("mul"), diff4, l4);
auto add4 = mm->add_instruction(migraphx::make_op("add"), mul4, slc40);
auto slc20 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {2}}}), add4);
auto slc21 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {2}}, {"ends", {4}}}), add4);
auto diff2 = mm->add_instruction(migraphx::make_op("sub"), slc21, slc20);
auto mul2 = mm->add_instruction(migraphx::make_op("mul"), diff2, l2);
auto add2 = mm->add_instruction(migraphx::make_op("add"), mul2, slc20);
auto slc10 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {1}}}), add2);
auto slc11 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {1}}, {"ends", {2}}}), add2);
auto diff1 = mm->add_instruction(migraphx::make_op("sub"), slc11, slc10);
auto mul1 = mm->add_instruction(migraphx::make_op("mul"), diff1, l1);
auto add1 = mm->add_instruction(migraphx::make_op("add"), mul1, slc10);
mm->add_return({add1});
auto prog = migraphx::parse_onnx("resize_upsample_linear_ac_test.onnx");
EXPECT(p == prog);
}
TEST_CASE(resize_upsample_linear_test)
{
migraphx::program p;
auto* mm = p.get_main_module();
migraphx::shape ss{migraphx::shape::float_type, {4}};
std::vector<float> ds = {1, 1, 2, 2};
mm->add_literal(migraphx::literal(ss, ds));
migraphx::shape sx{migraphx::shape::float_type, {1, 1, 2, 2}};
auto x = mm->add_parameter("X", sx);
migraphx::shape s_ind{migraphx::shape::int32_type, {16, 1, 4, 4}};
std::vector<int> d_ind = {
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2,
2, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
0, 1, 2, 2, 2, 3, 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1, 2, 2, 2,
3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 0, 0, 1,
2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0,
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3,
3, 3, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3,
3, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3,
2, 3, 3, 3, 2, 3, 3, 3, 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3};
auto l_ind = mm->add_literal(migraphx::literal(s_ind, d_ind));
migraphx::shape s8{migraphx::shape::float_type, {8, 1, 4, 4}};
std::vector<float> d8 = {
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0,
0, 1.0f / 3, 2.0f / 3, 0, 0, 1.0f / 3, 2.0f / 3, 0};
auto l8 = mm->add_literal(migraphx::literal(s8, d8));
migraphx::shape s4{migraphx::shape::float_type, {4, 1, 4, 4}};
std::vector<float> d4 = {
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0,
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0,
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0,
0, 0, 0, 0, 1.0f / 3, 1.0f / 3, 1.0f / 3, 1.0f / 3,
2.0f / 3, 2.0f / 3, 2.0f / 3, 2.0f / 3, 0, 0, 0, 0};
auto l4 = mm->add_literal(migraphx::literal(s4, d4));
migraphx::shape s2{migraphx::shape::float_type, {2, 1, 4, 4}};
std::vector<float> d2(32, 0);
auto l2 = mm->add_literal(migraphx::literal(s2, d2));
migraphx::shape s1{migraphx::shape::float_type, {1, 1, 4, 4}};
std::vector<float> d1(16, 0.0f);
auto l1 = mm->add_literal(migraphx::literal(s1, d1));
mm->add_instruction(migraphx::make_op("undefined"));
auto rsp = mm->add_instruction(migraphx::make_op("reshape", {{"dims", {4}}}), x);
auto data = mm->add_instruction(migraphx::make_op("gather", {{"axis", 0}}), rsp, l_ind);
auto slc80 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {8}}}), data);
auto slc81 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {8}}, {"ends", {16}}}), data);
auto diff8 = mm->add_instruction(migraphx::make_op("sub"), slc81, slc80);
auto mul8 = mm->add_instruction(migraphx::make_op("mul"), diff8, l8);
auto add8 = mm->add_instruction(migraphx::make_op("add"), mul8, slc80);
auto slc40 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {4}}}), add8);
auto slc41 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {4}}, {"ends", {8}}}), add8);
auto diff4 = mm->add_instruction(migraphx::make_op("sub"), slc41, slc40);
auto mul4 = mm->add_instruction(migraphx::make_op("mul"), diff4, l4);
auto add4 = mm->add_instruction(migraphx::make_op("add"), mul4, slc40);
auto slc20 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {2}}}), add4);
auto slc21 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {2}}, {"ends", {4}}}), add4);
auto diff2 = mm->add_instruction(migraphx::make_op("sub"), slc21, slc20);
auto mul2 = mm->add_instruction(migraphx::make_op("mul"), diff2, l2);
auto add2 = mm->add_instruction(migraphx::make_op("add"), mul2, slc20);
auto slc10 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {0}}, {"ends", {1}}}), add2);
auto slc11 = mm->add_instruction(
migraphx::make_op("slice", {{"axes", {0}}, {"starts", {1}}, {"ends", {2}}}), add2);
auto diff1 = mm->add_instruction(migraphx::make_op("sub"), slc11, slc10);
auto mul1 = mm->add_instruction(migraphx::make_op("mul"), diff1, l1);
auto add1 = mm->add_instruction(migraphx::make_op("add"), mul1, slc10);
mm->add_return({add1});
auto prog = migraphx::parse_onnx("resize_upsample_linear_test.onnx");
EXPECT(p == prog);
}
TEST_CASE(resize_upsample_pc_test) TEST_CASE(resize_upsample_pc_test)
{ {
migraphx::program p; migraphx::program p;
......
...@@ -240,7 +240,84 @@ TEST_CASE(lessorequal_test) ...@@ -240,7 +240,84 @@ TEST_CASE(lessorequal_test)
EXPECT(migraphx::verify_range(result_vector, gold)); EXPECT(migraphx::verify_range(result_vector, gold));
} }
TEST_CASE(resize_test) TEST_CASE(resize_downsample_f_test)
{
migraphx::program p = migraphx::parse_onnx("resize_downsample_f_test.onnx");
p.compile(migraphx::ref::target{});
migraphx::shape sx{migraphx::shape::float_type, {1, 1, 2, 4}};
std::vector<float> dx(sx.elements());
std::iota(dx.begin(), dx.end(), 0.0f);
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, dx.data());
auto result = p.eval(pp).back();
std::vector<float> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
std::vector<float> gold = {0.0f, 3.0f};
EXPECT(migraphx::verify_range(result_vector, gold));
}
TEST_CASE(resize_upsample_linear_ac_test)
{
migraphx::program p = migraphx::parse_onnx("resize_upsample_linear_ac_test.onnx");
p.compile(migraphx::ref::target{});
migraphx::shape sx{migraphx::shape::float_type, {1, 1, 2, 2}};
std::vector<float> dx = {1.0f, 2.0f, 3.0f, 4.0f};
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, dx.data());
auto result = p.eval(pp).back();
std::vector<float> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
std::vector<float> gold = {1,
4.0f / 3,
5.0f / 3,
2,
5.0f / 3,
2,
7.0f / 3,
8.0f / 3,
7.0f / 3,
8.0f / 3,
3,
10.0f / 3,
3,
10.0f / 3,
11.0f / 3,
4};
EXPECT(migraphx::verify_range(result_vector, gold));
}
TEST_CASE(resize_upsample_linear_test)
{
migraphx::program p = migraphx::parse_onnx("resize_upsample_linear_test.onnx");
p.compile(migraphx::ref::target{});
migraphx::shape sx{migraphx::shape::float_type, {1, 1, 2, 2}};
std::vector<float> dx = {1.0f, 2.0f, 3.0f, 4.0f};
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, dx.data());
auto result = p.eval(pp).back();
std::vector<float> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
std::vector<float> gold = {
1, 1.25, 1.75, 2, 1.5, 1.75, 2.25, 2.5, 2.5, 2.75, 3.25, 3.5, 3, 3.25, 3.75, 4};
EXPECT(migraphx::verify_range(result_vector, gold));
}
TEST_CASE(resize_upsample_pf_test)
{ {
migraphx::program p = migraphx::parse_onnx("resize_upsample_pf_test.onnx"); migraphx::program p = migraphx::parse_onnx("resize_upsample_pf_test.onnx");
p.compile(migraphx::ref::target{}); p.compile(migraphx::ref::target{});
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment